使用的是cortex A7内核
【串口通信的工作原理】
本次实验使用的是uart4的串口,分别使用了uart4_tx和uart4_rx两个引脚。根据板子的原理图我们可以知道,他们分别对应着芯片的PG11和PB2
从引脚名字也可以知道使用了GPIO口,所以本次实验同样需要使用到GPIO模块。再加上uart模块和RCC使能模块。所以本次实验一共需要使用到三个模块。基本工作框图如下:
【RCC使能】
通过芯片手册可以知道RCC对GPIO使能使用的是AHB总线,对UART使能是通过APB1总线
再在芯片手册中直接查询这两个总线的寄存器即可知道需要对哪一位数据进行修改进行使能
具体使能操作情况如下
//使能GPIOB,GPIOG,UART4
RCC->MP_AHB4ENSETR |= (0x1 << 1);
RCC->MP_AHB4ENSETR |= (0x1 << 6);
RCC->MP_APB1ENSETR |= (0x1 << 16);
【GPIO功能复用】
通过查询芯片手册可知,只需要对GPIO的AFRL或者AFRH寄存器进行修改即可。
端口复用寄存器存在两个的原因:
对端口的复用需要4位,而每个寄存器只有32位,所以每个端口复用寄存器只能操作8个GPIO口。但是每个口都有16个,所以需要两个端口复用寄存器来分别控制低8位口和高8位口,AFRL就操作0-7这8个,AFRH就操作8-15这8个口
所以本次使用的两个口PB2和PG11分别要修改的寄存器为GPIOB_AFRL和AFRHG_AFRH寄存器,再根据寄存器的结构便可以知道要修改的是哪几位。
现在我们知道了需要设置哪几位,但是设置成什么数字并不知道,继续查询芯片手册,我们需要把它复用成uart4的模式。
从上面这两张表就可以知道,只需要把PG11对应的几位设置成AF6,把PB2对应的几位设置成AF8即可,至此GPIO口的uart4复用功能就实现了。
【UART模块设置】
对uart模块的设置相对繁琐,具体分为以下几步:
1、设置数据长度为8
2、设置16位过采样
3、设置不需要校验位
4、设置1位停止位
5、设置不分频
6、设置波特率
7、对发送器、接收器、串口分别进行使能
对上面三张图的寄存器的对应位进行修改即可实现上述七个步骤。
值得一提的是在对BRR进行波特率设置的时候可以通过直接赋值的方式直接设置波特率,但是这个需要设置的值收到过采样频率的影响,由于8倍的过采样频率过于复杂,本次实验采用16倍采样频率,具体公式如下:
设置值 = 模块工作频率(HZ)/ 要设置的波特率(bit/s)
对uart模块进行读写操作的步骤
1、判断是否有数据需要读取或写入(对USART_ISR寄存器相应的位进行判断)
2、将需要读取的数据放入读取寄存器中(数据会存储在USART_RDR寄存器等待用户读取)
3、将需要发送的数据放入写寄存器中(将数据直接写入USART_TDR寄存器即可发送数据)
【代码】
在了解了以上串口工作原理之后就可以开始编写代码了。本次代码结合了昨天的led模块,实现了串口对灯的控制。具体功能见视频:
https://blink.csdn.net/details/1666295?spm=1001.2014.3001.5501
具体代码如下:
main.c
#include "uart4.h"
#include "led.h"
void delay(int ms)
{
int i,j;
for(i=ms;i>0;i--)
{
for(j=2000;j>0;j--)
{}
}
}
int main()
{
uart4_init();
led_init();
// char i;
char buf[30];
while(1)
{
// i = getchar();
// putchar(i + 1);
gets(buf);
if(strcmp(buf,"led1 on")==0)
{
led1_on();
puts("exec success\r\n");
}
else if(strcmp(buf,"led1 off")==0)
{
led1_off();
puts("exec success\r\n");
}
else if (strcmp(buf, "led2 on") == 0)
{
led2_on();
puts("exec success\r\n");
}
else if (strcmp(buf, "led2 off") == 0)
{
led2_off();
puts("exec success\r\n");
}
else if (strcmp(buf, "led3 on") == 0)
{
led3_on();
puts("exec success\r\n");
}
else if (strcmp(buf, "led3 off") == 0)
{
led3_off();
puts("exec success\r\n");
}
else
{
puts("error cmd\r\n");
}
}
return 0;
}
uart4.c
#include "uart4.h"
void uart4_init()
{
//使能GPIOB,GPIOG,UART4
RCC->MP_AHB4ENSETR |= (0x1 << 1);
RCC->MP_AHB4ENSETR |= (0x1 << 6);
RCC->MP_APB1ENSETR |= (0x1 << 16);
//设置pb2和pg11的管脚复用pb2/pg11
GPIOB->MODER &= ~(0x3 << 4);
GPIOG->MODER &= ~(0x3 << 22);
GPIOB->MODER |= (0x1 << 5);
GPIOG->MODER |= (0x3 << 23);
GPIOB->AFRL &= ~(0xf << 8);
GPIOB->AFRL |= (0x1 << 11);
GPIOG->AFRH &= ~(0xf << 12);
GPIOG->AFRH |= (0x3 << 13);
//设置串口不使能,ue=0
USART4->CR1 &= (~(0x1));
//设置8位数据位
USART4->CR1 &= (~(0x1 << 12));
USART4->CR1 &= (~(0x1 << 28));
//设置没有奇偶校验位
USART4->CR1 &= (~(0x1 << 10));
//设置1位停止位
USART4->CR2 &= (~(0x3 << 12));
//设置16倍过采样
USART4->CR1 &= (~(0x1 << 15));
//设置时钟不分频
USART4->PRESC &= (~(0xf));
//设置波特率为115200
USART4->BRR = 0x22b;
//使能发送器
USART4->CR1 |= (0x1 << 3);
//使能接收器
USART4->CR1 |= (0x1 << 2);
//使能uart4
USART4->CR1 |= (0x1);
}
void putchar(char a)
{
//先判断发送寄存器是否为空
while(!(USART4->ISR & 0x1<<7)); //取出第七位的数据,如果是1表示 当前寄存器为空
USART4->TDR = a;
//不为空阻塞等待
//为空向寄存器中写入数据
//写入完成判断发送是否完成,未完成阻塞,完成函数结束
while (!(USART4->ISR & 0x1 << 6));
}
char getchar()
{
//判断接收寄存器中是否有数据有数据读取,没数据阻塞
//读取成功返回该字符
char a;
while (!(USART4->ISR & 0x1 << 5));
a = USART4->RDR;
return a;
}
void puts(char *s)
{
while(*s)
{
putchar(*s);
s++;
}
putchar('\r');
putchar('\n');
}
void gets(char *s)
{
while(1)
{
*s = getchar();
putchar(*s);
if (*s == '\r')
{
break;
}
s++;
}
*s = '\0';
}
int strcmp(char *src,char *dest)
{
char *p = src, *q = dest;
while(*p==*q&&*p!='\0'&&*q!='\0')
{
p++;
q++;
}
if(*p>*q)
{
return 1;
}
else if(*p<*q)
{
return -1;
}
else
{
return 0;
}
}