本贴是根据一博主改写的代码,实现了一个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] = '