单片机IO口模拟串口实现原理

参考链接

1、使用GPIO来模拟UART
2、STM32之IO模拟串口篇

1、工作原理

单片机IO口模拟串口的实现原理通常是通过软件来模拟串行通信的传输和接收。下面说明了单片机IO口模拟串口的实现原理:

  1. 配置IO口:选择两个IO口作为模拟串口的发送和接收引脚。通常使用的是GPIO引脚。

  2. 设置通信参数:设置波特率、数据位、停止位和校验位等串口通信参数。

  3. 发送数据:通过控制发送引脚的电平变化来模拟串口数据的传输。根据通信参数,将要发送的数据进行位的切换操作,逐位通过发送引脚发送出去。

  4. 接收数据:通过读取接收引脚的电平变化来模拟串口数据的接收。根据通信参数,读取接收引脚的电平状态,逐位组装接收到的数据。

  5. 确保同步:为了确保发送和接收的同步性,可以在发送和接收的开头和结尾添加起始位和停止位。发送时在数据的起始位置输出一个低电平的起始位,接收时检测到低电平的起始位后开始接收数据,接收完毕后检测到高电平的停止位表示接收完成。

单片机IO口模拟串口的实现原理相对简单,但通信速率可能受到单片机性能和IO口速度的限制,因此在高速通信或实时性要求较高的场景下,可能需要选择硬件串口或其他更高级的通信方式。

2、UART协议:

空闲位:当uart处于空闲状态(线路没有数据传输)时,TX、RX线都处于高电平状态(逻辑“1”),一般需要把相应IO口配置为上拉。

起始位:由高电平跳变为低电平,且持续一个位宽度,表示触发起始信号。

数据位:数据位可以5、6、7或8位,从最低位开始一位接着一位的传送。

校验位:有奇、偶或无校验。

	 奇校验:数据位+校验位的“1”的位数总和为奇数;
	 偶校验:数据位+校验位的“1”的位数总和为偶数;

例如:奇校验中,数据位“1”的位数为偶数个,则此时校验位为“1”。

无校验:顾名思义就是没有校验位,数据位后面接停止位。(通常配置为无校验。)

停止位:将数据线拉为高电平,可以设置停止位宽度为1位、1.5位或者2位。

波特率:在串口中波特率为每秒传送的bit数;通信双方必须设置相同的波特率,否则接收的数据为乱码。波特率9600时,传输1bit所需时间为(1/9600)us,大约为104us。

3、代码实现(无OS)

/**
 */
 
#include "./BSP/MYPRINTF/myprintf.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "./BSP/TIMER/btim.h"
 
//开始接收数据标志
volatile unsigned char uartStartFlag = 0;
 
//串口接收缓存
unsigned char uartBuf[256] = {0};
unsigned char uartBufLen = 0;
unsigned char uartHaveDat = 0;
 
//超时错误处理
volatile unsigned int uartBufTimeout = 0;
volatile unsigned int uartBufStartTimeout = 0;
 
void myuart_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
   
    TX_GPIO_CLK_ENABLE();
    gpio_init_struct.Pin = TX_GPIO_PIN;                   
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;         
    HAL_GPIO_Init(TX_GPIO_PORT, &gpio_init_struct);       
          
    RX_GPIO_CLK_ENABLE(); 
    gpio_init_struct.Pin = RX_GPIO_PIN;   
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */   
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;                  
    HAL_GPIO_Init(RX_GPIO_PORT, &gpio_init_struct);                        
    HAL_NVIC_EnableIRQ(RX_INT_IRQn);
    Set_TX(0);                                               
}
 
void send_byte(uint8_t data){
   Set_TX(0);
   delay_us(104);
   for(int i = 0; i < 8; i++){
      if(data & 0x01){
         Set_TX(1);
      }
      else{
         Set_TX(0);
      }
      delay_us(104);
      data = data >> 1;
   }
   Set_TX(1);
   delay_us(104);
      
}
 
void send_str(char *dat){
   for(int i = 0; i < strlen(dat); i++){
      send_byte(dat[i]);
   }
}
 
void myprintf(char *fmt, ...){
   va_list ap;
   char string[512];
   va_start(ap, fmt);
   vsprintf(string, fmt, ap);
   send_str(string);
   va_end(ap);
}
 
 
void RX_INT_IRQHandler(void){
    HAL_GPIO_EXTI_IRQHandler(RX_GPIO_PIN);         /* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */
    __HAL_GPIO_EXTI_CLEAR_IT(RX_GPIO_PIN);         /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}
 
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
   if(GPIO_Pin == RX_GPIO_PIN){
      if(uartStartFlag == 0){
         uartStartFlag = 1;
         btim_timx_int_init(52 - 1, 72 - 1, BTIM_TIM6_INT);    //52us接收数据
      }
   }    
}
 
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值