使用软件模拟硬件串口模块

[复制链接]
1431|10
 楼主| 会笑的星星 发表于 2020-4-5 09:38 | 显示全部楼层 |阅读模式
本帖最后由 会笑的星星 于 2020-4-5 09:44 编辑

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


知道时序图,我们就可以编写代码。下面代码是我写的一个模拟串口,波特率取决于你的要求,但模拟串口不宜使用高波特率,一般而言推荐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.         

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

首先,在硬件中断中调用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口后应该就能方便的移植到你的项目中了。


参考:
通用队列以及应用
https://bbs.21ic.com/icview-2894504-1-1.html

附件:


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 会笑的星星 发表于 2020-4-5 09:43 | 显示全部楼层
 楼主| 会笑的星星 发表于 2020-4-7 17:04 | 显示全部楼层
旭日东起 发表于 2020-4-9 10:33 | 显示全部楼层
谢谢了
叶春勇 发表于 2020-4-9 18:20 | 显示全部楼层
好贴,漏了,顶起。
airwill 发表于 2020-4-11 16:51 | 显示全部楼层
io 口模拟串口,  发送还比较简单一些,  接收会麻烦些
andy520520 发表于 2020-12-4 15:11 | 显示全部楼层
用软件模拟发送根本就不是难事

你用软件实现实现串口接收试下?
andy520520 发表于 2020-12-8 08:48 | 显示全部楼层
case SIM_UART_DATA_SEND:
                if(sim_uart.data & 1)
                {
                  SIM_UART_IO_H;
                }
                else
                {
                  SIM_UART_IO_L;
                }
                        
                sim_uart.data >>= 1;
                if(++sim_uart.bit_cnt >= 8)
                {
                  sim_uart.bit_cnt = 0;
                  sim_uart.step = SIM_UART_DATA_SEND_DONE;
                }
               break;

兄弟,你这状态机做得可以哟,1ms卡死在中断里面

拿个别人的案例改下发到网上,你搜下网上有多少这个

披不同的马甲混迹于论坛
andy520520 发表于 2020-12-8 08:49 | 显示全部楼层
case SIM_UART_DATA_SEND:
                if(sim_uart.data & 1)
                {
                  SIM_UART_IO_H;
                }
                else
                {
                  SIM_UART_IO_L;
                }
                        
                sim_uart.data >>= 1;
                if(++sim_uart.bit_cnt >= 8)
                {
                  sim_uart.bit_cnt = 0;
                  sim_uart.step = SIM_UART_DATA_SEND_DONE;
                }
               break;

兄弟,你这状态机做得可以哟,1ms卡死在中断里面

拿个别人的案例改下发到网上,你搜下网上有多少这个

披不同的马甲混迹于论坛
pq113_6 发表于 2020-12-10 10:00 | 显示全部楼层
我做了一个接收的,最大波特率38400,再大好像就不稳定了
uboot 发表于 2021-1-12 08:44 来自手机 | 显示全部楼层
to 9楼,代码不会在中断卡1ms
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

96

帖子

17

粉丝
快速回复 在线客服 返回列表 返回顶部