之前公司在做项目的时候需要用到多串口,板载串口资源不足,就在网上找相关内容,结合自己的理解做出虚拟串口。
模拟串口需要用到两个普通io引脚,一个定时器。
/**
*软件串口的实现(IO模拟串口)
* 波特率:4800 1-8-N
* TXD : PB13
* RXD : PB14
* 使用外部中断对RXD的下降沿进行触发,使用定时器4按照设定波特率进行定时数据接收。
*/
第一步,定时器初始化
void TIM4_Init(u16 arr)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
//定时器TIM4初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = 95; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM4中断,允许更新中断
TIM_Cmd(TIM4,DISABLE); //关闭定时器TIM4
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0; //先占优先级1级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //从优先级1级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
}
第二步,io口初始化
void USART_IO_Init(u16 baudRate)
{
u32 period;
if(baudRate == _4800BuadRate)
period = _4800BuadRate;
else if(baudRate == _9600BuadRate)
period = _9600BuadRate;
else if(baudRate==_19200BuadRate)
period = _19200BuadRate;
else if(baudRate == _38400BuadRate)
period = _38400BuadRate;
TIM4_Int_Init(period);
delayTime = baudRate;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
//SoftWare Serial TXD
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_13);
//SoftWare Serial RXD
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
EXTI_InitStruct.EXTI_Line = EXTI_Line14;
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_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
第三步,接受中断处理
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) != RESET)
{
if(!OI_RXD)
{
if(recvStat == COM_STOP_BIT)
{
recvStat = COM_START_BIT;
TIM_Cmd(TIM4, ENABLE);
}
}
EXTI_ClearFlag(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)
{
USART_IO_rxflag=1;
TIM_Cmd(TIM4, DISABLE);
USART_IO_RXbuf[USART_IO_len_count++]= recvData;
return;
}
if(OI_RXD)
{
recvData |= (1 << (recvStat - 1));
} else
{
recvData &= ~(1 << (recvStat - 1));
}
}
}
串口发送函数(偶校验)
void USART_IO_byte(u8 Data)
{
u8 i = 0;
u8 num = 0;
OI_TXD = 0;
delay_us(delayTime);
for(i = 0; i < 8; i++)
{
if(Data&0x01){
OI_TXD = 1;
num++;
}else{
OI_TXD = 0;
}
delay_us(delayTime);
Data = Data>>1;
}
if(num%2==0)
{
OI_TXD = 0;
}else
{
OI_TXD = 1;
}
delay_us(delayTime);
OI_TXD = 1;
delay_us(delayTime);
}
相关变量定义
uint8_t USART_IO_len_count = 0; /*接收缓冲区数据长度计数*/
uint8_t USART_IO_rxflag = 0; /*0:standy 1:receive ok*/
uint8_t USART_IO_RXbuf[USART_IO_RX_SIZE]= {0}; //接收缓冲区
uint8_t recvStat = COM_STOP_BIT;
uint8_t recvData = 0;
uint32_t delayTime;
以下为.h文件定义
//对应波特率的1个电平持续时间
//(1/9600) = 104us
/*用在IO模拟出uart定时器4的重装载值*/
#define _4800BuadRate 208
#define _9600BuadRate 104
#define _19200BuadRate 52
#define _38400BuadRate 25
#define OI_TXD PBout(13)
#define OI_RXD PBin(14)
#define USART_IO_RX_SIZE 100 /*接收缓冲区大小为200字节*/
extern u8 USART_IO_RXbuf[USART_IO_RX_SIZE];//接收缓冲区
extern u8 USART_IO_len_count; /*接收缓冲区数据长度计数*/
extern u8 recvData;
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,
};
以上内容就可以完成一个虚拟串口的发送与接收了