日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stm32串口通信(初学者对于串口通信的理解)

發布時間:2025/3/15 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32串口通信(初学者对于串口通信的理解) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

stm32串口通信(初學者對于串口通信的理解)

標簽:?stm32串口通信單片機 ?987人閱讀?評論(0)?收藏?舉報 ?分類: stm32

stm32的編程其實就是通過事先定義好的不同類型的指針對內存不同位置(寄存器)的修改,得以進行各種設定。

上面的一大段代碼實際上就是對某個寄存器的操控。(這里面USARTx是USART1,tmpreg是之前定義好的變量。出自stm32f4xx_usart.c? 255至267行)

這幾行代碼等價于下面這一行代碼。其中“寄存器 &= (類型)~(類型)(十六進制)”是常用的講寄存器某個位置置零的方法。

“USART1->CR2”是“ *((uint32_t*)(0x40011000+0x10))”。0x40011000是USART1的地址,0x10是CR2這個寄存器的偏移量(手冊上有寫)。

“寄存器 &= (類型)~(類型)(十六進制)”??? //將某個位置置零的方法

“寄存器 |=? (類型)(十六進制)”????????????????// 將某個位置置一的方法

/

因為本人是剛剛開始學習stm32,有些地方不是太清楚。而我的單片機上配的例程是用庫函數編寫的。讓我感覺不適應,像是蒙著一層布。所以我花了一些時間將庫函數版本的串口通信盡我所能刪減修改成直接對寄存器操作的程序。

這是main.c文件里的一個函數。對于庫函數的應用基本上都源于這里。

GPIO_InitTypeDef GPIO_InitStructure;??????????????????????????????????????????????????????????? //第21行,定義一個結構體。下面會用對結構體其中變量的賦值修改GPIOA寄存器。

? GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;???????????????????????????????? //第30至37行,對結構體賦值,然后通過GPIO_Init函數將值傳遞給GPIOA寄存器?????
? GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;???????????????????????????????????? //
? GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

?/* Configure USART Tx as alternate function? */
? GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
? GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
? GPIO_Init(GPIOA, &GPIO_InitStructure);


/*******************************************************庫函數RCC_AHB1PeriphClockCmd***********************************************************************/

打開庫函數。

第1094行和第1096行的這兩個函數。他們的作用是檢測傳遞參數是否有效,幾乎每一個庫函數都會用它檢測一下。重要的是黑色線框里的代碼。

第1097至1104行,使能時鐘。也是操作寄存器。

(注意:每個總線(AHB1,AHB2,APB1,APB2)都有各自的使能庫函數RCC_AxBxPeriphClockCmd。)

于是上述代碼可以再main.c的響應位置替換成

RCC->AHB1ENR |= RCC_AHB1Periph_GPIOA;

RCC_APB2PeriphClockCmd函數同理替換成RCC->APB2ENR |= RCC_APB2Periph_USART1。(每一個模塊都要初始化時鐘)

/****************************************************************庫函數GPIO_PinAFConfig******************************************************************************/

跟其他庫函數一樣,也用到了assert_param來檢測參數。

temp和temp_2是為了下面的數據計算。



temp = ((uint32_t)(GPIO_AF) << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4)) ;

GPIO_PinSource取值為0~15.“((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4))”的作用是將0~15這16個數轉換

0?? 1?? 2?? 3?? 4?? 5?? 6?? 7
8?? 9?? 10? 11? 12? 13? 14? 15

0?? 4?? 8??? 12? 16? 20? 24? 28

也就是說0和8轉換成0,1和9轉換成4,2和10轉換成8.。。。。。之所以這樣轉換,是因為AF有兩個寄存器——AFH,AHL。每一個是32位。其中每4位控制一個。

temp的值就是將GPIO_PinSource對應4位組合置為GPIO_AF(0x07)。

例:如果GPIO_PinSource的值是9。則temp = ((uint32_t)(GPIO_AF) << 4,即temp的值變成0x00000070;


///

GPIOx->AFR[GPIO_PinSource >> 0x03] &= ~((uint32_t)0xF << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4)) ;


“GPIOx->AFR[GPIO_PinSource >> 0x03]”的作用是如果GPIO_PinSource的值是0~7,就對AFRL操作,如果是8~15,就對AFRH操作。

這里還用到了常用的清零的方法。“寄存器 &= ~數值”。

最終結果是將寄存器相應的4位組合全部清零。



? temp_2 = GPIOx->AFR[GPIO_PinSource >> 0x03] | temp;
? GPIOx->AFR[GPIO_PinSource >> 0x03] = temp_2;

其實這部分代碼我覺得有點累贅。為什么不寫成GPIOx->AFR[GPIO_PinSource>>0x03] |=temp ?求大神指點。

上面已經說過“[GPIO_PinSource >> 0x03]”的作用區分高8位和低8位。這兩行代碼的意思就是講已經置零的4位賦值為0111;


至此,GPIO_PinAFConfig函數結束。

根據實際情況,我將main.c中GPIO_PinAFConfig的位置替換成

?GPIOA->AFR[1] &= ~0x00000ff0 ;
?GPIOA->AFR[1]? |=??? 0x00000770;


/*******************************************************************庫函數GPIO_Init*************************************************************************************/

其中變量定義如下

uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;


/

main.c文件中已經將賦值好的結構體傳遞給這個庫函數。

這個函數有一個感覺比較吊的部分。它通過for循環的檢測將引腳一個一個賦值。

通常情況下,給一個引腳置高置低要對MODER,OTYPER等寄存器分別操作。而這個庫函數是先鎖定引腳,這樣數據不會被掩蓋。比如fun1()是通常情況下的函數,fun2()是先鎖定引腳的函數。

例1:fun1(參數1);?????????????????????????????????????????????????|例2: ????????????????? fun2(參數1);

?????????fun1(參數2);?????????????????????????????????????????????????|????????????????????????????fun2(參數2);

在例1里面參數1不會有作用,因為被參數2覆蓋了。就相當于MODER=1;MODER=2;最終MODER的值是2。

在例2里面參數1先將0位置一,不影響其他位,然后參數2將1位置一,也是不影響其他位。

因此在main.c文件里就有了兩次調用


庫函數GPIO_Init的其他代碼就是先將引腳在寄存器上對應的特定位置清零,再將結構體所蘊含的數據傳送到剛才清零的位置上。



/*****************************************************************************庫函數USART_Init**************************************************************************************/

這部分代碼跟上面的也都是大同小異。響應位置清零,然后響應位置置一。讓我感到費解的是為什么庫函數在給寄存器賦值是總是喜歡定義一個變量,然后用變量傳值。比如代碼中的tmpreg就反復使用。而且由 uint32_t 轉化為uint16_t不會數據丟失嗎?為什么不直接用uint16_t?

BRR這個寄存器我還沒有弄明白。好像是關于波特率的計算。


經過這么一番折騰原本的main.c

改成

/*************************************************************************問題************************************************************************************************/

1???? 復位部分的知識沒有學習。

2?????函數重映射。

PUTCHAR_PROTOTYPE
{
? /* Place your implementation of fputc here */
? /* e.g. write a character to the USART */
? USART_SendData(USART1, (uint8_t) ch);

? /* Loop until the end of transmission */
? while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
? {}

? return ch;
}

這個函數好像是關于printf的函數重映射。

3???? 很多寄存器的賦值都會借助于一個中間變量,為什么?這樣有助于提高運行效率?(USART1的CR1,CR2,CR3這三個寄存器都用到了tmpreg這個中間變量)

4???? 在USART1的CR1.CR2.CR3??都是用uint32_t類型的tmpreg而不用uint16_t類型,為什么?用uint16_t類型的變量不行嗎????


初學stm32,歡迎指點。


總結

以上是生活随笔為你收集整理的stm32串口通信(初学者对于串口通信的理解)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。