串口RS232的数据协议有两种,我们以PC与8位/32位单片机间比较常用的异步模式为例子讲解.
单片机的TXD引脚先发一个低电平0给对方,然后发送5–8个位作为数据,最后发送1个位奇偶校验和1个位高电平1作为停止位.
以下例子为波特率9600bps,1个开始位,8个数据位,1个停止位 [10位数据,无奇偶校验位:9600-8-N-1]
波特率:每秒钟发送的字节位,如果baud=9600bps,也就是说,每发送1位耗时1000ms/9600bps=104uSec
//:常用波特率对应定时周期
2400bps<–>417uS (416.666us)
4800bps<—>208uS (208.333us)
9600bps<—>104uS (104.166us)
19200bps<–>52uS (52.083us)
115200bps<–>8uS (8.68us)
/**************************************
利用51定时器中断模拟串口信息发送
优点:可以自定义串口引脚,可以实现多个串口
缺点:占用1个定时器,定时器中断周期需要稳定不被打断
格式: 9600-8-N-1
功能:模拟串口Send
*****************************************/
#include <REG51.H>
char g_nSendInx=0;
char g_nBuff=0;
char g_nFlag=0;
#define MY_TXD P2_1 //定义TXD引脚,原生芯片是P3.1
void Timer0Init(void)//104微秒@11.0592MHz
{
//1000ms/9600=104.166us
//使用这种方案,由于精确性问题,长时间传输会偶然出现几个误码,而且需要保证定时器不被其他中断打断
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x02; //设置定时器模式
TL0 = 0xA0; //设置定时初值
TH0 = 0xA0; //设置定时重载值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
}
/******************************
自定义串口发送单字符
******************************/
void MyUart_Send(char ch){
g_nFlag=0;
g_nBuff=ch;
TR0=1; //Start timer
while(g_nFlag==0);
}
/******************************
自定义串口发送字符串
******************************/
void MyUart_String(char* str){
int i=0;
while(*(str+i)){
g_nFlag=0;
g_nBuff=*(str+i);
i++;
TR0=1; //Start Timer
while(g_nFlag==0);
}
}
/*
定时器0中断服务
*/
void Timer0_ISP() interrupt 1{
g_nSendInx++;//发送模拟第几位
if(g_nSendInx==1)
{
MY_TXD=0;//起始位拉低
}else if(g_nSendInx==10) //格式: 9600-8-N-1,完整10Bit数据
{
MY_TXD=1;//停止位拉高
TR0=0; //Stop Timer
g_nSendInx=0;
g_nFlag=1;///发送完成标志位
}else{
MY_TXD=g_nBuff&0x01; //set sbuffer_data
g_nBuff>>=1;
}
}
/**********************************
用Timer0定时中断模拟串口信号
**********************************/
void main (void) {
Timer0Init();///定时器初始化,周期为 1000ms/9600=104us
MyUart_String("===Welcome,My Uart===\r\n ");
MyUart_Send(0x61);
MyUart_Send(0x62);
MyUart_Send(0x63);
while (1);
}
/************************************
* 芯片:比亚迪 BF7612CMXX (51内核)
* 功能: 串口TXD模拟发送,采用Timer1
*************************************/
//以下头文件可在比亚迪半导体官网下载 [CTK_Library]
#include "BF7612CMXX.h"
#include "BF7612CMXX_API_Timer.h"
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned char BYTE;
#define SYSCLK_SEL 0 // 系统时钟选择0-12MHz,1-6MHZ,2-4MHz
#if (SYSCLK_SEL == 0)
#define SYSCLK 12 // SYS-12Mhz
#elif (SYSCLK_SEL == 1)
#define SYSCLK 6 // SYS-6Mhz
#elif (SYSCLK_SEL == 2)
#define SYSCLK 4 // SYS-04Mhz
#else
#endif
#define SYS_CLK_SET(x) \
{ \
SYS_CLK_CFG &= ~(0x03); \
SYS_CLK_CFG |= (0x03 & (x << 0)); \
} //(0--12Mhz,1--06Mhz,2--04Mhz)
char g_nSendInx = 0;
char g_nTxdBuff = 0;
char g_nTxdFlag = 0;
#define MY_TXD PD3 //=====自定义TXD引脚=====
#define T_UART_START TR1 = 1
#define T_UART_STOP TR1 = 0
uchar TH1_Reload = 0;
uchar TL1_Reload = 0;
/******************************
自定义串口发送单字符
******************************/
void MyUart_Send(char ch)
{
g_nTxdFlag = 0;
g_nTxdBuff = ch;
T_UART_START; // Start timer
while (g_nTxdFlag == 0)
;
}
/******************************
自定义串口发送字符串
******************************/
void MyUart_String(char *str)
{
int i = 0;
while (*(str + i))
{
g_nTxdFlag = 0;
g_nTxdBuff = *(str + i);
i++;
T_UART_START; // Start Timer
while (g_nTxdFlag == 0)
;
}
}
void Print_Num(char *str, int num)
{
idata char buf[15] = {0}; // max len=15
int cnt = 0;
int i = 0;
int temp = num;
if (num < 0)
temp = -num;
for (i = 0; i < 15; i++)
{
cnt++;
buf[i] = temp % 10 + '0';
temp /= 10;
if (temp == 0)
break;
}
MyUart_String(str);
if (num < 0)
MyUart_Send('-');
while (cnt >= 1)
{
MyUart_Send(buf[cnt - 1]);
cnt--;
}
MyUart_Send('\n'); // end char
}
void MyTimer1_ISP(void)
{
g_nSendInx++; // 发送模拟第几位
if (g_nSendInx == 1)
{
MY_TXD = 0; // 起始位拉低
}
else if (g_nSendInx == 10) // 格式: 9600-8-N-1,完整10Bit数据
{
MY_TXD = 1; // 停止位拉高
T_UART_STOP; // Stop Timer
g_nSendInx = 0;
g_nTxdFlag = 1; /// 发送完成标志位
}
else
{
MY_TXD = g_nTxdBuff & 0x01; // set sbuffer_data
g_nTxdBuff >>= 1;
}
}
void Timer1_Init(uint Timer1Us)
{
EA = 0; // 关总中断;
T1_IP_SET; // 根据实际应运设置优先级
T1_INT_FLAG_CLR; // 清除Timer1中断标志
T1_CT_MODE(0); // Timer1定时/计数模式选择0为定时模式,1为计数模式
T1_MODE_SET(1); // 设置为16位计算器,tiemr1_clk=(1/12)*sys_clk
TH1 = (0xFFFF - ((uint)((Timer1Us * 1.0) * ((SYSCLK * 1.0) / 12.0)))) >> 8;
TL1 = (0xFFFF - ((uint)((Timer1Us * 1.0) * ((SYSCLK * 1.0) / 12.0))));
TH1_Reload = TH1;
TL1_Reload = TL1;
T1_IE_SET; // 开Timer1中断使能
T1_RUN; // 开启Timer1
EA = 1; // 开总中断
}
// INT3 as timer1 isr fuction
void Timer1_ISR() interrupt 3
{
TH1 = TH1_Reload;
TL1 = TL1_Reload;
T1_INT_FLAG_CLR; // 清除Timer1中断标志
MyTimer1_ISP();
}
void main(void)
{
long j = 0;
int loop_cnt = 0;
SYS_CLK_SET(SYSCLK_SEL); // Select sysclk as 12Mhz
WDT_EN = 0X55; // Close WatchDog
// PD3 as txd test
TRISD = ~(1 << 3); // D_Port IOMode 0:Output; 1:input;
Timer1_Init(104); // 定时器1初始化,104us=>9600bps
MyUart_String("===StartTest===\n");
while (1)
{
for (j = 0; j < 150000; j++)
; //在12Mhz下延时1秒
Print_Num("#CNT=",loop_cnt++);
}
}
/*********************************
利用ESP32定时器中断模拟串口信息发送
优点:可以自定义串口引脚,可以实现多个串口
格式: 19200-8-N-1
功能:模拟串口Send
**********************************/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#define UART_BAUDRATE 19200 // MAX:19200
#define GPIO_SOFT_TXD (20) // 软件模拟串口TXD
esp_timer_handle_t g_hUart_handle;
uint16_t g_nTmInterval = 0;
char g_nSendInx = 0;
char g_nBuff = 0;
char g_nFlag = 0;
//实测通过范围 (1200~~19200) bps
void soft_uart_set_baud(uint32_t baud)
{
g_nTmInterval = 1000000 / baud;
}
void soft_uart_send_byte(uint8_t ch)
{
//(104.1us->9600bps)(52.0us->19200bps)
g_nFlag = 0;
g_nBuff = ch;
esp_timer_start_periodic(g_hUart_handle, g_nTmInterval); // TimeUnit:1us
while (g_nFlag == 0)
{
}
}
void soft_uart_send_str(char *str)
{
//(104.1us->9600bps)(52.0us->19200bps)
int i = 0;
while (1)
{
g_nFlag = 0;
g_nBuff = str[i++];
if (g_nBuff == 0)
{
break;
}
esp_timer_start_periodic(g_hUart_handle, g_nTmInterval); // TimeUnit:1us
while (g_nFlag == 0)
{
}
}
}
static void soft_uart_send_proc(void *arg)
{
g_nSendInx++; // 发送模拟第几位
if (g_nSendInx == 1)
{
gpio_set_level(GPIO_SOFT_TXD, 0); 起始位拉低
}
else if (g_nSendInx == 10) // 格式: Baud-8-N-1,完整10Bit数据
{
gpio_set_level(GPIO_SOFT_TXD, 1); // 停止位拉高
esp_timer_stop(g_hUart_handle); // stop tx send timer
g_nSendInx = 0;
g_nFlag = 1; /// 发送完成标志位
}
else
{
gpio_set_level(GPIO_SOFT_TXD, g_nBuff & 0x01);
g_nBuff >>= 1;
}
}
static void soft_uart_init()
{
gpio_config_t io_conf = {};
// set as output mode
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << GPIO_SOFT_TXD);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 1;
// disable interrupt
io_conf.intr_type = GPIO_INTR_DISABLE;
// configure GPIO with the given settings
gpio_config(&io_conf);
soft_uart_set_baud(UART_BAUDRATE);
const esp_timer_create_args_t periodic_timer_args = {
.callback = &soft_uart_send_proc,
.name = "soft_uart_send_proc"};
esp_err_t err = esp_timer_create(&periodic_timer_args, &g_hUart_handle);
if (err != ESP_OK)
{
// timer create fail
}
else
{
g_nSendInx = 0;
g_nBuff = 0;
g_nFlag = 0;
}
}
void my_printf(char *fmt, ...)
{
char *buf = (char *)malloc(1024);
if (buf == NULL)
{
return;
}
int len = 0;
va_list args;
va_start(args, fmt);
len += vsprintf(buf, fmt, args);
va_end(args);
*(buf + len) = 0;
soft_uart_send_str(buf);
free(buf);
return;
}
void app_main(void)
{
int cnt = 0;
uint8_t ch = 0;
char buffer[50] = {0};
soft_uart_init();
while (1)
{
cnt++;
ch = 'A' + cnt % 24;
soft_uart_send_byte(ch); // 发送单字符
my_printf("#COUNT=%d.Char=%c(%02x)\n",cnt,ch,ch);//发送字符串
vTaskDelay(200);
}
}