xyb852 发表于 2018-11-7 12:54:50

单片机通用软件模拟串口 三倍采样 同步收发

本人前段时间参考小小调度器写的单片机通用软件模拟串口
使用方法:

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从串口发送出去
        }



su33691 发表于 2018-11-7 13:25:59

51和PIC通用。不错。

hzpyl 发表于 2018-11-7 13:31:36

软件模拟串口,以前用过。

retention 发表于 2018-11-7 13:41:52

我模拟 UART接收,都是用下沿中断来触发的,程序比LZ的复杂了一点,好在没有数据过来的时候,不会产生频繁的定时中断。

LZ有没有做过长时间接收测试,效果如何

xyb852 发表于 2018-11-7 13:46:30

retention 发表于 2018-11-7 13:41
我模拟 UART接收,都是用下沿中断来触发的,程序比LZ的复杂了一点,好在没有数据过来的时候,不会产生频繁 ...

已经用在产品上了的,一直稳定正常

xiaomu 发表于 2018-11-7 14:06:46

不错,学习一下!

laujc 发表于 2018-11-7 14:08:29

retention 发表于 2018-11-7 13:41
我模拟 UART接收,都是用下沿中断来触发的,程序比LZ的复杂了一点,好在没有数据过来的时候,不会产生频繁 ...

有一点点干扰都会导致接收错误。

我用的定时,以2倍的速度采样,通信很稳定。

retention 发表于 2018-11-7 14:24:00

laujc 发表于 2018-11-7 14:08
有一点点干扰都会导致接收错误。

我用的定时,以2倍的速度采样,通信很稳定。 ...

你的意思是说用下沿中断触发来接收,容易被干扰吗?

laujc 发表于 2018-11-7 14:30:41

retention 发表于 2018-11-7 14:24
你的意思是说用下沿中断触发来接收,容易被干扰吗?

是的,
不过感觉你不是这么简单处理的吧?

mashan75 发表于 2018-11-7 14:36:25

有排版强迫症的患者给他重新排了版。
//串口接收
#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;
        }
}

retention 发表于 2018-11-7 14:41:03

我的流程是:MCU使能RX脚的下沿中断,接收到中断后启动1/9600(9600为波特率)的定时中断,

然后在定时器中断里面接收 8个数据位和一个停止位,最后判断停止位是否为高电平,如果为高电平则认为是合法的数据。。。



经你这么提醒,我采样每个bit的时候,只采样一次的,这里可能会产生搞干扰能力不足的问题,但是碍于定时器中断得太频繁会干扰主程序运行,

所以就没以数倍于波特率的速度来采样

wsx 发表于 2018-11-15 17:05:28

留个记号,有空学习一下

rootxie 发表于 2019-1-15 16:58:30

好像有问题,粤原点 试了下,定时是对的
实际上位机COM设置9600 8 1
下面发送0X14,上位机收到0X2C

xyb852 发表于 2019-1-15 20:00:06

rootxie 发表于 2019-1-15 16:58
好像有问题,粤原点 试了下,定时是对的
实际上位机COM设置9600 8 1
下面发送0X14,上位机收到0X2C ...

我一直在粤原点上用,很正常的,你设置的定时器中断时间是多少?

zhiwei 发表于 2019-1-15 20:35:03

我的经验,4倍采样误码率比较低。

kinsno 发表于 2019-1-15 20:59:00

说实在话,我从来没用过这种,不过下次有机会可以试一试。。。

chunjiu 发表于 2019-1-15 21:07:13

我也是四倍采样率,可以消除边沿误差与偶发干扰,再低就信不过了。

lylm123 发表于 2019-1-15 23:19:12

学习了,有机会验证一下。

rootxie 发表于 2019-1-16 00:40:38

xyb852 发表于 2019-1-15 20:00
我一直在粤原点上用,很正常的,你设置的定时器中断时间是多少?

83系列,上面的问题是用T0定时,中断34.5us,16M 4T。刚才换成T2,还是将0x14收成34,用的是虚拟串口

xyb852 发表于 2019-1-16 06:50:35

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;
}

xyb852 发表于 2019-1-16 06:54:53

zhiwei 发表于 2019-1-15 20:35
我的经验,4倍采样误码率比较低。

主要是为了平衡CPU资源占用和误码率,所以选择三倍采样率了

rootxie 发表于 2019-1-16 08:09:51

跟指令执行周期有关,T2定时器,换成2T指令执行周期就没问题了,我估计1T的应该效果更好

rootxie 发表于 2019-1-16 09:01:42

本帖最后由 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指令还是不行

下午拿热风枪对着吹一下 看看会不会出现误码

kap 发表于 2019-1-16 09:38:55

软件模拟串口做好了,很稳定。

sup888 发表于 2019-1-16 09:43:49

有个疑问,好像ADC过采不是每次都进去累加的么!那么
                        f_rx.CNT++;
                        if (f_rx.CNT == 3)
                        {
                              f_rx.STA = 3;
                        }
像这段代码,只有累加三次后才进去判断,能算是过采么?请教!

Bicycle 发表于 2019-1-16 11:03:15

粤原点的ide是不是会破坏原来的mplab

xyb852 发表于 2019-1-16 20:57:20

sup888 发表于 2019-1-16 09:43
有个疑问,好像ADC过采不是每次都进去累加的么!那么
                        f_rx.CNT++;
               ...

这里是等待停止位时间结束

xyb852 发表于 2019-1-16 20:59:58

rootxie 发表于 2019-1-16 09:01
这个初始值好像很奇怪,128
TIMER0 的定时中断为 1/16M   * 2T * 2(分频) * (256-128) = 32us,实际并 ...

还有中断后现场保护代码执行占用的时间

xyb852 发表于 2019-3-22 08:23:56

2019-03-22 更新1.11版:
   取消结构体的定义,解决部分冷门单片机编译器的兼容问题。
   兼容的代价是,RAM的占用从1.01版的4字节增加到8字节.

woshigeshuai 发表于 2019-3-22 08:37:59

STM32 本身有好几个串口,本身的串口和 模拟的。10个应该没问题

xyb852 发表于 2019-3-22 11:43:17

woshigeshuai 发表于 2019-3-22 08:37
STM32 本身有好几个串口,本身的串口和 模拟的。10个应该没问题

以后有时间争取再搞一个支持扩展多个模拟串口的版本{:smile:}

waothom 发表于 2019-3-22 15:31:35

3倍率应该是比较合适的,误差范围也是可以的,我自己在慢速通信时喜欢用自己做的模拟UART,还能用单IO口做半双工通信,用在产品上一直很稳定

kingaaa 发表于 2019-4-20 21:01:36

mark,备用......

throg 发表于 2019-12-26 20:32:54

大佬们好,有不有在粤原点上使用的完整例程啊,{:hug:}我鼓捣不好嗯。

bad_fpga 发表于 2019-12-26 23:02:27

模拟串口,不错

xyb852 发表于 2019-12-27 21:25:50

throg 发表于 2019-12-26 20:32
大佬们好,有不有在粤原点上使用的完整例程啊,我鼓捣不好嗯。

明天争取搞一个完整例程发上来{:smile:}

throg 发表于 2020-1-1 18:35:28

xyb852 发表于 2019-12-27 21:25
明天争取搞一个完整例程发上来

大佬,我照着整了一个,一直在无限的发发发发,帮看下那里不对哟。

throg 发表于 2020-1-1 22:28:37

throg 发表于 2020-1-1 18:35
大佬,我照着整了一个,一直在无限的发发发发,帮看下那里不对哟。

又整了一个,这个可以发送一次,但是不会接收{:dizzy:}

xyb852 发表于 2020-1-2 10:32:35

throg 发表于 2020-1-1 22:28
又整了一个,这个可以发送一次,但是不会接收

最近特别忙
我发一个之前测试的MS84F的调度器+软件串口上来给你参考,软件串口请替换成本贴的稳定版.


a22785 发表于 2020-3-10 23:39:16

感谢,我拿来用了

xyb852 发表于 2020-5-27 14:34:25

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从串口发送出去
        }


chen849928055 发表于 2020-5-28 21:22:56

这个挺好,下载测试下

yelong98 发表于 2020-5-29 09:33:32

没看出来哪里三倍采样了,纯等待时间,并没有多次采样判断干扰,可能我水平低,有谁看出来的提点一下

mxper88 发表于 2020-5-29 21:26:24

学习一下,准备测试

shower.xu 发表于 2020-6-4 11:35:20

xyb852 发表于 2020-5-27 14:34
2020-05-27更新1.20版:
      优化结构体定义,修改了接收发送缓存的定义
      优化串口接收部分,接收更 ...

请教个问题,发送字符串怎么比较好,有没有不一直占用等待单字节的代码,以供参考。谢谢

bbbbbv 发表于 2020-6-4 11:37:23


留个记号,有空学习一下

xyb852 发表于 2020-6-4 18:34:25

shower.xu 发表于 2020-6-4 11:35
请教个问题,发送字符串怎么比较好,有没有不一直占用等待单字节的代码,以供参考。谢谢 ...

可以用论坛里的小小调度器,可以找找楼上有例子可以参考

mdcao 发表于 2020-6-18 09:03:12

不错,学习一下!

kap 发表于 2020-6-18 12:35:57

早期用PIC和台湾MCU很多都是用模拟串口,非常稳定

xyb852 发表于 2020-6-20 21:23:36

Bicycle 发表于 2019-1-16 11:03
粤原点的ide是不是会破坏原来的mplab

新版的辉芒和粤原点的IDE,C编译器会互相冲突,也和MPLAB的PICC编译器冲突。基本是只能用一个。

xyb852 发表于 2020-6-20 21:40:17

yelong98 发表于 2020-5-29 09:33
没看出来哪里三倍采样了,纯等待时间,并没有多次采样判断干扰,可能我水平低,有谁看出来的提点一下 ...

可能是我理解的不一样,这里的意思是把基准时间分成三等份,采样点的位置在三份的交汇点上。

BS_good200xy 发表于 2020-8-17 15:50:29

正在研究模拟串口,多谢分享!

BS_good200xy 发表于 2020-8-17 21:43:44

在STM32上成功移植。

chinazhsj 发表于 2021-5-14 21:23:15

好好研究一下{:smile:}

wqy0410 发表于 2021-5-14 23:25:53

这个中断cpu太频繁了吧

qwe2231695 发表于 2021-5-16 02:11:53

本帖最后由 qwe2231695 于 2021-5-16 02:16 编辑

要进行115200通信 , 一秒钟中断345600次, 每次间隔0.0029ms , 约3us.中断占用必须短,用汇编.

weiwei4 发表于 2021-6-29 14:16:26

楼主使用的这个方法很好,最近可能用得上
感谢分享

bbbbbv 发表于 2021-6-29 16:10:58

有机会验证一下。
页: [1]
查看完整版本: 单片机通用软件模拟串口 三倍采样 同步收发