打印

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

[复制链接]
1038|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 会笑的星星 于 2020-4-5 09:44 编辑

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


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

首先来看看sim_uart.h文件。

//sim_uart.h

#ifndef _SIM_UART_
#define _SIM_UART_

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

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

//把每一个数据按照时序图发送出去。为保证实时性,这个函数需要在中断中调用,调用
//间隔取决于串口波特率。比如我需要的串口波特率是9600bps,那么传送一bit所要的时
//间就是104us,此时中断的调用间隔就是104us
void app_sim_uart_transmit(void );

//把一个字节的数据复制到串口缓冲区,这相当于把数据放到硬件串口的数据缓冲区
void app_sim_uart_tx_set_data(unsigned char  data);

//查询一个串口缓冲区的数据是否发送完成,如果完成返回0,否则返回1
unsigned char app_sim_uart_get_tx_state(void );

//初始化模拟串口模块的参数
void app_sim_uart_init(void );

#endif

再看看sim_uart.c文件。

//sim_uart.c
               
#include "sim_uart.h"
               
struct
{
   unsigned char data;
   unsigned char step : 3,
                 start_f : 1,
                 bit_cnt: 4;                  
}sim_uart;
               
typedef enum
{
  SIM_UART_IDEL,
  SIM_UART_DATA_SEND_START,
  SIM_UART_DATA_SEND,
  SIM_UART_DATA_SEND_DONE
  }sim_uart_send_state_t;
               
////////////////////////////////////////////////////////////////////////////////
//初始化串口模块
void app_sim_uart_init(void )
{
   sim_uart.step = SIM_UART_DATA_SEND_INIT;
   sim_uart.start_f  = 0;
   SIM_UART_IO_H;
}   

//检查发送状态
unsigned char app_sim_uart_get_tx_state(void )
{
  if(!sim_uart.start_f)
  {
    return 0;
  }
  
  return 1;
}

//将数据放到串口缓冲区
void  app_sim_uart_tx_set_data(unsigned char data)
{
  sim_uart.data = data;
  sim_uart.start_f = 1;
  sim_uart.step = SIM_UART_DATA_SEND_INIT;
}

//这个函数模拟了串口的时序
void app_sim_uart_transmit(void )
{               
  if(sim_uart.start_f == 1)
  {         
     switch(sim_uart.step)
     {
            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;
                        
           case SIM_UART_DATA_SEND_DONE:
               SIM_UART_IO_H;
               sim_uart.start_f = 0;
               sim_uart.step = SIM_UART_DATA_SEND_INIT;
               break;
                        
           case SIM_UART_DATA_SEND_START:
                SIM_UART_IO_L;
                sim_uart.step = SIM_UART_DATA_SEND;
                break;

           case SIM_UART_DATA_SEND_INIT:
                SIM_UART_IO_H;
                sim_uart.bit_cnt = 0;
                sim_uart.step = SIM_UART_DATA_SEND_START;
                break;
     }
  }
}
        

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

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

//9600bps,104us定时中断
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{
  app_sim_uart_transmit();

  TIM4->SR1 = (uint8_t)(~TIM4_IT_UPDATE);
}

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

#include "sim_uart.h"

main()
{
  app_sim_uart_init();  
  
  while(1)
  {
   //检查串口缓冲区是否为空,如果为空,发送下一个字节   
   if(app_sim_uart_get_tx_state() == 0)
   {
      //发送下一个字节的数据
      app_sim_uart_tx_set_data(0xaa);
   }
  }
}

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


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

附件:
sim_uart.zip (959 Bytes)


使用特权

评论回复

相关帖子

沙发
会笑的星星|  楼主 | 2020-4-5 09:43 | 只看该作者

使用特权

评论回复
板凳
会笑的星星|  楼主 | 2020-4-7 17:04 | 只看该作者

使用特权

评论回复
地板
旭日东起| | 2020-4-9 10:33 | 只看该作者
谢谢了

使用特权

评论回复
5
叶春勇| | 2020-4-9 18:20 | 只看该作者
好贴,漏了,顶起。

使用特权

评论回复
6
airwill| | 2020-4-11 16:51 | 只看该作者
io 口模拟串口,  发送还比较简单一些,  接收会麻烦些

使用特权

评论回复
7
andy520520| | 2020-12-4 15:11 | 只看该作者
用软件模拟发送根本就不是难事

你用软件实现实现串口接收试下?

使用特权

评论回复
8
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卡死在中断里面

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

披不同的马甲混迹于论坛

使用特权

评论回复
9
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卡死在中断里面

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

披不同的马甲混迹于论坛

使用特权

评论回复
10
pq113_6| | 2020-12-10 10:00 | 只看该作者
我做了一个接收的,最大波特率38400,再大好像就不稳定了

使用特权

评论回复
11
uboot| | 2021-1-12 08:44 | 只看该作者
to 9楼,代码不会在中断卡1ms

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

96

帖子

16

粉丝