STM32模拟串口(UART)使用

发布时间:2024-01-29  

1、添加头文件


首先我们先添加相应的头文件。既然我们要进行对串口的模拟,因此我们要先了解uart相关的通信协议。由于UART的通信方式是由1个起始位,8个数据位,包含一个奇偶校验位,和结束位构成因此我们将使用单片机中的两个普通的IO口电平的高低进行对相应时序的模拟。

dcb69e8a2d5775b1cfd71ff561299319_wKgaomTneriAIotKAAEbfKP_cA4297.png

#include"stm32f10x.h"

#include"vuart2.h"

2、宏定义


使用到的io口为


#defineOI2_TXDPDout(6)

#defineOI2_RXDPDin(7)


#defineBuadRate2_9600104

#defineRecive2_Byte19//接收缓冲器的个数

u8len2=0;//接收计数

u8USART2_buf[Recive2_Byte];//接收缓冲区

将IO口相应的位带操作函数进行宏定义从而使得在对不同的电平的进行转换的时候更为方便,并且减少了调用其他函数的过程所消耗的时间,程序执行效率更高。


在本次的传输过程中我选用的是使用波特率速率为9600,也就是1s中发送9600个字节,因此对每个字节数据进行计算1000000us/9600可以得出,发一个字节的数据需要进行大概需要 104.16us并且对于相应的电平持续时间要求误差不能超过±5%因此对我们进行时间的控制要求就显得比较重要了。


3、枚举出各个位


enum{

COM_START_BIT,

COM_D0_BIT,

COM_D1_BIT,

COM_D2_BIT,

COM_D3_BIT,

COM_D4_BIT,

COM_D5_BIT,

COM_D6_BIT,

COM_D7_BIT,

COM_STOP_BIT,

};


u8recvStat2=COM_STOP_BIT;

u8recvData2=0;

4、IO——TXD进行模拟


voidIO2_TXD(u8Data)

{

u8i=0;

OI2_TXD=0;

delay_us(BuadRate2_9600);

for(i=0;i< 8; i++)

    {

        if(Data&0x01)

            OI2_TXD = 1;  

        else

            OI2_TXD = 0;  

        

        delay_us(BuadRate2_9600);

        Data = Data>>1;

}

OI2_TXD=1;

delay_us(BuadRate2_9600);

}

由于发送的信号是将TXD信号进行拉低处理,因此在拉低TXD相应的IO口之后进行延时处理,再进行循环对我们需要发送的各个位的数据继续进行发送循环发送完成之后将电平拉高代表停止位。


5、构建发送函数


voidUSART2_Send(u8*buf,u8len2)

{

u8t;

for(t=0;t< len2; t++)

    {

        IO2_TXD(buf[t]);

    }

}

其中的*buf为需要发送的数据,len2为数据长度,进行循环调用IO_TXD进行一个字节一个字节的数据发送。


6、IO口初始化


voidIO2Config(void)

{

GPIO_InitTypeDefGPIO_InitStructure;//初始化gpio

NVIC_InitTypeDefNVIC_InitStructure;//中断初始化函数

EXTI_InitTypeDefEXTI_InitStruct;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC,ENABLE);//使能PD,PC端口时钟


//SoftWareSerialTXD

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;//选择io口6

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO口速度为50MHz

GPIO_Init(GPIOD,&GPIO_InitStructure);

GPIO_SetBits(GPIOD,GPIO_Pin_6);//TXD默认电平拉高


//SoftWareSerialRXD

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_Init(GPIOD,&GPIO_InitStructure);


GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource7);//对D7的下降沿进行中断采样,当接收到下降沿时代表接收到数据触发中断处理函数

EXTI_InitStruct.EXTI_Line=EXTI_Line7;//用到了中断7

EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;

EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发中断

EXTI_InitStruct.EXTI_LineCmd=ENABLE;

EXTI_Init(&EXTI_InitStruct);//初始化中断



NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;//中断发生于9-5的中断之中

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;

NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

NVIC_Init(&NVIC_InitStructure);

}

7、定时器初始化


voidTIM5_Int_Init(u16arr,u16psc)

{

TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;

NVIC_InitTypeDefNVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//时钟使能


//定时器TIM5初始化

TIM_TimeBaseStructure.TIM_Period=arr;//设置在下一个更新事件装入活动的自动重装载寄存器周期的值

TIM_TimeBaseStructure.TIM_Prescaler=psc;//设置用来作为TIMx时钟频率除数的预分频值

TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//设置时钟分割:TDTS=Tck_tim

TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//TIM向上计数模式

TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);//根据指定的参数初始化TIMx的时间基数单位

TIM_ClearITPendingBit(TIM5,TIM_FLAG_Update);

TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);//使能指定的TIM5中断,允许更新中断


//中断优先级NVIC设置

NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;//TIM5中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//先占优先级1级

NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//从优先级1级

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//IRQ通道被使能

NVIC_Init(&NVIC_InitStructure);//初始化NVIC寄存器

}

对TIM5进行初始化操作使得定时器可以检测到各个位的电平持续性时间从而对接收到的数据进行分析。计时结束后进入中断TIM5处理。


8、外部中断处理函数


voidEXTI9_5_IRQHandler(void)

{

if(EXTI_GetFlagStatus(EXTI_Line7)!=RESET)//对中断标志位进行采集

{

if(OI2_RXD==0)

{

if(recvStat2==COM_STOP_BIT)

{

recvStat2=COM_START_BIT;//将当前的状态设置为开始位

TIM_Cmd(TIM5,ENABLE);//开启定时器计数

}

}

EXTI_ClearITPendingBit(EXTI_Line7);//清除中断标志

}

}

9、定时器中断处理函数


voidTIM5_IRQHandler(void)

{

if(TIM_GetFlagStatus(TIM5,TIM_FLAG_Update)!=RESET)

{

TIM_ClearITPendingBit(TIM5,TIM_FLAG_Update);//清除中断标志位

recvStat2++;//将位置移动到第一位的数据

if(recvStat2==COM_STOP_BIT)//当运行到停止位时进入

{

TIM_Cmd(TIM5,DISABLE);//停止tim5

USART2_buf[len2++]=recvData2;//将采集到的各个数据传递给USART2_buf

if(len2>Recive2_Byte-1)//将数据通过回显到串口调试助手中

{

len2=0;

USART2_Send(USART2_buf,Recive2_Byte);

}

return;

}

if(OI2_RXD)//采集RXD各个电平

{

recvData2|=(1<< (recvStat2 - 1));

        }else{

            recvData2 &= ~(1 << (recvStat2 - 1));

        } 

  }  

}

整体代码


vuart2.c


#include"stm32f10x.h"

#include"vuart2.h"

/**

*软件串口的实现(IO模拟串口)

*波特率:9600 1-8-N

*TXD:PD6

*RXD:PD7

*使用外部中断对RXD的下降沿进行触发,使用定时器5按照9600波特率进行定时数据接收。

*Demo功能:接收11个数据,然后把接收到的数据发送出去

*/



#defineOI2_TXDPDout(6)

#defineOI2_RXDPDin(7)


#defineBuadRate2_9600104

#defineRecive2_Byte19//接收缓冲器的个数

u8len2=0;//接收计数

u8USART2_buf[Recive2_Byte];//接收缓冲区


enum{

COM_START_BIT,

COM_D0_BIT,

COM_D1_BIT,

COM_D2_BIT,

COM_D3_BIT,

COM_D4_BIT,

COM_D5_BIT,

COM_D6_BIT,

COM_D7_BIT,

COM_STOP_BIT,

};


u8recvStat2=COM_STOP_BIT;

u8recvData2=0;


voidIO2_TXD(u8Data)

{

u8i=0;

OI2_TXD=0;

delay_us(BuadRate2_9600);

for(i=0;i< 8; i++)

    {

        if(Data&0x01)

            OI2_TXD = 1;  

        else

            OI2_TXD = 0;  

        

        delay_us(BuadRate2_9600);

        Data = Data>>1;

}

OI2_TXD=1;

delay_us(BuadRate2_9600);

}


voidUSART2_Send(u8*buf,u8len2)

{

u8t;

for(t=0;t< len2; t++)

    {

        IO2_TXD(buf[t]);

    }

}

    

 void IO2Config(void)

 {

    GPIO_InitTypeDef  GPIO_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

     EXTI_InitTypeDef EXTI_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC, ENABLE);  //使能PB,PC端口时钟 

     

     //SoftWare Serial TXD

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;     

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //IO口速度为50MHz  

    GPIO_Init(GPIOD, &GPIO_InitStructure);       

    GPIO_SetBits(GPIOD,GPIO_Pin_6);       

     

    //SoftWare Serial RXD

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

    GPIO_Init(GPIOD, &GPIO_InitStructure);  


    GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource7);

    EXTI_InitStruct.EXTI_Line = EXTI_Line7;

    EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;

    EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断

    EXTI_InitStruct.EXTI_LineCmd=ENABLE;

    EXTI_Init(&EXTI_InitStruct);



    NVIC_InitStructure.NVIC_IRQChannel= EXTI9_5_IRQn ; 

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; 

    NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;  

    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;  

    NVIC_Init(&NVIC_InitStructure);  

}

 

void TIM5_Int_Init(u16 arr,u16 psc)

{

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //时钟使能

    

    //定时器TIM5初始化

    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

    TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update);

    TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE ); //使能指定的TIM5中断,允许更新中断


    //中断优先级NVIC设置

    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM4中断

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级1级

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //从优先级1级

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能

    NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器    

}

void EXTI9_5_IRQHandler(void)

{

    if(EXTI_GetFlagStatus(EXTI_Line7) != RESET)

    {

        if(OI2_RXD == 0) 

        {

            if(recvStat2 == COM_STOP_BIT)

            {

                recvStat2 = COM_START_BIT;

                TIM_Cmd(TIM5, ENABLE);

            }

        }

        EXTI_ClearITPendingBit(EXTI_Line7);

    }

}


void TIM5_IRQHandler(void)

{  

    if(TIM_GetFlagStatus(TIM5, TIM_FLAG_Update) != RESET)

    {

        TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); 

        recvStat2++;

        if(recvStat2 == COM_STOP_BIT)

        {

            TIM_Cmd(TIM5, DISABLE);

            USART2_buf[len2++] = recvData2;

        if(len2 >Recive2_Byte-1)

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    率先入门STM32单片机。这是第三部分针对串口通信的一个实例,虚拟串口其实只是计算机以软件的方式模拟串口通信的功能,可以基本等同于实际的串口。本文主要用于对STM32串口通信的理解。 硬件模......
    说明的是,对于串行异步通信而言,通信双方的波特率不必严格相等,只要双方的差别在一定的范围之内,就可实现准确通信。 2.2.2 软件模拟串口的实现 当波特率确定以后,即可用软件模拟实现串行口。对单片机而言,要实现模拟串口......
    体数值无要求。 另外,需要说明的是,对于串行异步通信,通信双方的波特率不必严格相等,只要双方的差别在一定的范围之内,就可以实现准确的通信。 2.2.2 软件模拟串口的实现 波特率确定以后,即可用软件模拟......
    脚完全够一般的控制使用,最小系统也就是个电路滤波----加上一个47uf电容和一个103电容即可,但因为其是一个5V单片机,供电需要使用5V左右电源。该款单片机视乎没有硬件串口,所以想要使用串口完成开发则应该使用软件模拟串口......
    8051单片机-模拟串口;传统的8051系列单片机一般都配备一个串口,而STC89C52RC增强型单片机也不例外,只有一个串口可供使用,这样就出问题了,假如当前单片机系统要求二个串口或多个串口......
    不要再使能T1的中断了。 我们先来看一下由IO口模拟串口通信直接改为使用硬件UART模块时的程序代码,看看程序是不是简单了很多,因为大部分的工作硬件模块都替我们做了。程序功能和IO口模拟......
    STM32 IO模拟串口程序;  随着单片机的使用日益频繁,用其作前置机进行采集和通信也常见于各种应用,一般是利用前置机采集各种终端数据后进行处理、存储,再主动或被动上报给管理站。这种情况下下,采集会需要一个串口......
    数据给我们的单片机,也可以把我们的单片机发送的数据接收到这个调试助手界面上。   因为初次接触通信方面的技术,所以我把后面的IO模拟串口通信程序进行一下解释,大家可以边看我的解释边看程序,把底......
    STM32中IO口模拟串口输出的乱码现象;因为芯片串口不够用,只好用IO口模拟串口,在网上下载了个模拟串口的程序,可运行,但发现串口输出隔几个字符就会出现乱码,主要部分代码如下: [cpp......
    STM32的USB虚拟串口介绍;通常我们使用STM32进行串口通信会选择其硬件串口,但在某些情况下串口会不够用,这个时候就可以选择USB的虚拟串口,这样可以增加一个串口。 USB是非......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>