使用软件模拟硬件串口模块 ——工程师原创应用笔记

发布时间:2020-06-28 阅读量:1110 来源: 我爱方案网 作者:

【编者按】有时由于单片机的硬件串口不够用,在要求不高的情况下,比如发送一些调试信息,我们就可以使用I/O口模拟一个串口以完成要做的事情。要自己模拟串口,我们必须要知道串口时序图,如下图所示。poiuy.jpg


知道时序图,我们就可以编写代码。下面代码是我写的一个模拟串口,波特率取决于你的要求,但模拟串口不宜使用高波特率,一般而言推荐9600以下会比较好,另外该模拟串口没有奇偶校验位。


首先来看看sim_uart.h文件。


  1. //sim_uart.h


  2. #ifndef _SIM_UART_

  3. #define _SIM_UART_


  4. #include "stm8s.h" //替换成自己的头文件


  5. #define SIM_UART_IO_H   GPIOA->ODR |= 0x08 //拉高,需要更换成你自己的管脚

  6. #define SIM_UART_IO_L   GPIOA->ODR &= ~0x08//拉低,需要更换成你自己的管脚


  7. //把每一个数据按照时序图发送出去。为保证实时性,这个函数需要在中断中调用,调用

  8. //间隔取决于串口波特率。比如我需要的串口波特率是9600bps,那么传送一bit所要的时

  9. //间就是104us,此时中断的调用间隔就是104us

  10. void app_sim_uart_transmit(void );


  11. //把一个字节的数据复制到串口缓冲区,这相当于把数据放到硬件串口的数据缓冲区

  12. void app_sim_uart_tx_set_data(unsigned char  data);


  13. //查询一个串口缓冲区的数据是否发送完成,如果完成返回0,否则返回1

  14. unsigned char app_sim_uart_get_tx_state(void );


  15. //初始化模拟串口模块的参数

  16. void app_sim_uart_init(void );


  17. #endif

复制代码



再看看sim_uart.c文件。


  1. //sim_uart.c

  2.                

  3. #include "sim_uart.h"

  4.                

  5. struct

  6. {

  7.    unsigned char data;

  8.    unsigned char step : 3,

  9.                  start_f : 1,

  10.                  bit_cnt: 4;                  

  11. }sim_uart;

  12.                

  13. typedef enum

  14. {

  15.   SIM_UART_IDEL,

  16.   SIM_UART_DATA_SEND_START,

  17.   SIM_UART_DATA_SEND,

  18.   SIM_UART_DATA_SEND_DONE

  19.   }sim_uart_send_state_t;

  20.                

  21. ////////////////////////////////////////////////////////////////////////////////

  22. //初始化串口模块

  23. void app_sim_uart_init(void )

  24. {

  25.    sim_uart.step = SIM_UART_DATA_SEND_INIT;

  26.    sim_uart.start_f  = 0;

  27.    SIM_UART_IO_H;

  28. }   


  29. //检查发送状态

  30. unsigned char app_sim_uart_get_tx_state(void )

  31. {

  32.   if(!sim_uart.start_f)

  33.   {

  34.     return 0;

  35.   }

  36.   

  37.   return 1;

  38. }


  39. //将数据放到串口缓冲区

  40. void  app_sim_uart_tx_set_data(unsigned char data)

  41. {

  42.   sim_uart.data = data;

  43.   sim_uart.start_f = 1;

  44.   sim_uart.step = SIM_UART_DATA_SEND_INIT;

  45. }


  46. //这个函数模拟了串口的时序

  47. void app_sim_uart_transmit(void )

  48. {               

  49.   if(sim_uart.start_f == 1)

  50.   {         

  51.      switch(sim_uart.step)

  52.      {

  53.             case SIM_UART_DATA_SEND:

  54.                 if(sim_uart.data & 1)

  55.                 {

  56.                   SIM_UART_IO_H;

  57.                 }

  58.                 else

  59.                 {

  60.                   SIM_UART_IO_L;

  61.                 }

  62.                         

  63.                 sim_uart.data >>= 1;

  64.                 if(++sim_uart.bit_cnt >= 8)

  65.                 {

  66.                   sim_uart.bit_cnt = 0;

  67.                   sim_uart.step = SIM_UART_DATA_SEND_DONE;

  68.                 }

  69.                break;

  70.                         

  71.            case SIM_UART_DATA_SEND_DONE:

  72.                SIM_UART_IO_H;

  73.                sim_uart.start_f = 0;

  74.                sim_uart.step = SIM_UART_DATA_SEND_INIT;

  75.                break;

  76.                         

  77.            case SIM_UART_DATA_SEND_START:

  78.                 SIM_UART_IO_L;

  79.                 sim_uart.step = SIM_UART_DATA_SEND;

  80.                 break;


  81.            case SIM_UART_DATA_SEND_INIT:

  82.                 SIM_UART_IO_H;

  83.                 sim_uart.bit_cnt = 0;

  84.                 sim_uart.step = SIM_UART_DATA_SEND_START;

  85.                 break;

  86.      }

  87.   }

  88. }

  89.         

复制代码




再看看sim_uart.c文件。


  1. //sim_uart.c

  2.                

  3. #include "sim_uart.h"

  4.                

  5. struct

  6. {

  7.    unsigned char data;

  8.    unsigned char step : 3,

  9.                  start_f : 1,

  10.                  bit_cnt: 4;                  

  11. }sim_uart;

  12.                

  13. typedef enum

  14. {

  15.   SIM_UART_IDEL,

  16.   SIM_UART_DATA_SEND_START,

  17.   SIM_UART_DATA_SEND,

  18.   SIM_UART_DATA_SEND_DONE

  19.   }sim_uart_send_state_t;

  20.                

  21. ////////////////////////////////////////////////////////////////////////////////

  22. //初始化串口模块

  23. void app_sim_uart_init(void )

  24. {

  25.    sim_uart.step = SIM_UART_DATA_SEND_INIT;

  26.    sim_uart.start_f  = 0;

  27.    SIM_UART_IO_H;

  28. }   


  29. //检查发送状态

  30. unsigned char app_sim_uart_get_tx_state(void )

  31. {

  32.   if(!sim_uart.start_f)

  33.   {

  34.     return 0;

  35.   }

  36.   

  37.   return 1;

  38. }


  39. //将数据放到串口缓冲区

  40. void  app_sim_uart_tx_set_data(unsigned char data)

  41. {

  42.   sim_uart.data = data;

  43.   sim_uart.start_f = 1;

  44.   sim_uart.step = SIM_UART_DATA_SEND_INIT;

  45. }


  46. //这个函数模拟了串口的时序

  47. void app_sim_uart_transmit(void )

  48. {               

  49.   if(sim_uart.start_f == 1)

  50.   {         

  51.      switch(sim_uart.step)

  52.      {

  53.             case SIM_UART_DATA_SEND:

  54.                 if(sim_uart.data & 1)

  55.                 {

  56.                   SIM_UART_IO_H;

  57.                 }

  58.                 else

  59.                 {

  60.                   SIM_UART_IO_L;

  61.                 }

  62.                         

  63.                 sim_uart.data >>= 1;

  64.                 if(++sim_uart.bit_cnt >= 8)

  65.                 {

  66.                   sim_uart.bit_cnt = 0;

  67.                   sim_uart.step = SIM_UART_DATA_SEND_DONE;

  68.                 }

  69.                break;

  70.                         

  71.            case SIM_UART_DATA_SEND_DONE:

  72.                SIM_UART_IO_H;

  73.                sim_uart.start_f = 0;

  74.                sim_uart.step = SIM_UART_DATA_SEND_INIT;

  75.                break;

  76.                         

  77.            case SIM_UART_DATA_SEND_START:

  78.                 SIM_UART_IO_L;

  79.                 sim_uart.step = SIM_UART_DATA_SEND;

  80.                 break;


  81.            case SIM_UART_DATA_SEND_INIT:

  82.                 SIM_UART_IO_H;

  83.                 sim_uart.bit_cnt = 0;

  84.                 sim_uart.step = SIM_UART_DATA_SEND_START;

  85.                 break;

  86.      }

  87.   }

  88. }

  89.         

复制代码


最后来看看如何应用上述的模拟串口。


首先,在硬件中断中调用app_sim_uart_transmit()函数,如下所示。



  1. //9600bps,104us定时中断

  2. INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)

  3. {

  4.   app_sim_uart_transmit();


  5.   TIM4->SR1 = (uint8_t)(~TIM4_IT_UPDATE);

  6. }

复制代码


接下来我们可以在应用层中使用模拟串口了,如下所示。


  1. #include "sim_uart.h"


  2. main()

  3. {

  4.   app_sim_uart_init();  

  5.   

  6.   while(1)

  7.   {

  8.    //检查串口缓冲区是否为空,如果为空,发送下一个字节   

  9.    if(app_sim_uart_get_tx_state() == 0)

  10.    {

  11.       //发送下一个字节的数据

  12.       app_sim_uart_tx_set_data(0xaa);

  13.    }

  14.   }

  15. }

复制代码


上述代码是经过实际检验的,如果需要,可以在自己的项目中简单修改一下I/O口后应该就能方便的移植到你的项目中了。


本文作者:会笑的星星是一名设计开发工程师,在平台上的用户名为“ 1585292050XQYe”,有多年的物联网安防产品设计开发经验。

相关资讯
核心对比!无源晶振与有源晶振在结构和工作原理的本质区别

无源晶振与有源晶振是电子系统中两种根本性的时钟元件,其核心区别在于是否内置振荡电路。晶振结构上的本质差异,直接决定了两者在应用场景、设计复杂度和成本上的不同。

温度稳定性对RTC晶振的计时误差影响与分析

RTC(实时时钟)电路广泛采用音叉型32.768kHz晶振作为时基源,但其频率稳定性对温度变化极为敏感。温度偏离常温基准(通常为25℃)时,频率会产生显著漂移,且偏离越远漂移越大。

从参数到实践!剖析有源晶振的频率稳定度、老化率及正确接线方案

有源晶振作为晶振的核心类别,凭借其内部集成振荡电路的独特设计,无需依赖外部电路即可独立工作,在电子设备中扮演着关键角色。本文将系统解析有源晶振的核心参数、电路设计及引脚接法,重点阐述其频率稳定度、老化率等关键指标,并结合实际电路图与引脚定义,帮助大家全面掌握有源晶振的应用要点,避免因接线错误导致器件失效。

如何对抗晶振老化?深入生产工艺与终端应用的防老化指南

晶振老化是影响其长期频率稳定性的核心因素,主要表现为输出频率随时间的缓慢漂移。无论是晶体谐振器还是晶体振荡器,在生产过程中均需经过针对性的防老化处理,但二者的工艺路径与耗时存在显著差异。

无源晶振YSX321SL应用于高精度HUD平视显示系统YXC3225

在现代汽车行业中,HUD平视显示系统正日益成为驾驶员的得力助手,为驾驶员提供实时导航、车辆信息和警示等功能,使驾驶更加安全和便捷。在HUD平视显示系统中,高精度的晶振是确保系统稳定运行的关键要素。YSX321SL是一款优质的3225无源晶振,拥有多项卓越特性,使其成为HUD平视显示系统的首选。