历史上的今天
返回首页

历史上的今天

今天是:2025年01月31日(星期五)

2019年01月31日 | 基于STM32系列的模拟串口(非阻塞式)

2019-01-31 来源:eefocus

STM32单片机一般少则3个串口,多则5个,而我这次的项目还偏偏5个硬件串口还是不够用.


至于不够用的原因,哎,是项目做到后面有定制,随便哪个串口都省不得,没得办法,只能另想法子咯~


板子上有几个预留IO口,可以用来模拟串口. 模拟串口一般都选9600,速度最快试了也才19200,


所以限制还是较多的,一般不得以情况下才会用到.


在写程序之前我也是参考了前辈们,觉得写的不咋地,照抄他的代码,那我的系统啥事情也


不要干了,就在那里delay吧!!! 模拟串口分收和发:收比较难,发送比较容易,那就先将接收这块吧。


接收:


有2种思路: 一种是第一个下降沿开始启动定时器,每个bit都去采样Rx电平;


                 从第一个边沿开始统计时间戳,接收完10个bit后再解析.


我这边碰巧选择的是第二种方法,具体实现思路是:


① 启动定时器2用来做背景时间,定时器分频后的计数频率为1MHz,那么对于9600bps来说1个bit就是104.16us,我们取整数104就可以了;


② 再同时启动一个定时器3,定时时间为104*9.5,这个定时器中断发生的时候表示一个字节接收完毕了;

我们以0x37串口通讯时序图为例, ,传输的时候是LSB 1st, Rx引脚选择边沿触发, 第1次边沿到的时候启动定时器2,同时启动定时器3, 第2个边沿的时候将定时器2的计数值存到数组里,此时是104,第3个边沿触发的时候将其计数104*5计数值,同理将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[i]+=ONE_BIT_TIME/2;

timerecode[i]/=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[i]=timerecode[i+1];

if(find_array[i]!=0)

{

new_array[find_array[i]-1]=find_array[i];

}

}

/*

然后在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[i]-1)

a=!a;

 

if(a)

byte|=0x80;

}

return byte;

}


对于上图0x37的例子,当定时器3中断时,就调用process_byte(nums-1),其中nums=6,就


是边沿次数,来解析我们接收到的byte啦.


下面的截图是我用模拟串口编写的收发例子:


收发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;

}

}

}


推荐阅读

史海拾趣

B&F Fastener Supply公司的发展小趣事

凭借优质的产品和服务,B&F Fastener Supply公司逐渐赢得了大型电子厂商的青睐。多家知名电子企业选择与B&F建立长期合作关系,将其作为紧固件和电子元器件的主要供应商。这不仅为B&F带来了稳定的订单和收入来源,还进一步提升了公司在电子行业的地位和影响力。

铨力(ALLPOWER)公司的发展小趣事

随着市场竞争的加剧,铨力公司意识到单纯依靠太阳能电源产品已无法满足市场需求。于是,公司开始着手研发更为先进、高效的能源技术。经过多次试验和改进,铨力成功突破了技术瓶颈,研发出了新一代高效能、低成本的太阳能电池板。这一技术的突破不仅提升了产品的竞争力,也为公司打开了更广阔的市场空间。

Gigarams Semiconductor Device Corp公司的发展小趣事
这可能是由于定位力量不够或初始力矩过大导致的。解决方案包括调整定位参数、优化启动策略或增加缓冲装置以减少振动。
Broyce Control公司的发展小趣事

Broyce Control的故事始于1963年,当时创始人[XXXXX]在英国的一个工业小镇上开设了这家专注于控制面板制造的公司。起初,Broyce Control主要为当地的中小型工厂提供定制化的电气控制解决方案。由于[XXXXX]对电气技术的深刻理解和对工业应用的敏锐洞察,公司的产品在市场上迅速获得了认可。随着口碑的传播,订单量逐渐增加,Broyce Control逐渐积累起了一定的资本和技术储备。

APC (APC by Schneider Electric)公司的发展小趣事

APC的产品和服务在全球范围内赢得了广泛认可。其InfraStruXure产品线因其集成式的电力、制冷、机架、管理和服务实现了一种开放、具有适应性且集成化的解决方案,赢得了近20个奖项。此外,APC还因其卓越的产品和业务表现,在多个年份赢得了全球100多个奖项。这些奖项不仅证明了APC在电子行业中的领先地位,也反映了其对于创新和质量的不懈追求。

振宝佳(DMBJ)公司的发展小趣事

作为一家具有国际视野的企业,振宝佳公司始终坚持以质量为核心的发展理念。为了满足出口欧盟的质量要求,公司不断提升产品的品质和性能,加强质量控制和检验。经过多年的努力,公司终于成功获得了出口欧盟的资格认证。这一成就的取得不仅证明了公司的实力和能力,也为其在国际市场上赢得了更多的机会和声誉。

以上五个故事框架均基于振宝佳(DMBJ)公司在电子行业发展的实际情况进行编写,旨在展示公司在技术、市场、生产、质量等方面的努力和成就。请注意,这些故事仅为概述,具体细节和数据可能需要根据实际情况进行补充和完善。

问答坊 | AI 解惑

ARM培训视频-dsp-CPLD-FPGA开发视频-嵌入式系统开发-单片机开发视频

ARM培训视频-dsp-CPLD-FPGA开发视频-嵌入式系统开发-单片机开发视频 ------------------------------------------------------------------ 【ARM培训视频】课程主要内容 1.概念和基本工具: 嵌入式系统基本概念、嵌入式操作系统介绍 嵌入式系 ...…

查看全部问答>

汽车伴我行——京郊另类玩家经典路线、汽车新科技、经典车型大罗列

汽车伴我行 1. 京郊另类玩家经典路线推荐 https://bbs.eeworld.com.cn/thread-2611-1-40.html 2. 智能车赛道记忆算法的研究 https://bbs.eeworld.com.cn/thread-9739-1-18.html 3. 汽车新科技 https://bbs.eeworld.com.cn/thread-7089-1-17.html ...…

查看全部问答>

推荐FPGA+单片机开发板。。。

联华众科FPGA开发板 FA130 北京联华众科科技有限公司 http://www.lianhua-zhongke.com.cn Abstract  摘 要:联华众科FPGA开发板FA130核心器件为 Altera Cyclone系列的EP1C3,配置芯片为EPCS1,FA130上可以运行SOPC Bui ...…

查看全部问答>

关于wince5.0 的IE支持flash的问题。

如何让wince5.0的IE支持flash呢?在adobe的网站上找到了用于ppc的flash插件,但不能用于wince, 听说,有人把ppc相应的dll考到了wince里,wince就可以用ppc的软件了,真的可能吗? 还有我的硬件是x86的,能否找到用于x86的ppc系统文件呢(dll), ...…

查看全部问答>

各位高手大虾帮帮忙!关于串口

各位高手大虾,我的开发板上有3个串口,串口1用来调试,串口2没有用,串口3接红外,由于我要加接GPS,又不想占用调试串口,就想把串口2打通, 在BSP中添加了相关代码后,在CE注册表下可以看到串口2,可是接上GPS线却什么反应的都没有,而串口1就可 ...…

查看全部问答>

同步电路,post_sta和post_sim不同结果,谁碰到过?

post_sta全通过,analysis_coverage分析基本都覆盖了,post_sim报setup违反,谁碰到过类似问题?用的是smic sc-m库   请各位高手支招!…

查看全部问答>

Bootloader 的概念

    简单地说,Bootloader 就是这么一小段程序,它在系统上电时开始执行,初始化硬件设备、准备好软件环境,最后调用操作系统内核。    可以增强Bootloader 的功能,比如增加网络功能、从PC 上通过串口或网络下载文 ...…

查看全部问答>

求助!附程序!MSP430F169用I2C控制ADV7183写数据程序调试不成功!!急!!

#include "io430.h" #include "In430.h" int tx_count; volatile unsigned char I2CBuffer[3]; void Init_Port(void) { P1DIR=0xFF; P2DIR=0XFF; P3DIR=0xFF; P4DIR=0xFF; P5DIR=0xFF; P6DIR=0xFF; P1SEL = 0; } void ...…

查看全部问答>

我是一个什么都不懂的菜鸟,想学有关硬件的开发,请各位前辈指导

我偶然间发现这个论坛,然后对这个开发感兴趣,而且和自己的工作有那么一丁点关系,在工作中使用3维制作软件,里面会用到虚拟的摄像机,我就想如果有传感器控制会更加方便。所以希望在此学习。请各位前辈告诉我该如何入门。谢谢…

查看全部问答>