发个软件UART代码吧
看到有位仁兄发帖用软件延时来模拟UART,实在惨不忍睹,看有人感兴趣,贴上8年前项目用的软件UART吧。#include <iom32v.h>
#include <macros.h>
#include <string.h>
#include "Vga_drv.h"
#include "uart.h"
#include "Key_Scan.h"
#include "bmp.h"
#include "Isr.h"
#include "ps2_driver.h"
#include "eeprom.h"
#include "global.h"
#include "CRC.h"
#define uint32 unsigned long
#define SC_RXD 6
#define SC_RXD_SET_INPUT() {DDRD&=(~BIT(SC_RXD));}
#define SC_RXD_GET() ((PIND&(BIT(SC_RXD))))
#define SC_TXD 7
#define SC_TXD_0() {PORTD&=(~BIT(SC_TXD));}
#define SC_TXD_1() {PORTD|=BIT(SC_TXD);}
#define SC_TXD_SET_OUTPUT() {DDRD|=BIT(SC_TXD);}
uint8 sc1_rxd_scan_ct=0;
uint8 sc1_rxd_scan_next_time=0;
uint8 sc1_rxd_scan_step=0;
uint8 sc1_rxd_dat;
uint8 sc1_rxd_ready=0;
uint8 sc1_rxd_tmpdat;
uint8 sc_txd_ready=0;//模拟串口变量
uint8 sc_txd_bit_pt=0;
uint8 sc_txd_data=0;
uint8 sc_txd_ct=0;
void SC_Recv_Pro(uint8 dat){
}
void SC_RxdSrv(void){
if(sc1_rxd_scan_step==0){
if(!SC_RXD_GET()){
sc1_rxd_scan_step=1;
return;
}
}
if(sc1_rxd_scan_step==1){
if(!SC_RXD_GET()){
sc1_rxd_scan_step=2; //rxd start bit ok,goto next step
sc1_rxd_scan_ct=0;
sc1_rxd_scan_next_time=3;
sc1_rxd_tmpdat=0;
return;
}
else{
sc1_rxd_scan_step=0; //rxd start bit is avalid
return;
}
}
if(sc1_rxd_scan_step>=2){
sc1_rxd_scan_ct++;
if(sc1_rxd_scan_ct<sc1_rxd_scan_next_time) return;
sc1_rxd_scan_ct=0;
if(sc1_rxd_scan_step<10){
sc1_rxd_tmpdat>>=1;
if(SC_RXD_GET()){
sc1_rxd_tmpdat|=0x80;
}
sc1_rxd_scan_step++;
return;
}
if(sc1_rxd_scan_step==10){
if(SC_RXD_GET()){
sc1_rxd_dat=sc1_rxd_tmpdat;
sc1_rxd_ready=1;
//Receive a byte OK
#if 0
sc_txd_data=sc1_rxd_dat;
sc_txd_ready=1;
#endif
#if 0
Test_Uart1(sc1_rxd_dat);
#endif
SC_Recv_Pro(sc1_rxd_dat);
}
sc1_rxd_scan_step=0;
return;
}
}
}
void SC_TxdSrv(void){
sc_txd_ct++;
if(sc_txd_ct<3) return;
sc_txd_ct=0;
if(sc_txd_ready){ //Data Ready
if(sc_txd_bit_pt<10){
if(sc_txd_bit_pt==0){
SC_TXD_0(); //Start BIT
}
else{
if(sc_txd_bit_pt>=9){
SC_TXD_1(); //End BIT
}
else{ //数据位
if((sc_txd_data>>(sc_txd_bit_pt-1))&0x01){
SC_TXD_1();
}
else{
SC_TXD_0();
}
}
}
}
if(sc_txd_bit_pt>10){
sc_txd_bit_pt=0; //发送完后延时两个时钟,复位各标志
sc_txd_ready=0;
}
else{
sc_txd_bit_pt++; //位指针自加
}
}
}
void SC_send_char(uint8 b){
sc_txd_data=b;
sc_txd_ready=1;
while(sc_txd_ready==1);
}
void SC_send_str(uint8 *str){
while((*str)!=0){
SC_send_char(*str);
str++;
}
}
void init_SimComIO(void){
SC_TXD_SET_OUTPUT();
SC_TXD_1();
SC_RXD_SET_INPUT();
}
#pragma interrupt_handler ISR_T1:8
void ISR_T1(void)
{
//compare occured TCNT1=OCR1A
SC_RxdSrv();
SC_TxdSrv();
}
把变量封装在结构体,在用结构体指针来访问,还可以无限扩展UART数量呢 曾经那这个代码(经过结构体封装的)跑过10个串口,在AVR上,当然波特率不高,4800bps。 只 用了一个定时器? 多谢分享了,先收藏 SC_Recv_Pro函数是空的。楼主介绍下是如何设置定时,如何在主程序中调用的。打包个源工程,或者最简例程也好。期待中。 留个记号,还真有用得到的时候。谢谢! bangbangji 发表于 2016-2-17 19:18
只 用了一个定时器?
对的,只用一个定时器,而且不影响定时器干其它事,波特率由定时中断的频率来决定, upli 发表于 2016-2-17 20:23
SC_Recv_Pro函数是空的。楼主介绍下是如何设置定时,如何在主程序中调用的。打包个源工程,或者最简例程也 ...
就是接收完一个字节后中断调用的回调函数啊,想干嘛就干嘛咯 upli 发表于 2016-2-17 20:23
SC_Recv_Pro函数是空的。楼主介绍下是如何设置定时,如何在主程序中调用的。打包个源工程,或者最简例程也 ...
您就当它是串口的接收中断就得了 感谢,很好的代码,这模拟UART 非常实用的程序。 谢谢,正好在考虑要不要用软件模拟串口。 顺便说明一下,为了提高接收的准确率和纠错性能,采用3倍采样率,定时中断的频率是波特率的3倍。例如:需要波特率9600bps的话,需要1/(9600*3)=34.72us的定时中断。 稳定性怎么样啊,曾经用过软件模拟,用的定时器中断模拟串口,飞思卡尔S12开两路CAN加两路LIN,在CAN100%负载率的时候模拟串口就容易出现错位,最后用输入捕捉和输出比较才解决的 mark....模拟串口。。。。。 mark下,休息时看看~~多谢楼主 q457344370 发表于 2016-2-18 07:49
稳定性怎么样啊,曾经用过软件模拟,用的定时器中断模拟串口,飞思卡尔S12开两路CAN加两路LIN,在CAN100%负 ...
稳定性非常的好,因为是3倍采样率,只要保证定时中断不被长时间打断即可保证通讯的稳定性 关键就是被频繁打断,单片机主频16M,两路CAN100%负载率的时候每ms中断近10次,两路硬件串口每ms也要中断2次,另外还有个1ms的定时器中断,再加上一个捕捉中断 q457344370 发表于 2016-2-18 12:45
关键就是被频繁打断,单片机主频16M,两路CAN100%负载率的时候每ms中断近10次,两路硬件串口每ms也要中断2 ...
AVR没有硬件中断优先级,会比较呛 用这种对齐格/换行格式(好像有个专门的称呼,忘了。。。。)的都是异端!!!
非常凑巧的是,我也是这样对齐/换行。。。
{:victory:} 优先级高的中断程序打断定时器中断,通讯是不是不能保证呀 amigenius 发表于 2016-2-18 14:30
AVR没有硬件中断优先级,会比较呛
刚看了下程序,只是为了稳定读起始位何必开3倍波特率呢,软件开销更大了 q457344370 发表于 2016-2-18 21:35
刚看了下程序,只是为了稳定读起始位何必开3倍波特率呢,软件开销更大了 ...
因为UART是异步,找到起始位的中点是关键,保证有足够的时序容差和稳定性,故开3倍采样率。其实每次进中断执行的就几行程序,CPU开销很小的。 amigenius 发表于 2016-2-19 09:45
因为UART是异步,找到起始位的中点是关键,保证有足够的时序容差和稳定性,故开3倍采样率。其实每次进中 ...
这种方法做中断频繁的时候肯定没法用的,我刚开始用的是定时器中断加外部中断,进外部中断开1/2波特率定时器,关外部中断,定时中断到后检测是不是起始位,是就开1倍定时器中断接收数据。接收完一帧关定时器开外部中断,然后处理数据。所以在不收发数据时是不占用资源的。但是结果依然很容易被别的中断干扰,最后把方案换成用输入边沿捕捉接收,用输出比较发送。中断压力测试明显比定时器中断效果好 留着备用,谢谢LZ。 q457344370 发表于 2016-2-19 12:37
这种方法做中断频繁的时候肯定没法用的,我刚开始用的是定时器中断加外部中断,进外部中断开1/2波特率定 ...
这位大侠的方法也很好,请问能共享个参考代码吗。 q457344370 发表于 2016-2-19 12:37
这种方法做中断频繁的时候肯定没法用的,我刚开始用的是定时器中断加外部中断,进外部中断开1/2波特率定 ...
没必要搞这么麻烦,软件串口占用中断的时间非常小,每次执行就几行程序,可以放在最高优先级中断。而且,我们设计程序结构时,占用时间长的程序段,是不会放在中断中处理的,这样才能保证中断的实时性,因为中断本来的意义就是处理需要实时响应的小段内容。如果您中断占用太多处理时间,那么您需要考虑您的程序结构了。 amigenius 发表于 2016-2-19 15:14
没必要搞这么麻烦,软件串口占用中断的时间非常小,每次执行就几行程序,可以放在最高优先级中断。而且, ...
在18楼已经说了,程序上万行,ROM使用近100K,用16M再加上频繁中断感觉任何构架都白搭 mark一下,以后细读 kation122 发表于 2016-2-19 14:20
这位大侠的方法也很好,请问能共享个参考代码吗。
程序在公司电脑上,没法外传,定时器中断的方法我已经说的很清楚了,捕捉中断原理就是先捕捉起始位(>=0.5Bit)然后把每次捕捉的时间四舍五入换算成nbit的0或1移进buff里,当检测移进去的位数大于等于接收1个字节需要的长度时说明接收完毕。大概就是这样,再把出错考虑进去就可以了{:lol:} mark一下 q457344370 发表于 2016-2-19 18:25
在18楼已经说了,程序上万行,ROM使用近100K,用16M再加上频繁中断感觉任何构架都白搭 ...
那么用AVR就是一个错误。上万行的程序不算什么,我们程序经常是10多万行,4,5个UART,还要100K以上波特率,外加视频处理和图形界面,在72M的STM32跑的非常欢快,中断占用的时间非常小,当然,我们有硬件DMA。程序架构和优化非常的重要,别小看这个东西,我们以前有位女程序员,做个简单的空调程序,居然会吧中断用到阻塞,厉害吧。 多谢分享,正想着做这个 amigenius 发表于 2016-2-20 09:16
那么用AVR就是一个错误。上万行的程序不算什么,我们程序经常是10多万行,4,5个UART,还要100K以上波特 ...
用的是飞思卡尔,再说芯片选型不是我能管的,ST达到汽车级的只有SPC 我已经用楼主的代码在一个没有UART的51单片机上移植成功了,9600bps,运行流畅。特回头来答谢楼主! upli 发表于 2016-3-2 19:34
我已经用楼主的代码在一个没有UART的51单片机上移植成功了,9600bps,运行流畅。特回头来答谢楼主! ...
恭喜!不客气。不过话说什么型号的51连个串口都没有这么寒酸 谢谢楼主,正需要这方面的资料呢 amigenius 发表于 2016-3-2 20:25
恭喜!不客气。不过话说什么型号的51连个串口都没有这么寒酸
自己公司开发的一个专用型MCU,纯粹为了测试验证的方便 谢谢楼主,之前模拟过接收,但是没有开启三倍采样~ 好东西感谢楼主分享有机会试试 学习一下,看看程序的完善程度是否可以借鉴一下 小溪 发表于 2016-9-23 09:02
学习一下,看看程序的完善程度是否可以借鉴一下
稳定度可媲美硬件串口,而且可以无限扩展,只要CPU速度够,来50个串口也无问题。 要扩展多个的话,最好用结构体封装一下变量,访问时传递指针即可,程序会非常简洁。 本帖最后由 shjw 于 2016-10-24 16:30 编辑
三倍采样率,程序计时要乘以3?
怎么用结构体封装变量扩展串口 shjw 发表于 2016-10-24 16:24
三倍采样率,程序计时要乘以3?
怎么用结构体封装变量扩展串口
typedef sim_uart_def{
uint8 sc1_rxd_scan_ct;
uint8 sc1_rxd_scan_next_time;
uint8 sc1_rxd_scan_step;
uint8 sc1_rxd_dat;
uint8 sc1_rxd_ready;
uint8 sc1_rxd_tmpdat;
uint8 sc_txd_ready;//模拟串口变量
uint8 sc_txd_bit_pt;
uint8 sc_txd_data;
uint8 sc_txd_ct;
}TYPE_SIM_UART_DEF;
TYPE_SIM_UART_DEF su1,su2,su3,su4,su5,su6;
void SC_RxdSrv(TYPE_SIM_UART_DEF *pSU);
void SC_TxdSrv(TYPE_SIM_UART_DEF *pSU);
软UART,收藏备用。谢谢! 收藏了,用在其他协议应该也可以 mcu内核够快才行!{:sweat:}{:shocked:} 这个程序起始位搜索都没有使用中断捕获下降沿,直接用定时器中断来采样值,这样真的可以吗,两次采样低就是正确的起始位???如何理解 本帖最后由 amigenius 于 2016-10-25 15:03 编辑
myxiaonia 发表于 2016-10-25 10:12
这个程序起始位搜索都没有使用中断捕获下降沿,直接用定时器中断来采样值,这样真的可以吗,两次采样低就是 ...
三倍采样,检测到第一个下降沿,然后以第二个中断时间点作为采样点,以保证异步串口的时序容差,所以稳定性极佳,而且可以无限扩展。 外中断+定时器中断,优点是CPU占用相对少一点,但无办法用同样的硬件开销来扩展多个串口。而三倍采样,则无这个限制,以多一点点的CPU开销为代价(现在的CPU太快了,扩展十来个软件串口,毫无压力),换来无限的串口数。 wkman 发表于 2016-10-25 08:40
mcu内核够快才行!
51跑得很欢快,AVR无压力,Cortex更加毫无压力。 amigenius 发表于 2016-10-25 15:01
三倍采样,检测到第一个下降沿,然后以第二个中断时间点作为采样点,以保证异步串口的时序容差,所以稳定 ...
没看到啊,我看接收程序第一阶段先检测低电平,第二阶段如果还是低电平,认为是起始位,之后采样值就当作串口值,感觉好奇怪的样子
因为如果按照这之后采样值当串口位值看,这样采样率应该就是串口波特率,并没有3倍采样
从2次检测起始位的方法看,应该是认为完整的1位低电平时间就是起始位了,但是串口随时可以发送数据,不可能刚好就对齐到起始位的两个边界上,就算刚好对齐到完整1位的两个边界,也未必是起始位,也可能是刚好串口发送到某位是0值 myxiaonia 发表于 2016-10-25 15:14
没看到啊,我看接收程序第一阶段先检测低电平,第二阶段如果还是低电平,认为是起始位,之后采样值就当作 ...
三倍采样的目的是准确找到起始位0的中点,然后以这个作为基准时间来采样往后的8位,以确保时序容差,避开各个位的交界点,使之准确接收数据而不产生误码。 q457344370 发表于 2016-2-19 12:37
这种方法做中断频繁的时候肯定没法用的,我刚开始用的是定时器中断加外部中断,进外部中断开1/2波特率定 ...
捕捉肯定好些 不过有些没捕捉功能就是。 amigenius 发表于 2016-10-25 15:51
三倍采样的目的是准确找到起始位0的中点,然后以这个作为基准时间来采样往后的8位,以确保时序容差,避开 ...
终于看明白了,第一步搜索起始位中心点,这一步还是非常巧妙的
不过第二次确定的采样点如果非常接近下一个位起始点的话,相当于你的3倍采样无效了 myxiaonia 发表于 2016-10-25 22:13
终于看明白了,第一步搜索起始位中心点,这一步还是非常巧妙的
不过第二次确定的采样点如果非常接近下一 ...
大哥您是一位非常严谨的人,敬佩!其实4倍采样可完全确保,不过CPU开销变大了,模拟的串口数不很多时可采用。实际使用时发现3倍采样,已经非常稳定,所以没做4倍采样。为了规避3倍采样踩到边界的风险,我以前做多串口时还加入了一个技巧,就是如果在多串口的时候,在扫描完各个串口IO之后,再回扫一次,是否有1到0的跳变,如果有,则置该串口的sc1_rxd_scan_step=1,这样采样点又回到位的中点了。 标记以下 标记一下,最近在用模拟串口 模拟串口 收藏 顶一下,用起来 WM_CH 发表于 2017-1-11 11:21
顶一下,用起来
这个代码其实很好用的,反正我自己用在很多项目上,无任何问题 学习学习 学习了,我一般就模拟一下IIC,SPI等 好好研究一下!感谢楼主分享。{:smile:} 好帖不会沉! 感谢分享 谢谢楼主分享,非常好用的代码 这个好!!谢谢。用在产品中,非常稳定
又学习到了一个实用知识 amigenius 发表于 2016-2-20 09:16
那么用AVR就是一个错误。上万行的程序不算什么,我们程序经常是10多万行,4,5个UART,还要100K以上波特 ...
感觉你也是汽车电子行业的?用哪个STM32哪个芯片能做视频处理?能处理 RGB CVBS? 本帖最后由 isakura 于 2018-1-21 23:16 编辑
看到这个帖子想到了以前的一个想法
原来以前一直在找那种8脚甚至是更小的单片机,只需要具有串口功能的,一直没找到
发现很多小封装的都有SPI功能,可是没有串口功能,很奇怪
现在想起来可以模拟(原来觉得模拟的不稳定,就没多考虑),
最近也做了一个类似的定时器模拟通讯,不过用的是STM32F105,这个定时器牛逼,都做到了几us的通讯,而且挺稳定的
不过老款的小封装单片机,像飞思卡尔那些,容量一两K的,估计主频不高,不知道波特率能到多少 谢谢楼主无私分享,最近刚好要用到 软UART,收藏备用。谢谢! 如果波特率用115200,能否稳定模拟3个UART?估计能模拟2个也很有难度 无限扩展,NB 标记一下,备用 用外部中端+定时器,或捕捉+定时输出,对cpu的占用都相对较小。但是模拟最大的缺点是如果cpu halt掉比如擦写flash就挂了 标记!仔细学习一下 nb,学习了,多谢,正好需要 收藏了多谢啊 收藏,mark下...... amigenius 发表于 2016-2-17 23:19
顺便说明一下,为了提高接收的准确率和纠错性能,采用3倍采样率,定时中断的频率是波特率的3倍。例如:需要 ...
我是假装来学习的。。定时器是最宝贵的资源。。
页:
[1]