48 STM32普通IO模拟usart串口
Posted Chasing_Chasing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了48 STM32普通IO模拟usart串口相关的知识,希望对你有一定的参考价值。
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!
以上是关于48 STM32普通IO模拟usart串口的主要内容,如果未能解决你的问题,请参考以下文章