完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
网上有好多关于利用IO口来实现串口数据收发的实例,这种方法的实质都是可以应用于任何一款微处理器上,而不仅仅局限于stm32。有相关的源代码链接参考:
当然,如果成本要求不高,可以利用串口扩展芯片是最方便的,如CH438,WK系列芯片等。 IO口模拟串口的一些基本原理,阐述如下: STM32单片机一般少则3个串口,多则5个,而项目还偏偏5个硬件串口还是不够用.板子上有几个预留IO口,可以用来模拟串口. 模拟串口一般都选9600,速度最快试了也才19200,所以限制还是较多的,一般不得以情况下才会用到. IO口模拟串口的思路也比较简单,一切按照串口协议进行操作即可。对于发送,计算好不同波特率对应的延时时间进行数据发送。对于接收,稍微复杂。通过外部中断检测接收管脚的下降沿,检测到起始信号后开启定时器,定时器按照波特率设定好时间,每隔一段时间进入定时器中断接收数据,完成一个字节后关闭定时器。 模拟串口分收和发:收比较难,发送比较容易,那就先将接收这块吧。 接收: 有2种思路: 一种是第一个下降沿开始启动定时器,每个bit都去采样Rx电平; 从第一个边沿开始统计时间戳,接收完10个bit后再解析. 我这边碰巧选择的是第二种方法,具体实现思路是: ① 启动定时器2用来做背景时间,定时器分频后的计数频率为1MHz,那么对于9600bps来说1个bit就是104.16us,我们取整数104就可以了; ② 再同时启动一个定时器3,定时时间为1049.5,这个定时器中断发生的时候表示 一个字节接收完毕了 我们以0x37串口通讯时序图为例, ,传输的时候是LSB 1st, Rx引脚选择边沿触发, 第1次边沿到的时候 启动定时器2,同时启动定时器3, 第2个边沿的时候将定时器2的计数值存到数组里,此时是104,第3个 边沿触发的时候将其计数1045计数值,同理将4,5,6三个边沿对应的TIM2值存到缓存里,最终是 0,104,416,520,728,936,除以104变成0,1,4,5,7,9; 如何将0,1,4,5,7,9解析成0x37,看似简单的问题其实还是有点麻烦的~ 给个思路,先将0和9这样的数字去掉,变成1,4,5,7;然后从1开始数到8,跟数组里元素是否有匹配,如果匹配就将状态取反,没有就维持之前的状态:1有,2,3没有,4有,5有,6没有,7有,8没有–>11101100->倒序后就是0x37了; 再来分析几个处理后的值:4,5,7: 1,2,3没有,4有,5有,6没有,7有,8没有–>00010011->逆序后11001000->0xC8 1,2: 1有,2有,3,4,5,6,7,8没有->10000000->逆序后00000001->0x01 1,2,3,4,6,7,8:1有,2有,3有,4有,5没有,6有,7有,8有->10100101->逆序后10100101->0xA5 7,8:1,2,3,4,5,6没有,7有,8有->00000010->逆序后01000000->0x40 总结下如果第一个是1开头的那么得出的bit就是1,如果第一个是其他数字开头的,则 “1到其他数字” 之间用0来填充; 下面我们就根据总结出来的规律编写解析代码: int process_byte(int nums) { int i; u8 a=0; u8 byte=0; u8 new_array[8]; memset(new_array,0,sizeof(new_array)); for(i=0;i<=nums;i++) //1...7 { timerecode+=ONE_BIT_TIME/2; timerecode/=ONE_BIT_TIME; } if(timerecode[nums]>=9) //计算下标是从1开始的,并且去掉了9,所以剩下的也不多了 nums--; if(nums<=0) //全0特殊处理 return 0; find_max=nums; //去掉第一个0 /* 假设收到的是0,2,4,9 经过处理后就剩下2,4 将2放到new_array下标为1的地方,4放到下标为3的地方 */ for(i=0;i find_array=timerecode[i+1]; if(find_array!=0) { new_array[find_array-1]=find_array; } } /* 然后在for(i=find_array[0]-1;i<8;i++) 这个循环里找,如果对应的位有边沿改变,就改变a的值 找不到就复制a的值,这里有点要注意,i循环是从高电平 开始的 */ for(i=find_array[0]-1;i<8;i++) { byte>>=1; if(i==new_array-1) a=!a; if(a) byte|=0x80; } return byte; } 对于上图0x37的例子,当定时器3中断时,就调用process_byte(nums-1),其中nums=6,就是边沿次数,来解析我们接收到的byte啦. 下面的截图是我用模拟串口编写的收发例子:``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912085041489.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzMxMzY5Nw==,size_16,color_FFFFFF,t_70) 收发48个任意字节都正常,没有哪个是解析出错的。 发送部分:这部分完全依赖定时器7了,定时周期104us。 发送的原理很简单,我这里没有使用delay方式来发送,而是在定时器里实现发送的; 对于上图0x37的例子,当定时器3中断时,就调用process_byte(nums-1),其中nums=6,就是边沿次数,来解析我们接收到的byte啦.下面的截图是我用模拟串口编写的收发例子:```![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzMxMzY5Nw==,size_16,color_FFFFFF,t_70)收发48个任意字节都正常,没有哪个是解析出错的。发送部分:这部分完全依赖定时器7了,定时周期104us。发送的原理很简单,我这里没有使用delay方式来发送,而是在定时器里实现发送的; #define VIR_TXBUFF_SIZ 128 #define VIR_RXBUFF_SIZ 128 typedef struct { //---------Rx------------ u8 rxov; u8 rxlen; u8 rxbuff_idx; u8 rx_decode_flag; u8 RxBuff[VIR_RXBUFF_SIZ];//大小一定要是2的次方关系 u8 RXREG; //---------Tx------------ u8 send_flag; u8 send_max; u8 send_cnt; u8 send_mode; //0-阻塞式发送 1-中断发送(放到sendbuff里,指定send_max即可) u8 sendbuff[VIR_TXBUFF_SIZ]; u8 TXREG; }VIRTUAL_UART_t; static u8 send_a_byte(u8 dat) { if(VirtualUart.send_flag) return 1; VirtualUart.TXREG=dat;TIM7->CR1 |= TIM_CR1_CEN;Tx_Pin=0;VirtualUart.send_flag=1;return 0; } static void send_remain_byte(void) { if(VirtualUart.send_cnt>=VirtualUart.send_max) { VirtualUart.send_flag=0; //发送完毕 } else { VirtualUart.TXREG=VirtualUart.sendbuff[VirtualUart.send_cnt++]; Tx_Pin=0; //产生START信号 } } //TIM7定时器里调用 static u8 tim_send_byte(void (*Callback)(void)) { static int sendidx=0; sendidx++; if(sendidx<=8) //DATA 1,2,3...8{ Tx_Pin=VirtualUart.TXREG&0x01; VirtualUart.TXREG>>=1; } else if(sendidx==9) { //STOP Tx_Pin=1;} else if(sendidx==10) { //STOP的发送完毕了 sendidx=0; if(Callback!=NULL) //有多个字节要发送,调用回调函数继续发送下一个字节 Callback(); else VirtualUart.send_flag=0; //已经发送完毕了 return 0;}return 1; //1-busy } //阻塞式发送 void vu_send_string(u8 *s) { VirtualUart.send_mode=0; while(*s) { while(VirtualUart.send_flag!=0); send_a_byte(*s); s++; } } //阻塞式发送 void vu_send_len(u8 *s,int len) { VirtualUart.send_mode=0; while(len–) { while(VirtualUart.send_flag!=0); send_a_byte(*s); s++; } } //中断发送 int vu_send_some_byte_noblock(int len) { if(VirtualUart.send_flag) return -1; if(len>VIR_TXBUFF_SIZ) return -2; VirtualUart.send_mode=1;VirtualUart.TXREG=VirtualUart.sendbuff[0];TIM7->CR1 |= TIM_CR1_CEN;Tx_Pin=0; //产生START信号VirtualUart.send_flag=1;VirtualUart.send_max=len;VirtualUart.send_cnt=1;return 0; } //主要发送部分代码就在这儿了 void TIM7_IRQHandler(void) { TIM7->SR = (uint16_t)~TIM_IT_Update; if(VirtualUart.send_mode) { if(tim_send_byte(send_remain_byte)==0) { if(VirtualUart.send_flag==0) TIM7->CR1 &= ~TIM_CR1_CEN; TIM7->CNT=0; }}else { if(tim_send_byte(NULL)==0) { TIM7->CR1 &= ~TIM_CR1_CEN; TIM7->CNT=0; }} } |
|
|
|
只有小组成员才能发言,加入小组>>
3278 浏览 9 评论
2955 浏览 16 评论
3455 浏览 1 评论
8987 浏览 16 评论
4050 浏览 18 评论
1102浏览 3评论
570浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
568浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2301浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1857浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 05:05 , Processed in 1.275547 second(s), Total 81, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号