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)

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

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

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

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

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

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

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

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