单片机通用软件模拟串口 三倍采样 同步收发
本人前段时间参考小小调度器写的单片机通用软件模拟串口使用方法:
1、定义接收和发送的IO口
//C51
sbit IO_RX P2^1;
sbit IO_TX P2^2;
//PIC
#define IO_RX RC3 //定义接收的IO口
#define IO_TX RC4 //定义发送的IO口
2、在main主程序while(1)循环之前加上串口初始化代码
UART_INIT();
3、设置一个定时器的中断时间为34.6uS,在定时器中断里加上下边的代码
UART_TX();
UART_RX();
4、在main主程序while(1)循环中判断,UART_RX_STA标志是否是RX_DATAOK,如果是则收到新数据,可以处理收到的数据或将数据转存。
if(UART_RX_STA==RX_DATAOK)
{
unsigned char dat;
dat=rx_buff; //rx_buff里的数据为串口收到的数据
UART_RREST(); //准备接收下一字节数据
}
5、在发送数据前判断UART_TX_STA标志是否是TX_FREE空闲状态,是空闲状态
if(UART_TX_STA==TX_FREE)
{
unsigned char dat;
dat=0xF2;
UART_TDATA(dat); //将dat从串口发送出去
}
51和PIC通用。不错。 软件模拟串口,以前用过。 我模拟 UART接收,都是用下沿中断来触发的,程序比LZ的复杂了一点,好在没有数据过来的时候,不会产生频繁的定时中断。
LZ有没有做过长时间接收测试,效果如何 retention 发表于 2018-11-7 13:41
我模拟 UART接收,都是用下沿中断来触发的,程序比LZ的复杂了一点,好在没有数据过来的时候,不会产生频繁 ...
已经用在产品上了的,一直稳定正常 不错,学习一下! retention 发表于 2018-11-7 13:41
我模拟 UART接收,都是用下沿中断来触发的,程序比LZ的复杂了一点,好在没有数据过来的时候,不会产生频繁 ...
有一点点干扰都会导致接收错误。
我用的定时,以2倍的速度采样,通信很稳定。 laujc 发表于 2018-11-7 14:08
有一点点干扰都会导致接收错误。
我用的定时,以2倍的速度采样,通信很稳定。 ...
你的意思是说用下沿中断触发来接收,容易被干扰吗? retention 发表于 2018-11-7 14:24
你的意思是说用下沿中断触发来接收,容易被干扰吗?
是的,
不过感觉你不是这么简单处理的吧? 有排版强迫症的患者给他重新排了版。
//串口接收
#define UART_RX()
{
switch(f_rx.STA)
{
case 0:
{
if (IO_RX == 0)
{
f_rx.STA=1;
f_rx.NUM=8;
rx_buff=0;
f_rx.CNT=0;
}
}break;
case 1:
{
f_rx.CNT++;
if (f_rx.CNT == 3)
{
if(IO_RX)
{
rx_buff|=0x80;
}else
{
rx_buff&=0x7F;
}
f_rx.CNT=0;
f_rx.NUM--;
if (f_rx.NUM > 0)
{
rx_buff>>=1;
}else
{
f_rx.STA=2;
}
}
}break;
case 2:
{
f_rx.CNT++;
if (f_rx.CNT == 3)
{
f_rx.STA = 3;
}
}break;
default:
break;
}
}
#define UART_TX()
{
switch(f_tx.STA)
{
case 0:
break;
case 1:
{
f_tx.CNT++;
if (f_tx.CNT == 1)
{
IO_TX=0;
}else if (f_tx.CNT == 3)
{
f_tx.STA=2;
f_tx.CNT=0;
f_tx.NUM=8;
}
}break;
case 2:
{
f_tx.CNT++;
if (f_tx.CNT == 1)
{
IO_TX=tx_buff&0X01;
tx_buff=(tx_buff>>1);
f_tx.NUM--;
}else if (f_tx.CNT == 3)
{
f_tx.CNT=0;
if(f_tx.NUM==0)
{
f_tx.STA=3;
}
}
}break;
case 3:
{
f_tx.CNT++;
if (f_tx.CNT ==1 )
{
IO_TX=1;
}else if (f_tx.CNT == 3)
{
f_tx.STA=0;
}
}break;
default:
break;
}
} 我的流程是:MCU使能RX脚的下沿中断,接收到中断后启动1/9600(9600为波特率)的定时中断,
然后在定时器中断里面接收 8个数据位和一个停止位,最后判断停止位是否为高电平,如果为高电平则认为是合法的数据。。。
经你这么提醒,我采样每个bit的时候,只采样一次的,这里可能会产生搞干扰能力不足的问题,但是碍于定时器中断得太频繁会干扰主程序运行,
所以就没以数倍于波特率的速度来采样 留个记号,有空学习一下 好像有问题,粤原点 试了下,定时是对的
实际上位机COM设置9600 8 1
下面发送0X14,上位机收到0X2C rootxie 发表于 2019-1-15 16:58
好像有问题,粤原点 试了下,定时是对的
实际上位机COM设置9600 8 1
下面发送0X14,上位机收到0X2C ...
我一直在粤原点上用,很正常的,你设置的定时器中断时间是多少? 我的经验,4倍采样误码率比较低。 说实在话,我从来没用过这种,不过下次有机会可以试一试。。。
我也是四倍采样率,可以消除边沿误差与偶发干扰,再低就信不过了。 学习了,有机会验证一下。 xyb852 发表于 2019-1-15 20:00
我一直在粤原点上用,很正常的,你设置的定时器中断时间是多少?
83系列,上面的问题是用T0定时,中断34.5us,16M 4T。刚才换成T2,还是将0x14收成34,用的是虚拟串口 rootxie 发表于 2019-1-16 00:40
83系列,上面的问题是用T0定时,中断34.5us,16M 4T。刚才换成T2,还是将0x14收成34,用的是虚拟串口 ...
只要T0的中断时间是对的,T0中断里UART_RX();UART_TX();放的位置对的就不会误码。
中断代码void interrupt ISR(void) //中断处理程序
{
if (T0IE && T0IF) //TMRE0中断 约34.6us
{
TMR0 = c_TMR0; //T0置初值 中断时间约34.6us
UART_TX(); //串口发送服务
UART_RX(); //串口接收服务
T0IF = 0; //清T0中断标志
}
}
定时器0初始化//16M 2T
#define c_TMR0 128 //定时器0初值 128=34.6us
void Timer0_Init(void)
{
//option
T0CS = 0; //0=定时器 1=计数器
PSA = 0;//0=预分频给定时器 1=预分频给看门狗
PS2 = 0;//预分频
PS1 = 0;
PS0 = 0;
TMR0 = c_TMR0; //赋初值
T0IF = 0;
T0IE = 1;
GIE = 1;
} zhiwei 发表于 2019-1-15 20:35
我的经验,4倍采样误码率比较低。
主要是为了平衡CPU资源占用和误码率,所以选择三倍采样率了 跟指令执行周期有关,T2定时器,换成2T指令执行周期就没问题了,我估计1T的应该效果更好 本帖最后由 rootxie 于 2019-1-16 09:06 编辑
xyb852 发表于 2019-1-16 06:50
只要T0的中断时间是对的,T0中断里UART_RX();UART_TX();放的位置对的就不会误码。
中断代码
定时器0初始 ...
这个初始值好像很奇怪,128
TIMER0 的定时中断为 1/16M * 2T * 2(分频) * (256-128) = 32us,实际并不是34.6us
难道是为了平衡其他指令执行消耗的时间留下的buffer?
实际上用T2更好一些,无需赋初始值,但是用4T指令还是不行
下午拿热风枪对着吹一下 看看会不会出现误码 软件模拟串口做好了,很稳定。 有个疑问,好像ADC过采不是每次都进去累加的么!那么
f_rx.CNT++;
if (f_rx.CNT == 3)
{
f_rx.STA = 3;
}
像这段代码,只有累加三次后才进去判断,能算是过采么?请教! 粤原点的ide是不是会破坏原来的mplab sup888 发表于 2019-1-16 09:43
有个疑问,好像ADC过采不是每次都进去累加的么!那么
f_rx.CNT++;
...
这里是等待停止位时间结束 rootxie 发表于 2019-1-16 09:01
这个初始值好像很奇怪,128
TIMER0 的定时中断为 1/16M * 2T * 2(分频) * (256-128) = 32us,实际并 ...
还有中断后现场保护代码执行占用的时间 2019-03-22 更新1.11版:
取消结构体的定义,解决部分冷门单片机编译器的兼容问题。
兼容的代价是,RAM的占用从1.01版的4字节增加到8字节.
STM32 本身有好几个串口,本身的串口和 模拟的。10个应该没问题 woshigeshuai 发表于 2019-3-22 08:37
STM32 本身有好几个串口,本身的串口和 模拟的。10个应该没问题
以后有时间争取再搞一个支持扩展多个模拟串口的版本{:smile:} 3倍率应该是比较合适的,误差范围也是可以的,我自己在慢速通信时喜欢用自己做的模拟UART,还能用单IO口做半双工通信,用在产品上一直很稳定 mark,备用...... 大佬们好,有不有在粤原点上使用的完整例程啊,{:hug:}我鼓捣不好嗯。 模拟串口,不错 throg 发表于 2019-12-26 20:32
大佬们好,有不有在粤原点上使用的完整例程啊,我鼓捣不好嗯。
明天争取搞一个完整例程发上来{:smile:} xyb852 发表于 2019-12-27 21:25
明天争取搞一个完整例程发上来
大佬,我照着整了一个,一直在无限的发发发发,帮看下那里不对哟。 throg 发表于 2020-1-1 18:35
大佬,我照着整了一个,一直在无限的发发发发,帮看下那里不对哟。
又整了一个,这个可以发送一次,但是不会接收{:dizzy:} throg 发表于 2020-1-1 22:28
又整了一个,这个可以发送一次,但是不会接收
最近特别忙
我发一个之前测试的MS84F的调度器+软件串口上来给你参考,软件串口请替换成本贴的稳定版.
感谢,我拿来用了 2020-05-27更新1.20版:
优化结构体定义,修改了接收发送缓存的定义
优化串口接收部分,接收更稳定正确
异步收发 接收和发送同时进行互不影响
三倍采样率 如9600波特率,定时器中断时间为34.6uS
使用方法:
1、定义接收和发送的IO口
//C51
sbit IO_RX P2^1;
sbit IO_TX P2^2;
//PIC
#define IO_RX RC3 //定义接收的IO口
#define IO_TX RC4 //定义发送的IO口
2、在main主程序while(1)循环之前加上串口初始化代码
UART_INIT();
3、设置一个定时器的中断时间为34.6uS,在定时器中断里加上下边的代码
UART_TX();
UART_RX();
4、在main主程序while(1)循环中判断,UART_RX_STA标志是否是RX_DATAOK,如果是则收到新数据,可以处理收到的数据或将数据转存。
if(UART_RX_STA==RX_DATAOK)
{
unsigned char dat;
dat=UART_RX_BUFF; //UART_RX_BUFF里的数据为串口收到的数据
UART_RREST(); //准备接收下一字节数据
}
5、在发送数据前判断UART_TX_STA标志是否是TX_FREE空闲状态,是空闲状态
if(UART_TX_STA==TX_FREE)
{
unsigned char dat;
dat=0xF2;
UART_TDATA(dat); //将dat从串口发送出去
}
这个挺好,下载测试下 没看出来哪里三倍采样了,纯等待时间,并没有多次采样判断干扰,可能我水平低,有谁看出来的提点一下 学习一下,准备测试 xyb852 发表于 2020-5-27 14:34
2020-05-27更新1.20版:
优化结构体定义,修改了接收发送缓存的定义
优化串口接收部分,接收更 ...
请教个问题,发送字符串怎么比较好,有没有不一直占用等待单字节的代码,以供参考。谢谢
留个记号,有空学习一下 shower.xu 发表于 2020-6-4 11:35
请教个问题,发送字符串怎么比较好,有没有不一直占用等待单字节的代码,以供参考。谢谢 ...
可以用论坛里的小小调度器,可以找找楼上有例子可以参考 不错,学习一下! 早期用PIC和台湾MCU很多都是用模拟串口,非常稳定 Bicycle 发表于 2019-1-16 11:03
粤原点的ide是不是会破坏原来的mplab
新版的辉芒和粤原点的IDE,C编译器会互相冲突,也和MPLAB的PICC编译器冲突。基本是只能用一个。 yelong98 发表于 2020-5-29 09:33
没看出来哪里三倍采样了,纯等待时间,并没有多次采样判断干扰,可能我水平低,有谁看出来的提点一下 ...
可能是我理解的不一样,这里的意思是把基准时间分成三等份,采样点的位置在三份的交汇点上。 正在研究模拟串口,多谢分享! 在STM32上成功移植。 好好研究一下{:smile:} 这个中断cpu太频繁了吧 本帖最后由 qwe2231695 于 2021-5-16 02:16 编辑
要进行115200通信 , 一秒钟中断345600次, 每次间隔0.0029ms , 约3us.中断占用必须短,用汇编. 楼主使用的这个方法很好,最近可能用得上
感谢分享 有机会验证一下。
页:
[1]