STM32模拟串口(UART)使用!
1、添加头文件
首先我们先添加相应的头文件。既然我们要进行对串口的模拟,因此我们要先了解uart相关的通信协议。由于UART的通信方式是由1个起始位,8个数据位,包含一个奇偶校验位,和结束位构成因此我们将使用单片机中的两个普通的IO口电平的高低进行对相应时序的模拟。
#include "stm32f10x.h" #include "vuart2.h"2、宏定义
使用到的io口为 #define OI2_TXD PDout(6) #define OI2_RXD PDin(7) #define BuadRate2_9600 104 #define Recive2_Byte 19 //接收缓冲器的个数 u8 len2 = 0; //接收计数 u8 USART2_buf[Recive2_Byte]; //接收缓冲区
将IO口相应的位带操作函数进行宏定义从而使得在对不同的电平的进行转换的时候更为方便,并且减少了调用其他函数的过程所消耗的时间,程序执行效率更高。
在本次的传输过程中我选用的是使用波特率速率为9600,也就是1s中发送9600个字节,因此对每个字节数据进行计算1000000us/9600可以得出,发一个字节的数据需要进行大概需要 104.16us并且对于相应的电平持续时间要求误差不能超过±5%因此对我们进行时间的控制要求就显得比较重要了。 3、枚举出各个位enum{ COM_START_BIT, COM_D0_BIT, COM_D1_BIT, COM_D2_BIT, COM_D3_BIT, COM_D4_BIT, COM_D5_BIT, COM_D6_BIT, COM_D7_BIT, COM_STOP_BIT, }; u8 recvStat2 = COM_STOP_BIT; u8 recvData2 = 0;4、IO——TXD进行模拟void IO2_TXD(u8 Data) { u8 i = 0; OI2_TXD = 0; delay_us(BuadRate2_9600); for(i = 0; i < 8; i++) { if(Data&0x01) OI2_TXD = 1; else OI2_TXD = 0; delay_us(BuadRate2_9600); Data = Data>>1; } OI2_TXD = 1; delay_us(BuadRate2_9600); }
由于发送的信号是将TXD信号进行拉低处理,因此在拉低TXD相应的IO口之后进行延时处理,再进行循环对我们需要发送的各个位的数据继续进行发送循环发送完成之后将电平拉高代表停止位。 5、构建发送函数void USART2_Send(u8 *buf, u8 len2) { u8 t; for(t = 0; t < len2; t++) { IO2_TXD(buf[t]); } }
其中的*buf为需要发送的数据,len2为数据长度,进行循环调用IO_TXD进行一个字节一个字节的数据发送。 6、IO口初始化void IO2Config(void) { GPIO_InitTypeDef GPIO_InitStructure;//初始化gpio NVIC_InitTypeDef NVIC_InitStructure;//中断初始化函数 EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC, ENABLE); //使能PD,PC端口时钟 //SoftWare Serial TXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //选择io口6 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_SetBits(GPIOD,GPIO_Pin_6); //TXD默认电平拉高 //SoftWare Serial RXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource7); //对D7的下降沿进行中断采样,当接收到下降沿时代表接收到数据触发中断处理函数 EXTI_InitStruct.EXTI_Line = EXTI_Line7;//用到了中断7 EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断 EXTI_InitStruct.EXTI_LineCmd=ENABLE; EXTI_Init(&EXTI_InitStruct);//初始化中断 NVIC_InitStructure.NVIC_IRQChannel= EXTI9_5_IRQn ; //中断发生于9-5的中断之中 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); }7、定时器初始化void TIM5_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //时钟使能 //定时器TIM5初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE ); //使能指定的TIM5中断,允许更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM5中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级1级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级1级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 }
对TIM5进行初始化操作使得定时器可以检测到各个位的电平持续性时间从而对接收到的数据进行分析。计时结束后进入中断TIM5处理。 8、外部中断处理函数void EXTI9_5_IRQHandler(void) { if(EXTI_GetFlagStatus(EXTI_Line7) != RESET)//对中断标志位进行采集 { if(OI2_RXD == 0) { if(recvStat2 == COM_STOP_BIT) { recvStat2 = COM_START_BIT;//将当前的状态设置为开始位 TIM_Cmd(TIM5, ENABLE);//开启定时器计数 } } EXTI_ClearITPendingBit(EXTI_Line7); //清除中断标志 } }9、定时器中断处理函数void TIM5_IRQHandler(void) { if(TIM_GetFlagStatus(TIM5, TIM_FLAG_Update) != RESET) { TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); //清除中断标志位 recvStat2++; //将位置移动到第一位的数据 if(recvStat2 == COM_STOP_BIT)//当运行到停止位时进入 { TIM_Cmd(TIM5, DISABLE);//停止tim5 USART2_buf[len2++] = recvData2;//将采集到的各个数据传递给USART2_buf if(len2 > Recive2_Byte-1)//将数据通过回显到串口调试助手中 { len2 = 0; USART2_Send(USART2_buf,Recive2_Byte); } return; } if(OI2_RXD)//采集RXD各个电平 { recvData2 |= (1 << (recvStat2 - 1)); }else{ recvData2 &= ~(1 << (recvStat2 - 1)); } } }整体代码vuart2.c#include "stm32f10x.h" #include "vuart2.h" /** *软件串口的实现(IO模拟串口) * 波特率:9600 1-8-N * TXD : PD6 * RXD : PD7 * 使用外部中断对RXD的下降沿进行触发,使用定时器5按照9600波特率进行定时数据接收。 * Demo功能: 接收11个数据,然后把接收到的数据发送出去 */ #define OI2_TXD PDout(6) #define OI2_RXD PDin(7) #define BuadRate2_9600 104 #define Recive2_Byte 19 //接收缓冲器的个数 u8 len2 = 0; //接收计数 u8 USART2_buf[Recive2_Byte]; //接收缓冲区 enum{ COM_START_BIT, COM_D0_BIT, COM_D1_BIT, COM_D2_BIT, COM_D3_BIT, COM_D4_BIT, COM_D5_BIT, COM_D6_BIT, COM_D7_BIT, COM_STOP_BIT, }; u8 recvStat2 = COM_STOP_BIT; u8 recvData2 = 0; void IO2_TXD(u8 Data) { u8 i = 0; OI2_TXD = 0; delay_us(BuadRate2_9600); for(i = 0; i < 8; i++) { if(Data&0x01) OI2_TXD = 1; else OI2_TXD = 0; delay_us(BuadRate2_9600); Data = Data>>1; } OI2_TXD = 1; delay_us(BuadRate2_9600); } void USART2_Send(u8 *buf, u8 len2) { u8 t; for(t = 0; t < len2; t++) { IO2_TXD(buf[t]); } } void IO2Config(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PC端口时钟 //SoftWare Serial TXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_SetBits(GPIOD,GPIO_Pin_6); //SoftWare Serial RXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource7); EXTI_InitStruct.EXTI_Line = EXTI_Line7; EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断 EXTI_InitStruct.EXTI_LineCmd=ENABLE; EXTI_Init(&EXTI_InitStruct); NVIC_InitStructure.NVIC_IRQChannel= EXTI9_5_IRQn ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); } void TIM5_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //时钟使能 //定时器TIM5初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE ); //使能指定的TIM5中断,允许更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM4中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级1级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级1级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 } void EXTI9_5_IRQHandler(void) { if(EXTI_GetFlagStatus(EXTI_Line7) != RESET) { if(OI2_RXD == 0) { if(recvStat2 == COM_STOP_BIT) { recvStat2 = COM_START_BIT; TIM_Cmd(TIM5, ENABLE); } } EXTI_ClearITPendingBit(EXTI_Line7); } } void TIM5_IRQHandler(void) { if(TIM_GetFlagStatus(TIM5, TIM_FLAG_Update) != RESET) { TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); recvStat2++; if(recvStat2 == COM_STOP_BIT) { TIM_Cmd(TIM5, DISABLE); USART2_buf[len2++] = recvData2; if(len2 > Recive2_Byte-1) { len2 = 0; USART2_Send(USART2_buf,Recive2_Byte); } return; } if(OI2_RXD) { recvData2 |= (1 << (recvStat2 - 1)); }else{ recvData2 &= ~(1 << (recvStat2 - 1)); } } }vuart2.h#ifndef __VUART2__H #define __VUART2__H #include "stm32f10x.h" void IO2_TXD(u8 Data); void USART2_Send(u8 *buf, u8 len); void IO2Config(void); void TIM5_Int_Init(u16 arr,u16 psc); #endif
本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
颜骏凌挽狂澜于既倒,莱科错用一人险丢胜局,哈维尔却笑了面对河南嵩山龙门的残阵,上半场已经两球领先,下半场更是把裁判无中生有的点球扑出,此时人们皆认为胜负毫无悬念了,可是剧情却逆转了。球迷说若不是颜骏凌开挂般的表现,四次扑出必进球,挽狂
小米新机官宣9月27日,正式发布从目前来看,9月份作为近几个月新机发布量最少的一个月,主要是苹果华为发布了旗舰机,其它手机品牌基本选择了让路,毕竟二大旗舰机无论是芯片还是系统都有一定的优势,实在是很难竞争,所以各
国产电视剧飞天标上线!设计真的很赞9月21日,广电总局发布通知。为适应新时代电视剧高质量发展需要,进一步提升电视剧制作规范化标准化水平,打造中国电视剧的标志性文化符号,自2022年9月21日起,国产电视剧将启用统一
新研究估算全球蚂蚁总数约2亿亿只蚂蚁是一种常见的昆虫,那地球上到底有多少只蚂蚁呢?一项最新的科学研究给出的估算结果是至少有2亿亿只。德国维尔茨堡大学中国香港大学等机构的研究人员在最新一期美国国家科学院学报上发表论
敌方秒选钟无艳怎么办?别用亚瑟,放弃芈月,他让钟无艳不敢出塔在王者荣耀中战士类英雄越来越受玩家们的欢迎,其实还是因为战士的整体强度提升了,虽然战士基本上都是打上单的位置,但是一名会玩上单的玩家,前期就可以在线上打出优势,之后可以快速游走支援
3。0须弥版本108个草神瞳路线(已完结)草神瞳共108个(建议做完森林书全篇开始)(森林书任务会解锁任务道具梦之树一些传送点和神像也是目前最阴间的任务)道成林和护世森p326个神瞳红色标点121(直接或解密获取)黑色标点
原神的反作弊驱动被勒索软件利用杀死杀毒软件原神的反作弊驱动被勒索软件利用杀死杀毒软件安全公司趋势科技的研究人员报告,原神的反作弊驱动mhyprot2。sys被勒索软件利用杀死杀毒软件的进程和服务。mhyprot2。sys作
58连线将在中超上演!海港官方巴尔加斯正式加盟北京时间8月30日,上海海港官方宣布,阿根廷前锋马蒂亚斯巴尔加斯正式加盟,他与武磊的58连线将在中超上演。附海港官宣官宣阿根廷前锋马蒂亚斯巴尔加斯正式加盟上海海港足球俱乐部经友好协
关窗前的疯狂!大连人继续官宣新外援博桑季奇加盟北京时间8月30日,大连人继续官方宣布新外援加盟,与塞尔维亚球员内马尼亚博桑季奇达成一致,后者正式加盟。附大连人官宣经与球员友好协商,塞尔维亚球员内马尼亚博桑季奇(NemanjaB
挖海河挖海河杨金山海河是个工程浩大的总称,分别有相当多的支干,起到防洪排洪水作用,在六七十年代,伟人毛主席号召一定要根治海河,千軍万马齐上阵,迅速掀起挖海河高潮。在那个年代人们生活还很差
洗冤还是洗白?梅西续约巴萨邮件被曝,拉波尔塔的心思路人皆知接连撬动4个杠杆,拉波尔塔成为今夏转会窗最靓的仔,巴萨也一改此前虎落平阳的落魄模样,重新焕发了活力。改头换面的巴萨,可以说是欧洲足坛势头最猛的球队之一。西甲6轮5胜1平,进18球失