之前做项目的时候遇到了一个问题,当把MCU本身的串口资源用完的时候,却还需要使用多几个串口,又不想使用几个MCU解决这个问题。那么模拟串口是解决这个问题的一种方法。
下图是我对串口通信时序图的个人理解:
好对串口有了大致的了解后我们将理解的捋一下编写程序的思路去实现串口的通信:
1、 使能PA时钟。
RCC->RCC_APB2ENR|=1<<2;
2、 我们是IO模拟的串口传输实验,所以要配置两个IO口。因为数据传输本质就是0和1的传输,所以我这里将PA5设置为推挽输出(0、1变换),PA6设置为上拉输入(高电平时停止状态,只有低电平才会触发数据传输)。
GPIOA->GPIO_CRL&=0xFF00FFFF;
GPIOA->GPIO_CRL|=0x00300000;
GPIOA->GPIO_CRL|=0X00080000;
GPIOA->GPIO_ODR|=1<<5;
GPIOA->GPIO_ODR|=0x30;
3、 设置发送数据的波特率,例如我这里用的波特率是9600,那么节拍时间是104us,我编写的例程没用写太多的波特率对应节拍值。
我们可以写出一一对应的节拍值,如:
#define BuadRate1200 832
#define BuadRate2400 416
#define BuadRate4800 208
#define BuadRate9600 104
.
.
.
4、 编写发送一个字节数据函数。(开始信号->节拍时间->第零位数据->节拍时间->第一位数据->节拍时间…第七位数据->节拍时间->停止位信号->节拍时间)。
void TX_Byte(u8 data)
{
int i=0;
TX_Low();
delay_us(BuadRate);
for(i=0;i<8;i++)
{
if(data & 0x01)
{
TX_Hight();
}
else
{
TX_Low();
}
delay_us(BuadRate);
data>>=1;
}
TX_Hight();
delay_us(BuadRate);
}
5、 编写发送一个字符串数据的函数。
void TX_String(u8 *str)
{
while(*str!=0)
{
TX_Byte(*str);
str++;
}
}
6、 编写接收数据的函数;
① 一些说明:
接收数据和发送数据是同一个原理的,只不过我们不可能将接收数据放到while循环上面让它一直重复查询等待接收数据信号,不然整个MUC都为它服务了,其它事啥都不用干了。
那我们用什么来识别接收数据信号呢?我们可以使用中断来触发,当高电平信号跳变为低电平信号时中断就触发,接收数据就开始启动,这样就不会一直占用MCU的资源。
这里我还说一下,我接收数据还使用了定时器作为节拍时间,很多人有疑问,为什么要用定时器做节拍时间呢,不是已经有delay延时函数来做节拍时间了吗? 因为delay延时函数的原理是让MCU在那空运行,等于啥事也没干。而定时器时间精准、有条序的
启动、接收、结束,充分利用MCU资源。降低了MCU资源占用率(比起寄存器那是没法比的)。
我这里用图描述一下定时器工作:
② 中断初始化配置和定时器初始化配置(这里中断我就不多说明了,我上一篇博客有详细说明)
Ⅰ、 中断:
ex_nvic_config(0,4,1);
NVIC_Init(2,2,10,2);
Ⅱ、 定时器:
RCC->RCC_APB1ENR|=1<<1;
TIME3->TIME_PSC=psc;
TIME3->TIME_ARR=arr;
TIME3->TIME_DIER|=1<<0;
TIME3->TIME_CR1&=~(1<<0);
NVIC_Init(1,3,29,2);
③ 原理:
接收起始信号触发中断(打开定时器)->节拍时间(delay)->定时器打开开始计数->运行接收第0个数据位的程序->定时器计数值到达复位->定时器重新开始计数->运行接收第1个数据位的程序->定时器计数值到达复位…->定时器重新开始计数->运行接收第7个数据位的程序->定时器计数值到达复位->定时器重新开始计数->结束接收程序,停止定时器等待下一次数据的接收并将接收的一个字节数据保存定义好的缓冲区中->定时器计数值到达复位。
void EXTI4_IRQHandler(void)
{
if( (EXTI->PR&(1<<4)) != 0 )
{
if(!(GPIOA->GPIO_IDR&(1<<4)))
{
if(receiverstate==STOP_BIT)
{
receiverstate = START_BIT;
delay_us(BuadRate/2);
TIME3->TIME_CR1|=1<<0;
}
}
}
EXTI->PR|=1<<4;
}
void TIM3_IRQHandler(void)
{
if(TIME3->TIME_SR&0x0001)
{
TIME3->TIME_SR&=~(1<<0);
receiverstate+=1;
if(receiverstate == STOP_BIT)
{
TIME3->TIME_CR1&=~(1<<0);
USART_BUF[len_buf] = receiverdata;
len_buf+=1;
return;
}
if((GPIOA->GPIO_IDR&(1<<4)))
{
receiverdata |= (1<<(receiverstate-1));
}
else
{
receiverdata &= ~(1<<(receiverstate-1));
}
}
}
串口调试窗口:
完整的程序:
TIME.h
#ifndef __TIME_H
#define __TIME_H
#include "RCC.h"
#define TIME2_BASE (0x40000000)
#define TIME3_BASE (0x40000400)
#define TIME4_BASE (0x40000800)
#define TIME5_BASE (0x40000C00)
typedef struct
{
volatile unsigned int TIME_CR1;
volatile unsigned int TIME_CR2;
volatile unsigned int TIME_SMCR;
volatile unsigned int TIME_DIER;
volatile unsigned int TIME_SR;
volatile unsigned int TIME_EGR;
volatile unsigned int TIME_CCMR1;
volatile unsigned int TIME_CCMR2;
volatile unsigned int TIME_CCER;
volatile unsigned int TIME_CNT;
volatile unsigned int TIME_PSC;
volatile unsigned int TIME_ARR;
unsigned int SAVE1;
volatile unsigned int TIME_CCR1;
volatile unsigned int TIME_CCR2;
volatile unsigned int TIME_CCR3;
volatile unsigned int TIME_CCR4;
unsigned int SAVE2;
volatile unsigned int TIME_DCR;
volatile unsigned int TMIE_DMAR;
}TIME_Type;
#define TIME2 ((TIME_Type*) TMIE2_BASE)
#define TIME3 ((TIME_Type*) TIME3_BASE)
#define TIME4 ((TIME_Type*) TMIE4_BASE)
#define TIME5 ((TIME_Type*) TMIE5_BASE)
void TIME3_init(u16 arr,u16 psc);
#endif
TIME.c
#include "TIME.h"
#include "nvic.h"
void TIME3_init(u16 arr,u16 psc)
{
RCC->RCC_APB1ENR|=1<<1;
TIME3->TIME_PSC=psc;
TIME3->TIME_ARR=arr;
TIME3->TIME_CR1&=~(1<<4);
TIME3->TIME_DIER|=1<<0;
TIME3->TIME_CR1&=~(1<<0);
NVIC_Init(1,3,29,2);
}
IO_SIMULATE_USART.h
IO_SIMULATE_USART.h
#ifndef __IO_SIMULATE_USART_H
#define __IO_SIMULATE_USART_H
#include "RCC.h"
#include "gpio.h"
#include "delay.h"
#include "exti.h"
enum{
START_BIT,
D0_BIT,
D1_BIT,
D2_BIT,
D3_BIT,
D4_BIT,
D5_BIT,
D6_BIT,
D7_BIT,
STOP_BIT,
};
void USART_IO_init(void);
void TX_Byte(u8 data);
void TX_String(u8 *str);
extern u8 USART_BUF[11];
extern u8 len_buf;
#endif
IO_SIMULATE_USART.c
#include "IO_SIMULATE_USART.h"
#include "TIME.h"
#define BuadRate 104
#define TX_Hight() {GPIOA->GPIO_ODR|=1<<5;}
#define TX_Low() {GPIOA->GPIO_ODR&=~(1<<5);}
u8 USART_BUF[11];
u8 len_buf = 0;
u8 receiverstate = STOP_BIT;
u8 receiverdata = 0;
void USART_IO_init(void)
{
RCC->RCC_APB2ENR|=1<<2;
GPIOA->GPIO_CRL&=0xFF00FFFF;
GPIOA->GPIO_CRL|=0x00300000;
GPIOA->GPIO_CRL|=0X00080000;
GPIOA->GPIO_ODR|=1<<5;
GPIOA->GPIO_ODR|=0x30;
ex_nvic_config(0,4,1);
NVIC_Init(2,2,10,2);
}
void TX_Byte(u8 data)
{
int i=0;
TX_Low();
delay_us(BuadRate);
for(i=0;i<8;i++)
{
if(data & 0x01)
{
TX_Hight();
}
else
{
TX_Low();
}
delay_us(BuadRate);
data>>=1;
}
TX_Hight();
delay_us(BuadRate);
}
void TX_String(u8 *str)
{
while(*str!=0)
{
TX_Byte(*str);
str++;
}
}
void EXTI4_IRQHandler(void)
{
if( (EXTI->PR&(1<<4)) != 0 )
{
if(!(GPIOA->GPIO_IDR&(1<<4)))
{
if(receiverstate==STOP_BIT)
{
receiverstate = START_BIT;
delay_us(BuadRate/2);
TIME3->TIME_CR1|=1<<0;
}
}
}
EXTI->PR|=1<<4;
}
void TIM3_IRQHandler(void)
{
if(TIME3->TIME_SR&0x0001)
{
TIME3->TIME_SR&=~(1<<0);
receiverstate+=1;
if(receiverstate == STOP_BIT)
{
TIME3->TIME_CR1&=~(1<<0);
USART_BUF[len_buf] = receiverdata;
len_buf+=1;
return;
}
if((GPIOA->GPIO_IDR&(1<<4)))
{
receiverdata |= (1<<(receiverstate-1));
}
else
{
receiverdata &= ~(1<<(receiverstate-1));
}
}
}
test.c
#include "gpio.h"
#include "clock.h"
#include "nvic.h"
#include "exti.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
#include "IO_SIMULATE_USART.h"
#include "TIME.h"
int main(void)
{
RCC_Config(9);
delay_init(72);
exti_init();
USART_IO_init();
TIME3_init(104,71);
while(1)
{
if(len_buf>0)
{
TX_String(USART_BUF);
len_buf = 0;
}
}
}
总结
谢谢大家的关注,希望给大家有所帮助。本人技术有限,写的哪里不好的地方大家多多包涵,欢迎大家提出问题,给予纠正。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)