cortex-A7核通過UART串口實現數據的收發
1.簡單理解總線
總線即為各個部位之間通信的一種媒介,芯片內部的總線控制的是內部各個控制器和核之間的通信,例如SOC通過AHB4總線可以和RCC控制器進行通信,芯片外部的總線控制的是芯片外部各個外設之間的通信,例如SOC通過UART串口控制TARGET(目標外設)。
2.簡單理解串口的連接方式
a.直連方式
一共有三根線:RXD(接收數據線)/TXD(發送數據線)/GND(地線)。
b.usb轉串口連接方式
SOC----->TTL電平,高電平:+5v,低電平:0v。
串口電平------>RS232電平,高電平:+15v ~ +3V,低電平:-15v ~ -3v。
c.st-link仿真器連接方式
ST-LINK仿真器能夠完成USB口和串口之間的轉換,在ST-LINK仿真器內部有一個芯片(STM32F103),這個芯片能夠完成USB口和串口之間的轉換,在STM32F103內部固化一段程序,這段程序不開源,這段程序可以完成USB口和串口之間的轉換。
3.串口通信協議
a.串口配置信息
串口采用串行通信方式,因為收發數據時,一個時鐘周期只能收發一位數據,波特率也叫bps(比特率),單位是二進制/秒,即為串口通信時,傳輸的速率,1s鐘能夠收發數據的位數,而上圖中115200bps表示1秒鐘可以收發115200bit數據,波特率的倒數為傳輸每位數據需要的時間,8N1代表八位數據位,沒有奇偶校驗位,一位停止位。
b.串口的通信協議
空閑態:UART總線不在傳輸數據的時候,總線處于空閑狀態,為高電平
起始信號:開始信號,串口通信的開始標志位
數據位:串口發送數據,先發低位,再發高位
奇偶校驗位:奇校驗即為數據位和校驗位1的個數為奇數,偶校驗則反之
停止信號:發送數據結束,回到高電平狀態,校準時鐘信號
校準時鐘信號的目的是因為串口采用的異步通信方式,雙方都有自己獨立的時鐘源,雖然設置了雙方的時鐘源保持一致,但是在發送數據時,每發送一幀數據時都會產生誤差,越往后,發送的數據越多,累計誤差越大,所以每發送一幀數據之后,需要校準時鐘信號。
4.電路圖分析
分析電路圖可得知UART4_RX對應的PB2引腳,UART4_TX對應的是PG11引腳。
5.框圖分析
通過框圖分析可知,需要分析芯片手冊RCC,GPIO,UART章節
分析思路:設置GPIOG/GPIOB引腳為復用功能
設置UART4串口初始化
實現數據的收發
6.RCC章節分析
a.使能GPIO控制器
使GPIO控制器使能需要通過AHB4總線,RCC控制GPIO則需要通過RCC_MP_AHB4ENSETR寄存器來使對應的GPIO端口使能,通過2.5.2章節可知基地址為0x50000000,而RCC_MP_AHB4ENSETR寄存器的偏移地址為0xA28,所以RCC_MP_AHB4ENSETR寄存器的地址基地址加偏移地址=0x50000A28,而要使GPIOB和GPIOG控制器使能需要使RCC_MP_AHB4ENSETR寄存器對應的地址的第一位和第六位設置為1。
b.使能UART4控制器
通過RCC_MP_APB1ENSETR寄存器設置UART4控制器使能,確定RCC_MP_APB1ENSETR寄存器的地址為0x50000A00,使能UART4控制器則需要設置RCC_MP_APB1ENSETR寄存器對應的地址內容第十六位為1。
7.GPIO章節分析
a.GPIOx_MODER寄存器分析
通過GPIOx_MODER寄存器設置PB2/PG11引腳為復用功能,確定GPIOB地址為0x50003000,GPIOG地址為0x50008000,通過設置GPIOB_MODER的第5位到第4位為10來使PB2引腳為復用功能和GPIOG_MODER的第23位到第22位為10來使PG11引腳為復用功能。
b.GPIOx_AFRL寄存器分析
通過GPIOB_AFRL寄存器設置PB2引腳為復用功能UART4_RX,確定GPIOB_AFRL地址為0x50003020,通過修改GPIOB_AFRL寄存器的第11位到第8位位1000來設置PB2引腳為復用功能UART4_RX。
c.GPIOx_AFRH寄存器分析
通過GPIOG_AFRH寄存器設置PG11引腳為復用功能UART4_TX,確定GPIOG_AFRH地址為0x50008024,通過修改GPIOG_AFRH寄存器的第15位到第12位為0110來設置PG11引腳為復用功能UART4_TX。
為何有GPIOx_AFRL和GPIOx_AFRH兩個復用功能寄存器,因為這個寄存器每四位管理一個引腳,一個寄存器最多管理8個寄存器,但是GPIO每組一共有16個引腳,所以需要兩個這樣的寄存器。
7.UART章節分析
a.UART框圖分析
通過以上分析可知:
USART_CR1:設置數據位寬度,以及將相應位進行使能
USART_CR2:設置停止位
USART_BRR:設置波特率---->設置的采樣率有關
USART_RDR :設置接收數據寄存器
USART_TDR :設置發送數據寄存器
USART_ISR:設置狀態寄存器
USART_PRESC :設置時鐘分頻器
b.USART_CR1寄存器分析
確定USART_CR1寄存器的地址為基地址+偏移地址=0x40010000+0x00-0x40010000
設置串口為八位數據位需要將USART_CR1寄存器的第28位和第12位設置為0,設置串口16倍采樣率需要將USART_CR1寄存器的第15位設置為0,設置串口無奇偶校驗位需要將USART_CR1寄存器的第10位設置為0,設置串口發送寄存器使能需要將USART_CR1寄存器的第3位設置為1,設置串口接受寄存器使能需要將USART_CR1寄存器的第2位設置為1,設置串口接受使能需要將USART_CR1寄存器的第0位設置為1。
c.USART_CR2寄存器分析
確定USART_CR2寄存器地址為基地址+偏移地址=0x40010000+0x04=0x40010004
通過USART_CR2寄存器分析得知設置串口一位停止位需要將USART_CR2寄存器的第13位到第12位設置為00。
d.USART_BRR寄存器分析
確定USART_BRR寄存器地址為基地址+偏移地址=0x40010000+0x0C=0x4001000C
設置串口波特率為115200與采樣率有關,系統提供的串口時鐘源為64MHZ,BRR=64MHZ/115200=0x22b,所以設置 確定USART_BRR寄存器內容為0x22b。
e.USART_RDR寄存器分析
f.USART_TDR寄存器分析
g.USART_PRESC寄存器分析
h.USART_ISR寄存器分析
確定USART_ISR寄存器地址為基地址+偏移地址=0x40010000+0x1C=0x4001001C。
USART_ISR寄存器的第7位為判斷發送寄存器是否為空,這位只可以讀。特點:如果發送數據寄存器為空,才可以發送下一個字節的數據(該位為1),如果發送寄存器為滿,則需要等待發送數據寄存器為空(該位為0)。
USART_ISR寄存器的第6位為判斷一幀數據是否發送完成,這位只可以讀。特點:如果發送數據完成之后,才可以發送下一幀數據 ,讀0:發送數據沒有完成,需要等待 讀1:發送數據完成,可以發送下一幀數據 。
USART_ISR寄存器的第5位為判斷接收數據寄存器是否有數據可讀,這位只可以讀 。特點:接收數據寄存器有數據,才可以讀數據 ,讀0:沒有接收到數據,需要等待 讀1:接收到數據,可以讀這個數據。
8.代碼編寫(實現接受發送一個字符串現象)
a.uart4.h文件
#ifndef __UART4_H__
#define __UART4_H__
#include "stm32mp1xx_uart.h"
#include "stm32mp1xx_rcc.h"
#include "stm32mp1xx_gpio.h"
//1.初始化函數
void uart4_init();
//2.發送一個字符
void put_char(const char str);
//3.發送一個字符串
void put_string(const char* str);
//4.接收一個字符
char get_char();
//5.接收一個字符串
char* get_string();
#endif
b.uart4.c文件
#include "uart4.h"
extern void delay_ms(int ms);
//1.初始化函數
void uart4_init()
{
/*******RCC章節初始化******/
//1.使能GPIOB控制器 MP_AHB4ENSETR[1] = 1
RCC->MP_AHB4ENSETR |= (0x1 << 1);
//2.使能GPIOG控制器 MP_AHB4ENSETR[6] = 1
RCC->MP_AHB4ENSETR |= (0x1 << 6);
//3.使能UART4控制器 MP_APB1ENSETR[16] = 1
RCC->MP_APB1ENSETR |= (0x1 << 16);
/*******GPIO章節初始化******/
//PB2---->UART4_Rx
//PG11----->UART4_Tx
//1.設置PB2引腳為復用功能 MODER[5:4] = 10
GPIOB->MODER &= (~(0x3 << 4));
GPIOB->MODER |= (0x1 << 5);
//2.設置PB2引腳復用功能為UART4_Rx AFRL[11:8] = 1000
GPIOB->AFRL &= (~(0xf << 8));
GPIOB->AFRL |= (0x1 << 11);
//3.設置PG11引腳為復用功能 MODER[23:22] = 10
GPIOG->MODER &= (~(0x3 << 22));
GPIOG->MODER |= (0x1 << 23);
//4.設置PG11引腳復用功能為UART4_Tx AFRH[15:12] = 0110
GPIOG->AFRH &= (~(0xf << 12));
GPIOG->AFRH |= (0x3 << 13);
/*******UART章節初始化******/
if(USART4->CR1 & (0x1 << 0))
{
delay_ms(500);
//將UE為禁止 CR1[0] = 0
USART4->CR1 &= (~(0x1 << 0));
}
//1.串口初始化 8位數據位 無奇偶校驗位 CR1[28][12]=00 CR1[10]=0
USART4->CR1 &= (~(0x1 << 28));
USART4->CR1 &= (~(0x1 << 12));
USART4->CR1 &= (~(0x1 << 10));
//2.設置串口一位停止位 CR2[13:12] = 00
USART4->CR2 &= (~(0x3 << 12));
//3.設置串口16倍采樣率 CR1[15] = 0
USART4->CR1 &= (~(0x1 << 15));
//4.設置串口不分頻 PRESC[3:0] = 0000
USART4->PRESC &= (~(0xf << 0));
//5.設置串口波特率115200 BRR = 0x22b
USART4->BRR = 0x22b;
//6.設置串口發送器使能 CR1[3] = 1
USART4->CR1 |= (0x1 << 3);
//7.設置串口接收器使能 CR1[2] = 1
USART4->CR1 |= (0x1 << 2);
//8.設置串口使能 CR1[0] = 1
USART4->CR1 |= (0x1 << 0);
}
//2.發送一個字符
void put_char(const char str)
{
//1.判斷發送數據寄存器是否有數據 ISR[7]
//讀0:發送數據寄存器滿,需要等待
//讀1:發送數據寄存器為空,才可以發送下一個字節數據
while(!(USART4->ISR & (0x1 << 7)));
//2.將要發送的字符,寫入到發送數據寄存器中
USART4->TDR = str;
//3.判斷發送數據是否發送完成
//讀0:發送數據沒有完成,需要等待
//讀1:發送數據完成,可以發送下一幀數據
while(!(USART4->ISR & (0x1 << 6)));
}
//3.發送一個字符串
void put_string(const char* str)
{
//判斷是否為''
//一個一個字符的進行發送
while(*str)
{
put_char(*str++);
}
put_char('n');
put_char('r');
}
//4.接收一個字符
char get_char()
{
char ch;
//1.判斷接收寄存器是否有數據可讀 ISR[5]
//讀0:沒有數據可讀,需要等待
//讀1:有數據可讀
while(!(USART4->ISR & (0x1 << 5)));
//2.將接收數據寄存器中的內容讀出來
ch = USART4->RDR;
return ch;
}
char buffer[50] = {0};
//5.接收一個字符串
char* get_string()
{
unsigned int i;
//1.循環進行接收
//2.循環實現:接收一個字符之后,發送一個字符
//當鍵盤回車建按下之后,代表字符串接收結束'r'
for(i=0;i<49;i++)
{
buffer[i] = get_char();
put_char(buffer[i]);
if(buffer[i] == 'r')
break;
}
//3.字符串補''
buffer[i] = '';
put_char('n');
return buffer;
}
c.main.c文件
#include "uart4.h"
extern void printf(const char *fmt, ...);
void delay_ms(int ms)
{
int i,j;
for(i = 0; i < ms;i++)
for (j = 0; j < 1800; j++);
}
int main()
{
//1.調用初始化函數
uart4_init();
//2.發送字符串
put_string("uart4 test!!!!");
while(1)
{
//put_char(get_char()+1);
put_string(get_string());
}
return 0;
}
測試結果
實現自己輸入一串字符串,串口 能夠接受并發送相同的字符串回來。
cortex-M4核通過UART串口實現數據的收發
步驟:
1.打開stm32cube軟件,左鍵點擊PB2和PG11引腳,設置為對應的模式,設置后為黃色。
2.在左邊A-Z列表中尋找UART4參數,進行如下圖的設置會觀察到PB2和PG11引腳變成綠色
3.導出keil程序,功能函數代碼中編寫fputc代碼
/*USER CODE BEGIN 0*/
int fputc(int ch,FILE* stream)
{
//判斷發送寄存器是否為空
while(!(huart4.Instance->ISR & (0x1<< 7)));
//將要發送的數據放入到發送寄存器中
huart4.Instance->TDR = ch;
//判斷是否'n'
if(ch == 'n')
{
//判斷發送寄存器是否為空
while(!( huart4.Instance->ISR & (0x1 << 7)));
huart4.Instance->TDR = 'r';
}
return ch;
}
/*USER CODE END 0 */
4.主函數代碼添加一句printf("uart4 test!!!n");觀察實驗現象如下圖。