主要借鉴https://blog.youkuaiyun.com/tonyiot/article/details/82502953这篇文章
在实际应用中往往会出现芯片的串口不够用的情况,此时可以利用多余的IO来模拟串口,实现数据的收发。
在利用IO模拟串口时,按照传输协议进行操作即可。
关键之处在于:
对于发送,计算好不同波特率对应的延时时间进行数据发送。
对于接收,通过外部中断检测接收管脚的下降沿,检测到起始信号后开启定时器,定时器按照波特率设定好时间,每隔一段时间进入定时器中断接收数据,完成一个字节后关闭定时器。
本文的波特率选用9600
发送延时计算: 1/9600≈104us,按照此时间进行数据的发送
#define TX_PIN GPIO_Pin_6
#define RX_PIN GPIO_Pin_7
#define TX_DATA_H() GPIO_SetBits(TX_PORT, TX_PIN) //高电平
#define TX_DATA_L() GPIO_ResetBits(TX_PORT,TX_PIN) //低电平
#define COM_RX_READ GPIO_ReadInputDataBit(RX_PORT, RX_PIN)//端口电平读取
#define BuadRate_9600 104 //模拟9600的频率
enum{
COM_START_BIT, //
COM_D0_BIT, //bit0
COM_D1_BIT, //bit1
COM_D2_BIT, //bit2
COM_D3_BIT, //bit3
COM_D4_BIT, //bit4
COM_D5_BIT, //bit5
COM_D6_BIT, //bit6
COM_D7_BIT, //bit7
COM_STOP_BIT, //停止位
};
unsigned char recvStat = COM_STOP_BIT;
uint8_t recvData = 0;//接收的数据
uint8_t len;
uint8_t uart_buf[20];//接收缓冲区
void VirtualCOM_Config(void)
{
VirtualTx_Config();//发送端口初始化
EXITRx_Init();//接收端口初始化,及接收中断配置
TIM2_Configuration(103,71); //设置对应模特率的定时器的定时时间
}
发送端口配置
void VirtualTx_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
//为数据输出口,模拟TX
GPIO_InitStructure.GPIO_Pin = TX_PIN; //PA.6 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(TX_PORT, &GPIO_InitStructure);
GPIO_SetBits(TX_PORT, TX_PIN);
}
void EXITRx_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
//为数据输入口,模拟RX
GPIO_InitStructure.GPIO_Pin = RX_PIN; //PA.7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RX_PORT, &GPIO_InitStructure);
//GPIOA.7 中断线以及中断初始化配置下降沿触发
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7);
EXTI_InitStructure.EXTI_Line = EXTI_Line7;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM2_Configuration(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//定时器TIM2初始化:t= (arr+1) *(psc+1) / Tck_tim
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void VirtualCOM_ByteSend(uint8_t Data)
{
uint8_t i;
TX_DATA_L(); // 发送起始位
delay_us(BuadRate_9600);
for(i=0; i<8; i++) // 发送8位数据位
{
if (Data & 0x01)
TX_DATA_H(); // 1
else
TX_DATA_L(); // 0
Data >>= 1;
delay_us(BuadRate_9600);
}
TX_DATA_H(); // 发送结束位
delay_us(BuadRate_9600);
}
void SendBytes(u8 *str,u8 len)
{
u16 i;
for(i=0; i<len; i++)
VirtualCOM_ByteSend(str[i]);
}
//发送字符串
void VirtualCOM_StringSend(u8 *str)
{
while(*str != 0)
{
VirtualCOM_ByteSend(*str);
str++;
}
}
void EXTI9_5_IRQHandler(void)
{
//确保是否产生了EXTI Line中断
if(EXTI_GetITStatus(EXTI_Line7) != RESET)
{
if(!COM_RX_READ)//检测接收引脚高低电平,如果低电平,则说明检测到下降沿
{
if(recvStat == COM_STOP_BIT)//为停止位
{
recvStat = COM_START_BIT; //接收到开始位
recvData = 0;
TIM_Cmd(TIM2, ENABLE);//打开定时器,接收数据
}
}
EXTI_ClearITPendingBit(EXTI_Line7); //清除EXTI_Line7中断挂起标志位
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
recvStat++; //改变状态机
if(recvStat == COM_STOP_BIT)//收到停止位
{
TIM_Cmd(TIM2, DISABLE);//关闭定时器
uart_buf[len++] = recvData;
return;
}
if(COM_RX_READ) //'1'
{
recvData |= (1 << (recvStat - 1));
}
else //'0'
{
recvData &= ~(1 <<(recvStat - 1));
}
}
}