0、摘要
在实际开发当中会遇到串口不够的情况,此时我们可以通过GPIO模拟USART。
IO口模拟串口的思路也比较简单,一切按照串口协议进行操作即可。
对于发送,计算好不同波特率对应的延时时间进行数据发送。
对于接收,稍微复杂。通过外部中断检测接收管脚的下降沿,检测到起始信号后开启定时器,定时器按照波特率
设定好时间,每隔一段时间进入定时器中断接收数据,完成一个字节后关闭定时器。
1、新建工程,勾选相应的库
选中:CMSIS>CORE;Device>Startup;>StdPeriph Drivers>EXTI;>Framework;>GPIO;>RCC;>TIM;等
2、程序分析
IO口初始化配置
//输出管脚配置
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//输入管脚及中断配置
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 ,IO口默认是高电平
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_10);//保持高电平
EXTI_InitStruct.EXTI_Line=EXTI_Line10;
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=EXTI15_10_IRQn; //外部中断,边沿触发
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
发送函数
//发送1个字节
void IO_TXD(u8 ch)
{
u8 i = 0;
GPIO_ResetBits(GPIOC,GPIO_Pin_9);
delay_us(BuadRate_9600);
for(i = 0; i < 8; i++)
{
if(ch&0x01)
GPIO_SetBits(GPIOC,GPIO_Pin_9);
else
GPIO_ResetBits(GPIOC,GPIO_Pin_9);
delay_us(BuadRate_9600);
ch = ch>>1;
}
GPIO_SetBits(GPIOC,GPIO_Pin_9);
delay_us(BuadRate_9600);
}
//发送字符串
void USART_Send(u8 *buf, u8 len)
{
u8 t;
for(t = 0; t < len; t++)
{
IO_TXD(buf[t]);
}
}
设置枚举变量,这里考虑到USART共有10个bit数据。
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, //结束位
};
接收程序可以使用软件延时的方式(但是不推荐),也可使用定时器的方式进行接收。
- 软件延时方式
void EXTI15_10_IRQHandler(void)
{
enum stat recvStat = COM_STOP_BIT; //定义初始为停止位
if(EXTI_GetITStatus(EXTI_Line10)!=RESET) //检查外部中断是否产生了
{
if(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)) //检测引脚高低电平,如果是低电平,则说明检测到起始位
{
Delay(0x33A); //延时 0x432 约等于104us
if(recvStat == COM_STOP_BIT)
{
recvStat = COM_START_BIT; //此时的状态是起始
while(recvStat!= COM_STOP_BIT) // 循环到停止位
{
recvStat++; // 改变状态
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)) //‘1’
{
recvData |= (1 <<(recvStat -1));
}
else
{
recvData &= ~(1 << (recvStat -1));
}
Delay(0x33A);
}
}
}
EXTI_ClearITPendingBit(EXTI_Line10); //清除EXTI_Line10中断挂起标志位
}
}
- 定时器方式
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line14) != RESET)
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10) == 0)
{
if(recvStat == COM_STOP_BIT)
{
recvStat = COM_START_BIT;
TIM_Cmd(TIM4, ENABLE);
}
}
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
void TIM4_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
recvStat++;
if(recvStat == COM_STOP_BIT)
{
TIM_Cmd(TIM4, DISABLE);
USART_buf[len++] = recvData;
return;
}
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)) //接收为高电平
{
recvData |= (1 << (recvStat - 1));
}
else
{
recvData &= ~(1 << (recvStat - 1));
}
}
}
3、参考链接
USART简介
USART模拟