1.引言
本次实验的原因是因为最近接触到一款单片机,只有一个串口,但项目中要用到至少2个串口,所以一个串口好少啊,没办法,只能另寻出路,通过普通IO来模拟usart串口了。然后,经过一番资料搜索,代码搬运,终于成功,组合出一个io模拟串口的模块,该模块成功实现了9600-8-N的串口数据收发,为方便记忆以及学习,特此记录!
2.普通IO模拟串口原理
普通io模拟串口,也需要严格的遵循串口协议规则,具体的规则可百度一下。
当波特率是115200时,发送1bit数据需要 1/115200 = 8.68us;所以,根据协议每一位数据传输电平持续8.68us
当波特率是9600时,发送1bit数据需要 1/9600= 104us;所以,根据协议每一位数据传输电平持续104us
这里要注意,在115200的时候,因为stm32默认的delay_us的不精准,会导致模拟串口的数据收发错误,在此可以建议用定时器延时,以达到精准操作。
在接收的地方,通过中断来触发启动,然后启动一个定时器,根据波特率的电平持续时间,在电平的中间去检测,是高电平还是低电平,从而确定bit的值。所以,检测到下降沿后延时一下,再启动定时器,定时去判断IO的高低。
3.代码实现
本次使用stm32f103c8t6实验板,所用到的硬件资源如下:
1.GPIO两个
2.外部中断1路
3.定时器两个
io模拟串口头文件:iousart.h
-
#ifndef __IOUSART_H_
-
#define __IOUSART_H_
-
-
#include "stm32f10x.h"
-
-
//对应波特率的1个电平持续时间
-
//(1/9600) = 104us
-
#define IO_USART_SENDDELAY_TIME 104
-
-
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 iouart1_SendByte(uint8_t uByte);
-
void iouart1_init(void);
-
-
#endif /* __LED_H */
io模拟串口源文件:iousart.c
-
#include <stdio.h>
-
#include "iousart.h"
-
#include "delay.h"
-
-
//
-
//io模拟串口-9600-8-N
-
//定时器2+定时器4+外部中断1路+2个GPIO
-
//
-
u8 recvData =
0;
-
u8 recvStat = COM_STOP_BIT;
-
//
-
//用以模拟串口延时
-
void TIM2_Int_Init(void)
-
{
-
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
-
-
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
-
-
TIM_TimeBaseStructure.TIM_Period =
65535 -
1;
-
TIM_TimeBaseStructure.TIM_Prescaler =
72 -
1;
//设置了用来作为 TIM3 时钟频率除数的预分频值
-
TIM_TimeBaseStructure.TIM_ClockDivision =
0;
//时钟分割为0
-
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//TIM3 向上计数模式
-
-
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
-
TIM_Cmd(TIM2,ENABLE);
-
}
-
-
//用以模拟io串口数据接收计时
-
void TIM4_Int_Init(void)
-
{
-
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
-
NVIC_InitTypeDef NVIC_InitStructure;
-
-
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
//时钟使能
-
-
//定时器TIM4初始化
-
TIM_TimeBaseStructure.TIM_Period = IO_USART_SENDDELAY_TIME;
//设置在下一个更新事件装入活动的自动重装载寄存器周期的值
-
TIM_TimeBaseStructure.TIM_Prescaler =
72 -
1;
//设置用来作为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 );
//使能指定的TIM3中断,允许更新中断
-
-
//中断优先级NVIC设置
-
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
//TIM4中断
-
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =
3;
//先占优先级1级
-
NVIC_InitStructure.NVIC_IRQChannelSubPriority =
1;
//从优先级1级
-
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//IRQ通道被使能
-
NVIC_Init(&NVIC_InitStructure);
//初始化NVIC寄存器
-
}
-
-
//模拟串口1初始化
-
void iouart1_init(void)
-
{
-
GPIO_InitTypeDef GPIO_InitStructure;
-
NVIC_InitTypeDef NVIC_InitStructure;
-
EXTI_InitTypeDef EXTI_InitStruct;
-
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);
//使能PB,PC端口时钟
-
-
//SoftWare Serial TXD
-
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
-
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_8);
-
-
-
//SoftWare Serial RXD
-
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
-
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
-
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
-
GPIO_Init(GPIOB, &GPIO_InitStructure);
-
-
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9);
-
EXTI_InitStruct.EXTI_Line = EXTI_Line9;
-
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= EXTI9_5_IRQn ;
-
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=
3;
-
NVIC_InitStructure.NVIC_IRQChannelSubPriority =
0;
-
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
-
NVIC_Init(&NVIC_InitStructure);
-
-
TIM2_Int_Init();
-
TIM4_Int_Init();
-
}
-
-
-
void iouart1_TXD(uint8_t option)
-
{
-
if(
1 == option)
-
{
-
GPIO_SetBits(GPIOB, GPIO_Pin_8);
-
}
-
else
-
{
-
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
-
}
-
}
-
-
uint8_t iouart1_RXD(void)
-
{
-
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_9);
-
}
-
-
/***************************************
-
* 函 数 名: Delay_Ms
-
* 功能说明: 延时
-
* 形 参:nTime,单位为uS
-
* 返 回 值: 无
-
****************************************/
-
void iouart1_delayUs(volatile u32 nTime)
-
{
-
-
u16 tmp;
-
-
tmp = TIM_GetCounter(TIM2);
//获得 TIM3 计数器的值
-
-
if(tmp + nTime <=
65535)
-
while( (TIM_GetCounter(TIM2) - tmp) < nTime );
-
else
-
{
-
TIM_SetCounter(TIM2,
0);
//设置 TIM3 计数器寄存器值为0
-
while( TIM_GetCounter(TIM2) < nTime );
-
}
-
}
-
-
/*****************************************
-
* 函 数 名: iouart1_SendByte
-
* 功能说明: 模拟串口发送一字节数据
-
* 形 参:无
-
* 返 回 值: 无
-
******************************************/
-
void iouart1_SendByte(u8 datatoSend)
-
{
-
u8 i, tmp;
-
-
// 开始位
-
iouart1_TXD(
0);
//将TXD的引脚的电平置低
-
iouart1_delayUs(IO_USART_SENDDELAY_TIME);
-
-
for(i =
0; i <
8; i++)
-
{
-
tmp = (datatoSend >> i) &
0x01;
-
-
if(tmp ==
0)
-
{
-
iouart1_TXD(
0);
-
iouart1_delayUs(IO_USART_SENDDELAY_TIME);
//0
-
}
-
else
-
{
-
iouart1_TXD(
1);
-
iouart1_delayUs(IO_USART_SENDDELAY_TIME);
//1
-
}
-
}
-
-
// 结束位
-
iouart1_TXD(
1);
//将TXD的引脚的电平置高
-
iouart1_delayUs(IO_USART_SENDDELAY_TIME);
-
}
-
-
-
void EXTI9_5_IRQHandler(void)
-
{
-
if(EXTI_GetITStatus(EXTI_Line9) != RESET)
-
{
-
if(iouart1_RXD() ==
0)
-
{
-
if(recvStat == COM_STOP_BIT)
-
{
-
recvStat = COM_START_BIT;
-
//这里要延时一下
-
iouart1_delayUs(
50);
-
TIM_Cmd(TIM4, ENABLE);
-
}
-
}
-
EXTI_ClearITPendingBit(EXTI_Line9);
-
}
-
}
-
-
-
void TIM4_IRQHandler(void)
-
{
-
if(TIM_GetITStatus(TIM4, TIM_FLAG_Update) != RESET)
-
{
-
TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
-
recvStat++;
-
if(recvStat == COM_STOP_BIT)
-
{
-
TIM_Cmd(TIM4, DISABLE);
-
//到这里就接收到完整的一个字节数据
-
recvData = recvData;
-
-
-
return;
-
}
-
if(iouart1_RXD())
-
{
-
recvData |= (
1 << (recvStat -
1));
-
}
else{
-
recvData &= ~(
1 << (recvStat -
1));
-
}
-
}
-
}
-
以上代码,测试过是可以的。over!
转载:https://blog.csdn.net/Chasing_Chasing/article/details/116458804
查看评论