你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32模拟UART串口通信

[复制链接]
攻城狮Melo 发布时间:2023-9-20 16:45
UART工作原理    UART即通用异步收发器,是一种串行通信方式。数据在传输过程中是通过一位一位地进行传输来实现通信的,串行通信方式具有传输线少,成本底等优点,缺点是速度慢。串行通信分为两种类型:同步通信方式和异步通信方式。   
但一般多用异步通信方式,主要因为接受和发送的时钟是可以独立的这样有利于增加发送与接收的灵活性。异步通信是一个字符接着一个字符传输,一个字符的信息由起始位、数据位、奇偶校验位和停止位组成。   
每一个字符的传输靠起始位来同步,字符的前面一位是起始位,用下降沿通知收方开始传输,紧接着起始位之后的是数据位,传输时低位在前高位在后,字符本身由5~8位数据位组成。   
数据位后面是奇偶校验位,最后是停止位,停止位是用高电平来标记一个字符的结束,并为下一个字符的传输做准备。停止位后面是不同长度的空闲位。停止位和空闲位都规定为高电平,这样可以保证起始位有一个下降沿。   


UART的帧格式如图:


微信图片_20230920164450.png


    UART的帧格式包括线路空闲状态(idle,高电平)、起始位(start bit,低电平)、5~8位数据位(data bits)、校验位(parity bit,可选)和停止位(stop bit,位数可为1、1.5、2位)。  


UART模拟原理   
UART的模拟方式基本就是定时器+IO口实现。


方案1:只打印不接收
    如果在实际使用中只是为了打印log而不接收数据,可以采用DWT加普通IO口的方式;
  1. #define  VCOM_BOUND     115200
  2. #define  VCOM_PIN       GPIO_Pin_11
  3. #define  VCOM_PORT      GPIOA
  4. #define  VCOM_PIN_HIGH  VCOM_PORT->BSRR = VCOM_PIN
  5. #define  VCOM_PIN_LOW   VCOM_PORT->BRR  = VCOM_PIN

  6. #define  BSP_REG_DEM_CR                           (*(volatile unsigned int *)0xE000EDFC) //DEMCR寄存器
  7. #define  BSP_REG_DWT_CR                           (*(volatile unsigned int *)0xE0001000)   //DWT控制寄存器
  8. #define  BSP_REG_DWT_CYCCNT                       (*(volatile unsigned int *)0xE0001004) //DWT时钟计数寄存器
  9. #define  BSP_REG_DBGMCU_CR                        (*(volatile unsigned int *)0xE0042004)

  10. #define  DEF_BIT_00                               0x01u
  11. #define  DEF_BIT_24                               0x01000000u
  12. #define  BSP_BIT_DEM_CR_TRCENA                    DEF_BIT_24   
  13. #define  BSP_BIT_DWT_CR_CYCCNTENA                 DEF_BIT_00
  14. static unsigned int  sys_clock = 48000000;

  15. inline void dwt_start(void)
  16. {
  17. BSP_REG_DEM_CR     |= (unsigned int)BSP_BIT_DEM_CR_TRCENA;
  18.     BSP_REG_DWT_CYCCNT  = (unsigned int)0u;            //初始化CYCCNT寄存器
  19.     BSP_REG_DWT_CR     |= (unsigned int)BSP_BIT_DWT_CR_CYCCNTENA;    //开启CYCCNT
  20. }

  21. inline void dwt_stop(void)
  22. {
  23. BSP_REG_DWT_CR = 0;
  24. }

  25. void vcom_pin_init(void)
  26. {
  27.     GPIO_InitTypeDef GPIO_InitStructure;
  28.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  29.     GPIO_InitStructure.GPIO_Pin   = VCOM_PIN;
  30.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  31.     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
  32.     GPIO_Init(VCOM_PORT, &GPIO_InitStructure);
  33.     GPIO_SetBits(VCOM_PORT,VCOM_PIN);
  34. VCOM_PIN_HIGH;
  35. }

  36. void vcom_put_char(char ch)
  37. {
  38. int i;
  39. int dat[8];
  40. uint32_t sys_clk, bit_width;
  41. volatile uint32_t time_stamp;

  42.     sys_clk = sys_clock/1000000;  
  43.     bit_width = 1000000*sys_clk/VCOM_BOUND;
  44.     for(i=0; i<8; i++)           
  45.     {
  46.         if(ch & 0x01)
  47.             dat[i] = 1;
  48.         else
  49.             dat[i] = 0;
  50.         ch >>= 1;
  51.     }
  52.     OS_CPU_SR cpu_sr;
  53.     enter_critical();//以下代码进行临界保护,防止被中断打断造成发送误码
  54.     dwt_start();
  55.     VCOM_PIN_LOW; //发送起始位
  56. time_stamp = BSP_REG_DWT_CYCCNT;
  57. while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width));
  58. for(i=0; i<8; i++)
  59. {
  60.   if(dat[i])
  61.    VCOM_PIN_HIGH;
  62.   else
  63.    VCOM_PIN_LOW;
  64.   time_stamp = BSP_REG_DWT_CYCCNT;
  65.   while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width)); //发8bit 数据位
  66. }
  67. VCOM_PIN_HIGH;
  68. time_stamp = BSP_REG_DWT_CYCCNT;
  69. while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width));     //发停止位
  70. dwt_stop();
  71. exit_critical();
  72. }

  73. void vcom_printf(const char *fmt, ...)
  74. {
  75.     char buf[0x80];
  76.     int  i;
  77.     va_list ap;
  78. memset(buf, 0x00, sizeof(buf));
  79.     va_start(ap, fmt);
  80.     vsnprintf(buf, sizeof(buf), fmt, ap);
  81.     va_end(ap);

  82. i = 0;
  83. while(buf[i])
  84. {
  85.   vcom_put_char(buf[i]);
  86.   i++;
  87. }
  88. }
复制代码


方案2:半双工UART
    实现方式: 普通定时器+普通IO口中断+fifo

  1. /**
  2. *软件串口的实现(IO模拟串口)
  3. * 波特率:9600    1-8-N
  4. * TXD : PC13
  5. * RXD : PB14
  6. * 使用外部中断对RXD的下降沿进行触发,使用定时器4按照9600波特率进行定时数据接收。
  7. * Demo功能: 接收11个数据,然后把接收到的数据发送出去
  8. */


  9. #define OI_TXD PCout(13)
  10. #define OI_RXD PBin(14)

  11. #define BuadRate_9600 100

  12. u8 len = 0; //接收计数
  13. u8 USART_buf[11];  //接收缓冲区

  14. enum{
  15. COM_START_BIT,
  16. COM_D0_BIT,
  17. COM_D1_BIT,
  18. COM_D2_BIT,
  19. COM_D3_BIT,
  20. COM_D4_BIT,
  21. COM_D5_BIT,
  22. COM_D6_BIT,
  23. COM_D7_BIT,
  24. COM_STOP_BIT,
  25. };

  26. u8 recvStat = COM_STOP_BIT;
  27. u8 recvData = 0;

  28. void IO_TXD(u8 Data)
  29. {
  30. u8 i = 0;
  31. OI_TXD = 0;  
  32. delay_us(BuadRate_9600);
  33. for(i = 0; i < 8; i++)
  34. {
  35.   if(Data&0x01)
  36.    OI_TXD = 1;  
  37.   else
  38.    OI_TXD = 0;  
  39.   
  40.   delay_us(BuadRate_9600);
  41.   Data = Data>>1;
  42. }
  43. OI_TXD = 1;
  44. delay_us(BuadRate_9600);
  45. }

  46. void USART_Send(u8 *buf, u8 len)
  47. {
  48. u8 t;
  49. for(t = 0; t < len; t++)
  50. {
  51.   IO_TXD(buf[t]);
  52. }
  53. }

  54. void IOConfig(void)
  55. {
  56. GPIO_InitTypeDef  GPIO_InitStructure;
  57. NVIC_InitTypeDef NVIC_InitStructure;
  58. EXTI_InitTypeDef EXTI_InitStruct;
  59. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE);  //使能PB,PC端口时钟

  60. //SoftWare Serial TXD
  61. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;     
  62. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //推挽输出
  63. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //IO口速度为50MHz  
  64. GPIO_Init(GPIOC, &GPIO_InitStructure);      
  65. GPIO_SetBits(GPIOC,GPIO_Pin_13);      
  66.   
  67.   
  68. //SoftWare Serial RXD
  69. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
  70. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  71. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  72. GPIO_Init(GPIOB, &GPIO_InitStructure);  

  73. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
  74. EXTI_InitStruct.EXTI_Line = EXTI_Line14;
  75. EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
  76. EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断
  77. EXTI_InitStruct.EXTI_LineCmd=ENABLE;
  78. EXTI_Init(&EXTI_InitStruct);

  79. NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn ;
  80. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
  81. NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;  
  82. NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;  
  83. NVIC_Init(&NVIC_InitStructure);  

  84. }

  85. void TIM4_Int_Init(u16 arr,u16 psc)
  86. {
  87. TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  88. NVIC_InitTypeDef NVIC_InitStructure;

  89. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能

  90. //定时器TIM4初始化
  91. TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  92. TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
  93. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
  94. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  95. TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
  96. TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
  97. TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

  98. //中断优先级NVIC设置
  99. NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  //TIM4中断
  100. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级1级
  101. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //从优先级1级
  102. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  103. NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器   
  104. }


  105. int main(void)
  106. {  
  107. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
  108. delay_init();
  109. IOConfig();
  110. TIM4_Int_Init(107, 71);  //1M计数频率

  111. while(1)
  112. {
  113.   if(len > 10)
  114.   {
  115.    len = 0;
  116.    USART_Send(USART_buf,11);
  117.   }
  118. }
  119. }

  120. void EXTI15_10_IRQHandler(void)
  121. {
  122. if(EXTI_GetFlagStatus(EXTI_Line14) != RESET)
  123. {
  124.   if(OI_RXD == 0)
  125.   {
  126.    if(recvStat == COM_STOP_BIT)
  127.    {
  128.     recvStat = COM_START_BIT;
  129.     TIM_Cmd(TIM4, ENABLE);
  130.    }
  131.   }
  132.   EXTI_ClearITPendingBit(EXTI_Line14);
  133. }
  134. }

  135. void TIM4_IRQHandler(void)
  136. {  
  137. if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) != RESET)
  138. {
  139.   TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
  140.   recvStat++;
  141.   if(recvStat == COM_STOP_BIT)
  142.   {
  143.    TIM_Cmd(TIM4, DISABLE);
  144.    USART_buf[len++] = recvData;
  145.    return;
  146.   }
  147.   if(OI_RXD)
  148.   {
  149.    recvData |= (1 << (recvStat - 1));
  150.   }else{
  151.    recvData &= ~(1 << (recvStat - 1));
  152.   }
  153. }  
  154. }
复制代码

转载自:玩转单片机与嵌入式
如有侵权请联系删除



收藏 评论0 发布时间:2023-9-20 16:45

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版