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