完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
本贴是根据一博主改写的代码,实现了一个io口模拟串口对字符串进行收发。原帖地址:https://blog.csdn.net/wxh0000mm 话不多说进入正题,串口通信协议发送一个字节默认为10个bit,其中包括开始位、停止位和中间八个数据位。数据固定开始位为低电平,结束位为高电平。如果我们发送字母a最终会以二进制0 0110 0001 1形式进行数据传输。 对串口通信协议有了基础的了解写代码就好办了,发送函数是很简单的,只需要根据通信协议,在字符bit位为1的时候拉高引脚,bit位为0的时候将引脚拉低即可进行数据的传输。以波特率9600为例:传输一个bit位的间隔就是1/9600s,就约等于104us发送一个bit位。 下面是io口模拟的发送函数 /*********************模拟串口发送数据***********************************/ //IO口引脚定义 void VirtualCOM_TX_GPIOConfig(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); /* PA4设为数据输出口,模拟TX */ GPIO_InitStruct.GPIO_Pin = COM_TX_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(COM_TX_PORT, &GPIO_InitStruct) GPIO_SetBits(COM_TX_PORT, COM_TX_PIN); //设置默认位为高电平 } 定义了数据发送引脚即可利用串口通信协议,间隔一定延时对单个bit位进行传输 //模拟引脚发送单个字节 void VirtualCOM_ByteSend(u8 val) { u8 i = 0; COM_DATA_LOW; //引脚拉低,即将发送数据 Delay_Us(BuadRate9600); //延时104us for(i = 0; i < 8; i++) //8位数据位 { if(val & 0x01) //如果bit位为1 COM_DATA_HIGH; //引脚拉高 else //否则拉低 COM_DATA_LOW; Delay_Us(BuadRate9600); val >>= 1; } COM_DATA_HIGH; //停止位 Delay_Us(BuadRate9600); } 有了单个字节的发送函数,直接调用该函数即可发送字符串 void VirtualCOM_StringSend(u8 *str) { while(*str != 0) { VirtualCOM_ByteSend(*str); str++; } } 发送的流程明白了,接收也是同样的道理,不过接收就不能用延时去进行接收,因为程序代码运行需要一定的周期,一旦数据量大了就无法准确的进行数据的接收。所以我们需要在检测到开始位的时候去开启一起定时器,定期接收bit数据位。 /*********************串口变量初始化***********************************/ u8 recvStat = COM_STOP_BIT; u8 recvData = 0x00; u8 COM_RX_BUF[COM_REC_LEN]; u16 COM_RX_STA = 0; u8 COM_RX_END = 1; /*********************模拟串口接收数据***********************************/ //接收引脚定义 void VirtualCOM_RX_GPIOConfig(void) { GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE); /* PA5设为数据输入口,模拟RX */ GPIO_InitStruct.GPIO_Pin = COM_RX_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(COM_RX_PORT,&GPIO_InitStruct); GPIO_SetBits(COM_RX_PORT, COM_RX_PIN); //设置默认位为高电平 EXTI_InitStruct.EXTI_Line = EXTI_Line5; //中断线 EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式 EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿中断 EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn; //外部中断,边沿触发 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); } //串口外部中断 void EXTI9_5_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line5) != RESET) { if(!COM_RX_STAT) //检测引脚高低电平,如果是低电平,则说明检测到下升沿 { if(recvStat == COM_STOP_BIT) //状态为停止位 { recvStat = COM_START_BIT; //接收开始位 COM_RX_END = 0; //标志数据是否处理完成 Delay(63); TIM_Cmd(TIM2,ENABLE); //开启定时器 } } EXTI_ClearITPendingBit(EXTI_Line5); //清除EXTI_Line1中断挂起标志位 } } //清空字符数组 void CLR_Buf(void) { unsigned char y; for(y = 0;y < COM_REC_LEN;y ++ ) { COM_RX_BUF[y] = ' |