软件模拟UART收发(以瑞萨RL78为示例演示)
文章目录
前言
最近学习使用瑞萨芯片,遇到了一个问题,使用的串口不够,所以才在网上找资料,使用两个IO口实现模拟串口的收发功能。
我选用的是:
P42—>RX 配置为输入
P43—>TX 配置为输出,默认情况位高电平
配置如下图所示:
一、串口的定义,波形
1.1 串口的定义
串口的基础定义我在此不做过多赘述,如果大家不懂的话我建议大家去看一文读懂串口,这个博主讲的很详细具体。
1.2 串口的波形
对于正逻辑的TTL电平,线路总是高电平(引用串口波形分析)。我们知道串口报文包含起始位、数据位、奇偶校验位、停止位:
起始位:低电平
数据位:低位优先,高电平代表1,低电平代表0
奇偶校验位:奇偶检验等N、O、E、M、S 五种串口检验位类型
停止位:高电平
下图是无校验位的串口波形图。
1.3 波特率及一个bit延时时间
我采用的是9600波特率传输,表示1秒内传输9600个bit位,所以每一个bit位的延时时间为1/9600 = 104(us)
二、串口发送
IO口需要功能:能够正常拉高拉低电平就可以,不要使用开漏输出。
2.1 无校验位
#define BuadRate_9600 104 //104us
/***************************************************************
* @brief 发送一个字节
* @param 要发送的字节
* @return void
**************************************************************/
void TX_Data(uint8_t data)
{
uint8_t i = 0;
TX_L;
delay_us(BuadRate_9600); //延时一个bit位时间
for(i = 0;i < 8;i++)
{
if(data & 0x01)
TX_H;
else
TX_L;
delay_us(BuadRate_9600);
data = data>>1;
}
TX_H; //停止位1个比特
delay_us(BuadRate_9600);
}
/***************************************************************
* @brief 计算字符串长度
* @param 传入字符串首地址指针
* @return 字符串长度
**************************************************************/
uint16_t countCharacters(uint8_t *str)
{
uint16_t count = 0;
while (*str != '\0')
{
count++;
str++; //指针后移
}
return count;
}
/***************************************************************
* @brief 串口发送函数
* @param 要发送的字符串首地址
* @return void
**************************************************************/
void USART_Send(uint8_t *buf)
{
uint16_t len = countCharacters(buf);
uint8_t i = 0;
for(i = 0;i < len;i++)
TX_Data(buf[i]);
}
2.2 带奇校验位
/***************************************************************
* @brief 带奇校验的字节发送
* @param 要发送的数据
* @return void
**************************************************************/
void TX_Data_Odd(uint8_t data)
{
uint8_t i = 0,flag_O = 0;
TX_L;
delay_us(BuadRate_9600); //起始位
for(i = 0;i < 8;i++)
{
if(data & 0x01)
{
TX_H;
flag_O++;
}
else
TX_L;
delay_us(BuadRate_9600);
data = data>>1;
}
if(flag_O%2 == 0) //偶数个1 高电平校验位
TX_H;
else
TX_L;
delay_us(BuadRate_9600);
TX_H; //结束位1
delay_us(BuadRate_9600);
}
/***************************************************************
* @brief 串口发送函数带奇校验
* @param 要发送的字符串
* @return void
**************************************************************/
void USART_Send_Odd(uint8_t *buf)
{
uint16_t len = countCharacters(buf);
uint8_t i = 0;
for(i = 0;i < len;i++)
TX_Data_Odd(buf[i]);
}
其他的校验方式不做演示。
三、 串口接收
IO口需要带定时器功能,把定时器配置为104us进入中断来检测电平高低。使用间隔定时器(任意哪个通道的间隔定时器都可以),配置如下图所示:
3.1 头文件
#ifndef __UART_H__
#define __UART_H__
#include "r_cg_macrodriver.h"
#include "iodefine.h"
typedef enum
{
Uart_Start = 0, //起始状态
Uart_transfer, //传输中
Uart_Stop, //ֹ停止状态
}UartState;
#define BuadRate_9600 104 // 1/9600 = 104us
#define UartLength 100 //最大接收字节数
extern uint8_t UartCnt; //模拟串口接收的个数
extern uint8_t UartBuff[UartLength]; //模拟串口缓冲区
#define TX P4_bit.no3
#define TX_H TX=1
#define TX_L TX=0
#define RX P4_bit.no2
void TX_Data(uint8_t data);
void USART_Send(uint8_t *buf);
void TX_Data_Odd(uint8_t data);
void USART_Send_Odd(uint8_t *buf);
uint16_t countCharacters(uint8_t *str);
uint8_t USART_Recive(uint8_t *buf);
#endif
3.2 无校验位+1停止位的接收
uint8_t UartCnt = 0; //模拟串口缓冲区位置
uint8_t UartBuff[UartLength]; //模拟串口缓冲区
/***************************************************************
* @brief 定时器通道4的中断函数内接收串口数据
* @param void
* @return void
**************************************************************/
static void __near r_tau0_channel4_interrupt(void)
{
/* Start user code. Do not edit comment generated here */
static uint8_t value = 0,bit_cnt;
static UartState state = Uart_Start;
if(RX == 0 && state == Uart_Start) //检测到起始位
{
state = Uart_transfer; //变成传输状态
bit_cnt = 0; //bit位数清0
}
else if(state == Uart_transfer) //传输状态中
{
bit_cnt++;
if(RX)
{
value |= (1<<(bit_cnt-1));
}
else
{
value &= ~(1<<(bit_cnt-1));
}
if(bit_cnt >= 8) //bit位数大于8,来到了停止位
state = Uart_Stop;
}
else if(RX && state == Uart_Stop) //判断停止位情况
{
bit_cnt = 0;
if(UartCnt < UartLength)
{
UartBuff[UartCnt++] = value;
}
else //串口接收个数超过最大个数,覆盖之前的内容
UartCnt = 0;
state = Uart_Start;
}
/* End user code. Do not edit comment generated here */
}
/***************************************************************
* @brief 串口接收函数
* @param 要存入的内存地址
* @return 返回存入了多少个字节数据
**************************************************************/
uint8_t USART_Recive(uint8_t *buf)
{
uint8_t len = 0;
if(UartCnt > 0)
{
len = UartCnt;
memcpy( buf, UartBuff, len);
UartCnt = 0; //位置清0
}
return len;
}
3.3 main函数
/* Start user code for global. Do not edit comment generated here */
uint8_t buff[100];
/* End user code. Do not edit comment generated here */
void main(void)
{
R_MAIN_UserInit();
/* Start user code. Do not edit comment generated here */
R_TAU0_Channel4_Start(); //启动定时器4通道,每104us进入中断监视RX
while (1U)
{
if(USART_Recive( buff)) //接收串口信息存到buff
{
USART_Send(buff); //发送接收到的信息
}
}
/* End user code. Do not edit comment generated here */
}
四、现象演示
总结
本篇文章思路适用于各种芯片IO口的软件模拟串口收发,我是以瑞萨芯片为模版写的。此外大家需要自己解决延时函数,如有需要我以后会分享。