0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM32模拟串口(UART)使用

硬件攻城狮 来源:CSDN-噗尼果 2023-03-22 15:56 次阅读

1、添加头文件

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

d63a3398-c884-11ed-bfe3-dac502259ad0.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)
{
len2=0;
USART2_Send(USART2_buf,Recive2_Byte);
}
return;
}
if(OI2_RXD)
{
recvData2|=(1<< (recvStat2 - 1));
        }else{
            recvData2 &= ~(1 << (recvStat2 - 1));
        } 
  }  
}

vuart2.h

#ifndef__VUART2__H
#define__VUART2__H
#include"stm32f10x.h"

voidIO2_TXD(u8Data);
voidUSART2_Send(u8*buf,u8len);
voidIO2Config(void);
voidTIM5_Int_Init(u16arr,u16psc);
#endif

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6001

    文章

    43973

    浏览量

    620838
  • 通信
    +关注

    关注

    18

    文章

    5706

    浏览量

    134394
  • STM32
    +关注

    关注

    2239

    文章

    10671

    浏览量

    348744
  • 串口
    +关注

    关注

    14

    文章

    1483

    浏览量

    74511
  • uart
    +关注

    关注

    22

    文章

    1159

    浏览量

    99961

原文标题:STM32 模拟串口(UART)使用!

文章出处:【微信号:mcu168,微信公众号:硬件攻城狮】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    STM32外部中断模拟UART串口,不看肯定后悔

    STM32外部中断模拟UART串口,不看肯定后悔
    发表于 11-17 07:47

    IO模拟串口UART

    IO模拟串口UART 本文介绍GPIO模拟UART的算法和实现
    发表于 04-03 14:11 86次下载

    niosii的UART串口通信

    niosii的UART串口通信niosii的UART串口通信。
    发表于 04-06 17:03 0次下载

    uart是什么意思?认识uart串口

    设备没有显示屏,无法获得嵌入式设备实时数据信息,通过UART串口和超级终端相连,打印嵌入式设备输出信息。并且在对嵌入式系统进行跟踪和调试时,UART串口了是必要的通信手段。比如:网络路
    发表于 12-06 14:51 10.7w次阅读
    <b class='flag-5'>uart</b>是什么意思?认识<b class='flag-5'>uart</b><b class='flag-5'>串口</b>

    串口通信的原理,IO口模拟UART串口通信

    UART串口波特率,常用的值是300、600、1200、2400、4800、9600、14400、19200、28800、38400、57600、115200等速率。IO口模拟UART
    的头像 发表于 05-04 15:26 2.1w次阅读
    <b class='flag-5'>串口</b>通信的原理,IO口<b class='flag-5'>模拟</b><b class='flag-5'>UART</b><b class='flag-5'>串口</b>通信

    基于STM32Uart2串口问题

    21IC买了一块1元的活动板子,移植了RTT(RT-THREAD)的国人嵌入式操作系统.系统能够正常跑起来,接着就按照RTT的官网教程,写了一个串口驱动. 原来定义的是UART1的端口,但是没有
    发表于 06-08 08:30 8312次阅读
    基于<b class='flag-5'>STM32</b>的<b class='flag-5'>Uart</b>2<b class='flag-5'>串口</b>问题

    STM32F407的串口UART 基础配置STM32CubeMX

    STM32F407的串口UART 基础配置STM32CubeMX
    发表于 11-29 16:06 51次下载
    <b class='flag-5'>STM32</b>F407的<b class='flag-5'>串口</b><b class='flag-5'>UART</b> 基础配置<b class='flag-5'>STM32</b>CubeMX

    STM32软件模拟串口的实现-基于CrubeMX

    STM32软件模拟串口的实现-基于CrubeMX
    发表于 11-30 10:36 10次下载
    <b class='flag-5'>STM32</b>软件<b class='flag-5'>模拟</b><b class='flag-5'>串口</b>的实现-基于CrubeMX

    基于STM32UART串口通信协议(一)详解

    F429开发板来举例讲解(其他STM32系列芯片大多数都可以按照这些步骤来操作的),如有不足请多多指教。2、UART简介  嵌入式开发中,UART串口通信协议是我们常用的通信协议(
    发表于 11-30 14:36 22次下载
    基于<b class='flag-5'>STM32</b>之<b class='flag-5'>UART</b><b class='flag-5'>串口</b>通信协议(一)详解

    stm32使用IO模拟串口

    STM32使用IO口模拟串口期望效果:使用普通IO模拟串口,实现全双工通讯功能。硬件资源:1.2个IO口2.1个外部中断3.1个定时器
    发表于 12-04 20:21 17次下载
    <b class='flag-5'>stm32</b>使用IO<b class='flag-5'>模拟</b><b class='flag-5'>串口</b>

    STM32F103RCT6 两个串口通讯程序

    STM32F103RCT6 两个串口通讯程序此程序为STM32F103RCT6单片机UART1 与UART3两个个
    发表于 12-24 18:38 47次下载
    <b class='flag-5'>STM32</b>F103RCT6 两个<b class='flag-5'>串口</b>通讯程序

    STM32F103VE USART & UART串口通信

    STM32F103VE共有5个串口功能,其中USART1,USART2,USART3为通用同步异步串口通信,UART4,UART5为通用异步
    发表于 12-24 19:06 23次下载
    <b class='flag-5'>STM32</b>F103VE USART & <b class='flag-5'>UART</b><b class='flag-5'>串口</b>通信

    STM32 HAL库 UART 串口读写功能笔记

    STM32L0 HAL库 UART 串口读写功能串口发送功能:uint8_t TxData[10]= “01234abcde”;HAL_UART
    发表于 12-27 19:11 13次下载
    <b class='flag-5'>STM32</b> HAL库 <b class='flag-5'>UART</b> <b class='flag-5'>串口</b>读写功能笔记

    STM32笔记4】UART的时序解读与硬件运用(即硬件UART的实现,另有硬件UART运用和软件UART运用/硬件串口和软件串口/USART的差异说明)

    一、UART简介1、UART(Universal Asynchronous Receiver Transmitter)总线是异步串口,故一般比I2C、SPI两种同步串口的结构要复杂很多
    发表于 01-17 13:00 3次下载
    【<b class='flag-5'>STM32</b>笔记4】<b class='flag-5'>UART</b>的时序解读与硬件运用(即硬件<b class='flag-5'>UART</b>的实现,另有硬件<b class='flag-5'>UART</b>运用和软件<b class='flag-5'>UART</b>运用/硬件<b class='flag-5'>串口</b>和软件<b class='flag-5'>串口</b>/USART的差异说明)

    单片机IO口模拟UART串口通信

    为了让大家充分理解 UART 串口通信的原理,我们先把 P3.0 和 P3.1 当做 IO 口来进行模拟实际串口通信的过程,原理搞懂后,我们再使用寄存器配置实现
    发表于 02-09 10:25 23次下载
    单片机IO口<b class='flag-5'>模拟</b><b class='flag-5'>UART</b><b class='flag-5'>串口</b>通信