1、总线相关的概念
总线:连接多个部件的信息传输线,是各部件共享的传输介质。
芯片内部总线:ARM内核和各种外设控制器连接的总线,比如AHB1 ~ AHB4, APB1 ~ APB4
芯片内部总线又可以划分为地址总线和数据总线。
芯片外部总线:处理器和外设之间连接的总线,比如:串口,IIC, SPI,USB, 485, CAN
2、串行通信和并行通信的区别
串行通信:指的是同一时刻只能收或发一个bit位信息,因此只用1根信号线即可。
并行通信:指的是同一时刻可以收或发多个bit位的信息,因此需要多根信号线才行。
3、单工、半双工、全双工
单工:要么收,要么发,只能做接收设备或者发送设备。
半双工:可以收,可以发,但是不能同时收发。
比如:IIC, 485, CAN, USB2.0
全双工:可以在同一时刻既接收,又发送。
比如:UART, SPI, USB3.0
4、同步通信和异步通信
同步通信:
一般情况下同步通信指的是通信双方根据同步时钟信号进行通信的方式。
比如通信双方有一个共同的时钟信号,大家根据时钟信号的变化进行通信。
一个时钟周期内,收发1个bit位的数据。
__ __ __ __
__| |__| |__| |__| |__
\ /
\ /
1个时钟周期
同步通信总线:IIC, SPI
异步通信:
是指数据传输速度匹配依赖于通信双方有自己独立的系统时钟,
大家约定好通信的速度。异步通信不需要同步信号,但是并不是说通信的过程不同步。
一个时钟周期收发一个bit位的数据。
异步通信:UART,485,CAN,USB
5、串口相关硬件的介绍
串口接口实物图及串口引脚的功能说明:
在实际开发中,串口常用的硬件连接:
串口常用的硬件连接主要使用3根线:RXD, TXD, GND
RXD : 接收数据线
TXD : 发送数据线
GND : 共地
6、在实际开发中串口常用的连接方式
在实际开发中串口使用的范围非常的广泛,
1> 可以用于通过串口打印调试信息到PC端的串口工具中;
2> 可以使用串口实现两个设备间的通信,
串口常用连接方式1:
串口常用连接方式2:
串口常用连接方式3:
7、UART串口总线的通信协议
串口:异步串行全双工总线
串口常用的通信的协议:8N1
8 : 8位数据位
N : 没有校验位
1 : 1个停止位
波特率:串口的传输的速率,每秒传输数据的bit位数,单位bps
常用的串口的波特率:115200bps , 9600bps
实验3 - UART串口实验
1、分析串口的电路图
2、分析芯片手册
2.1 分析2.5.2章节,确定GPIOB,GPIOG,UART4,RCC分别接到哪根总线上,以及外设控制器对应的寄存器的基地址
2.2 分析RCC章节,使能GPIOB,GPIOG,UART4外设控制器的时钟源
2.3 分析GPIO章节,设置PB2和PG11引脚为串口的复用功能
2.3.1 GPIOx_MODER寄存器
2.3.2 GPIOx_AFRL寄存器
2.3.3 GPIOx_AFRH寄存器
2.4 分析UART章节,设置串口的协议,串口的波特率,实现串口数据的收发
2.4.1 USART_CR1寄存器 ---> 串口控制寄存器1
2.4.2 USART_CR2寄存器 ---> 控制寄存器2
2.4.3 USART_BRR寄存器 ---> 串口波特率寄存器
BRR[15:4] = USARTDIV[15:4]
当over8 = 0; 16倍的采样率,
BRR[3:0] = USARTDIV[3:0]
当over8 = 1; 8倍的采样率
BRR[2:0] = USARTDIV[3:0] >> 1
BRR[3]必须保持清0的状态
假设usart_ker_ck_pres = 64MHz, 波特率为115200bps
则当采样率为16倍时,USARTDIV = 64 000 000 / 115200 = 555 = 0x22B
BRR = 0x22B
当采样率为8倍时,USARTDIV = 2 * 64 000 000 / 115200 = 1111 = 0x457
BRR = 0x453
2.4.4 USART_ISR寄存器 ----> 中断和状态寄存器
TXE[7] : 发送数据寄存器是否为空的标志位
读0:表示发送数据寄存器不为空,不可以向发送数据寄存器中写入下一个字节数据
读1:表示发送数据寄存器为空,可以向发送数据寄存器中写入下一个字节的数据
当发送数据寄存器中的数据被传送到发送移位寄存器时,硬件自动将TXE位置1.
当向发送数据寄存器中写入数据时,硬件自动将TXE位清0.
RXNE[5] : 读接收数据寄存器非空的标志位
读0:数据没有接收,不可以从接收数据寄存器中读取数据
读1:接收数据寄存器有数据,可以从接收数据寄存器中读取数据
当接收数据移位寄存器中的数据被传送到接收寄存器时,硬件自动将RXNE位置1,
当从接收数据寄存器中读取数据之后,硬件自动将RXNE位清0.
2.4.5 USART_RDR寄存器 ----> 接收数据寄存器
2.4.6 USART_TDR寄存器 ----> 发送数据寄存器
2.4.7 USART_PRESC寄存器 ----> 串口分频寄存器
3、编写串口的驱动代码
1. 串口的初始化
2. 发送一个字符的函数
3. 发送一个字符串的函数
4. 接收一个字符的函数
5. 接收一个字符串的函数
uart4.h
完成函数的声明
#ifndef _UART4_H_
#define _UART4_H_
#include "gpio.h"
void hal_uart_init();
void hal_sendchar(char data);
char hal_recvchar();
void hal_sendstring(char *data);
char *hal_recvstring();
#endif /*_UART4_H_*/
uart4.c
注:串口中只有USART4提供给用户用于数据收发的功能,所以无需像LED和KEY一样封装一个库,不同的外设多次调用库函数,这个函数封装好后就只会在mian.c里调用,所以直接操作寄存器,只封装一个hal库即可。
#include "../include/uart4.h"
#include "../common/include/stm32mp1xx_uart.h"
extern void delay_ms(unsigned int ms);
void hal_uart_init(){
//使能时钟
RCC->MP_AHB4ENSETR |=(1<<1);
RCC->MP_AHB4ENSETR |=(1<<6);
RCC->MP_APB1ENSETR |=(1<<16);
//GPIO
GPIOB->MODER &=~(0b11<<4);
GPIOB->MODER |=(0b10<<4);
GPIOG->MODER &=~(0b11<<22);
GPIOG->MODER |=(0b10<<22);
GPIOB->AFRL &=~(0b1111<<8);
GPIOB->AFRL |=(0b1000<<8);
GPIOG->AFRH &=~(0b1111<<12);
GPIOG->AFRH |=(0b0110<<12);
//UART
if(USART4->CR1 & 1){
delay_ms(2000);
USART4->CR1&=~1;//关闭串口
}
//数据位 0b00
USART4->CR1 &=~(1<<28);
USART4->CR1 &=~(1<<12);
//采样率 16倍
USART4->CR1 &=~(1<<15);
//校验位 无校验位
USART4->CR1 &=~(1<<10);
//停止位 1个
USART4->CR2 &=~(0b11<<12);
//波特率115200
USART4->BRR=0x22B;
//分频
USART4->PRESC &=~(0b1111);
//使能TE RE
USART4->CR1 &=~(0b11<<2);
USART4->CR1 |=(0b11<<2);
//使能串口
USART4->CR1 |=(1);
}
void hal_sendchar(const char data){
while (!(USART4->ISR &(1<<7)));
USART4->TDR=data;
if(data=='\n'){//此时\n已经发给串口调试助手,光标已经在下一行,但是不在行首
hal_sendchar('\r');
}
}
char hal_recvchar(){
char data;
while(!(USART4->ISR &(1<<5)));
data=(char)USART4->RDR;
return data;
}
void hal_sendstring(const char *data){
while(*data !='\0'){
hal_sendchar(*data);
data++;
}
}
char buff[50]={0};
#if 1
char *hal_recvstring(){
int i=0;
for(;i<49;i++){
buff[i]=hal_recvchar();
hal_sendchar(buff[i]);
if (buff[i] == '\r')//代表串口调试助手的enter,串口调试助手没有\0
{
break;
}
}
buff[i]='\0';
hal_sendchar('\n');
return buff;
}
#endif
main.c
#include "include/led.h"
#include "include/key.h"
#include "include/uart4.h"
void delay_ms(unsigned int ms){
int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 1800; j++)
;
}
int my_strcmp(const char* src,const char *dst){
int ret = 0;
while( !(ret = *(unsigned char*)src - *(unsigned char*)dst) && *dst)
{
src ++;
dst ++;
}
if( ret < 0) ret = -1;
else if(ret > 0) ret = 1;
return ret;
}
int main(){
led_init();
hal_uart_init();
//char data;
char *str;
while (1){
#if 0
data=hal_recvchar();
hal_sendchar(data+1);
#endif
#if 0
str=hal_recvstring();
hal_sendstring(str);
#endif
#if 1
//发送命令点灯
str=hal_recvstring();
if(my_strcmp(str,"led1on")==0){
led_open_off(LED1,led_on);
}else if(my_strcmp(str,"led2on")==0){
led_open_off(LED2,led_on);
}else if(my_strcmp(str,"led3on")==0){
led_open_off(LED3,led_on);
}else if(my_strcmp(str,"led1off")==0){
led_open_off(LED1,led_off);
}else if(my_strcmp(str,"led2off")==0){
led_open_off(LED2,led_off);
}else if(my_strcmp(str,"led3off")==0){
led_open_off(LED3,led_off);
}
#endif
}
}