最近做了一个项目,由于需要6个串口,单片机串口串口资源不够用,就用stm32单片机模拟了几路串口,波特率9600.
1、首先,我们先了解串行通信数据格式
2、数据发送端
实现方案:模拟串口的波特率为9600,故我们延时时间为104us,根据通信数据格式起始位为低电平,停止位为高电平。
/*
*********************************************************************************************************
* 函 数 名: SendOneByte
* 功能说明: 模拟串口发送一字节数据
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void SendOneByte(uint8_t datatoSend)
{
uint8_ti, tmp;
/*Start bit */
TXD_low();
delay_us(SendingDelay);
for(i= 0; i < 8; i++)
{
tmp = (datatoSend >> i) & 0x01;
if(tmp== 0)
{
TXD_low();
delay_us(SendingDelay); //0
}
else
{
TXD_high();
delay_us(SendingDelay); //1
}
}
/*Stop bit */
TXD_high();
delay_us(SendingDelay);
}
3、数据接收端
实现方案:串口接收数据实现比较复杂,
1、IO口需配置成外部中断(下降沿触发)
2、外部中断触发,开启定时中断TIM1
3、TIM1函数处理:第一位检测到低电平,接收到起始位,后面8位为数据位,后再 检测到高电平,一个字节接收结束。
第一步:配置外部中断和104us定时器
static void EXTI_Configuration(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*Enable the AFIO Clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource12);
/* Configure Button EXTI line */
EXTI_InitStructure.EXTI_Line = EXTI_Line12;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* Enable and set Button EXTI Interrupt to the lowest priority */
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/*
*********************************************************************************************************
* 函 数 名: TIM1_Configuration
* 功能说明: 配置TIM1
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void TIM1_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period =SendingDelay -1;
TIM_TimeBaseStructure.TIM_Prescaler = 72-1;
TIM_TimeBaseStructure.TIM_ClockDivision= 0;
TIM_TimeBaseStructure.TIM_CounterMode =TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure);
TIM_ClearITPendingBit(TIM1,TIM_FLAG_Update);
/*开启TIM1的中断 */
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);
/* 选择TIM2的中断通道 */
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
/*抢占式中断优先级设置为0 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
/*响应式中断优先级设置为0 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
/*使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
第二步:外部中断触发:
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line12)!= RESET)
{
/*Disable the Selected IRQ Channels -------------------------------------*/
EXTI->IMR &= ~1<<12; //屏蔽外部中断
if(Rx1Flag== 0)
{
Rx1Flag= 1;
}
TIM_Cmd(TIM1,DISABLE); //开启TIM1
TIM_SetCounter(TIM1,0);
TIM_Cmd(TIM1,ENABLE); //开启TIM1
}
}
第三步:定时器处理,接收一个字节数据
void TIM1_UP_IRQHandler(void)
{
staticuint8_t tmp;
staticuint8_t i,j;
if(TIM_GetFlagStatus(TIM1,TIM_FLAG_Update) != RESET)
{
if(Rx1Flag == 1)
{
Rx1Flag= 2;
i=0;
}
elseif(Rx1Flag == 2)
{
tmp= GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12);
if(tmp== 1)
dataReceived[receivedNum]|= (1 << i);
else dataReceived[receivedNum] &= ~(1 << i);
i++;
}
if(i>= 8)
{
Rx1Flag= 0;
i= 0;
receivedNum++;
EXTI->IMR |= 1<<12; //屏蔽外部中断
TIM_Cmd(TIM1,DISABLE); //关闭TIM1
}
TIM_ClearITPendingBit(TIM1,TIM_FLAG_Update);
}
}