目錄
- 前言
- 一、如何編程使用?
- 二、使用步驟
- 1.I2C通訊協議采集傳感器資料
- BH1750傳感器簡介
- CubeMX配置
- 1.GPIO引腳
- 2.時鐘
- 3.I2C外設配置
- 代碼撰寫
- 總結
- 2.SPI通訊協議LCD
- LCD簡介
- CubeMX配置
- 1.GPIO引腳
- 2.時鐘
- 3.SPI外設配置
- 代碼撰寫
- 總結
- 3.QSPI通訊協議讀寫FLASH
- QSPI簡介
- W25Q64串行FLASH簡介
- 操作原理
- CubeMX配置
- 1.GPIO引腳
- 2.時鐘
- 3.QSPI外設配置
- 代碼撰寫
- 總結
- 總結
前言
前邊的兩篇筆記將STM32總線上與外部的通信協議I2C和SPI,本筆記著重講解如何使用,
一、如何編程使用?
我們使用這些總線上的通信協議和外部模塊進行通信時,STM32外設的配置要和外部模塊的一致,這樣才能通訊同步,得到正確的資料,而這些外部模塊一般廠商都會提供手冊和驅動,我們只需要移植過來,將引腳和基本的通訊配置好,詳細的收發函式大部分都已由廠商實作好了,我們只需要閱讀廠商提供的代碼和檔案來了解針對不同模塊的指令,如常用的FLASH芯片是使用QSPI通信的,支持佇列操作,我手上的開發板的LCD是使用SPI通信的,一些傳感器是使用I2C通信的,我們根據芯片資料手冊分配的引腳和外部模塊給出的詳細指令通訊協議在CubeMX中配置好即可,
二、使用步驟
1.I2C通訊協議采集傳感器資料
BH1750傳感器簡介
BH1750FVI是日本羅姆(ROHM)半導體生產的數字式環境光傳感IC,BH1750環境光傳感器內置16位的模數轉換器,它能夠直接輸出一個數字信號,不需要再做復雜的計算,這是一種容易使用簡易電阻器的版本,通過計算電壓,來獲得有效的資料,這款環境光傳感器能夠直接通過光度計來測量,當物體在均勻的光照下它能夠在每平方米獲得1lux的光通量,它們的光強度是1lux,有時為了充分利用光源,你可以增加一個光源的反射裝置,那樣在某些方向就能獲得更多的光通量,以增加被照表面的亮度,
功能測驗
BH1750FVI支持單次或連續兩種測量模式,每種測量模式又提供了0.5lux、1lux、4lux三種解析度供選擇,分辨力越高,一次測量所需的時間就越長,在單次測量模式時,每次測量之后傳感器都自動進入Power Down模式,
連續模式解析度從高到低的指令為:0x10; 0x11; 0x13
單次模式解析度從高到低的指令為:0x20; 0x21; 0x23
主要特性:
I2C數字介面,支持速率最大400Kbps
輸出量為光照度(Illuminance)
測量范圍1~65535 lux,解析度最小到1lux
低功耗(Power down)功能
屏蔽50/60Hz市電頻率引起的光照變化干擾
支持兩個I2C地址,通過ADDR引腳選擇
較小的測量誤差(精度誤差最大值+/-20%)
CubeMX配置
1.GPIO引腳

由于要串口列印資料,所以將I2C1引腳配置為PB6/7;使用兩個GPIO引腳連接燈進行指示,引數都使用默認的,
2.時鐘

采用內部時鐘(默認)配置系統時鐘最高80MHz
I2C1時鐘為16MHz
3.I2C外設配置
使用默認引數,
代碼撰寫
- 發送檢測光照強度模式指令
- 檢測時間延時
- 讀取光照強度資料
注意:讀地址為0x47,寫地址為0x46
主要呼叫I2C函式(示例):
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
主要函式代碼(示例):
/*
連續高解析度模式精度1lux:0x10
連續高解析度模式2精度0.5lux:0x11
低解析度模式:0x13
*/
float ReadBH1750(void)
{
uint8_t temp[1]={0x11};//連續高解析度模式2
float lux=0;
uint8_t ReadData[2]={0};
HAL_I2C_Master_Transmit(&hi2c1, 0x46, temp, 1, 0xff);
HAL_Delay(180);
HAL_I2C_Master_Receive(&hi2c1, 0x47,ReadData, 2, 0xff);
lux=(float)((ReadData[0]<<8)|ReadData[1]);
lux=(double)lux/1.2;
return lux;
}
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
float lux=ReadBH1750();
printf("BH1750:%d\r\n",(int)lux);
if(lux<2||(lux>900&&lux<1100))
HAL_GPIO_WritePin(LED_SW_GPIO_Port,LED_SW_Pin,GPIO_PIN_SET);
else
HAL_GPIO_WritePin(LED_SW_GPIO_Port,LED_SW_Pin,GPIO_PIN_RESET);
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
HAL_Delay(1000);
/* USER CODE END WHILE */
總結
I2C多數是用來采集資料,因為使用兩根線,使用從機地址進行尋址通信,所以傳輸是單向的,我們只需很簡單的將我們的芯片作為主機向傳感器發送檢測指令,等待傳感器送回資料即可,編程要點在于,根據外部模塊和芯片的原理圖合理選擇引腳連接,并配置相關引數:
(1) 配置通訊使用的目標引腳為開漏模式;
(2) 使能I2C 外設的時鐘;
(3) 配置I2C 外設的模式、地址、速率等引數并使能I2C 外設;
實作資料的收發函式,如本例的ReadBH1750(),將I2C的讀寫函陣列合封裝進我們自行撰寫的函式以實作我們想要的復雜功能,
2.SPI通訊協議LCD
LCD簡介
TFT-LCD 即薄膜晶體管液晶顯示幕,其英文全稱為:Thin Film Transistor-Liquid Crystal Display,TFT-LCD 與無源TN-LCD、STN-LCD 的簡單矩陣不同,它在液晶顯示屏的每一個象素上都設定有一個薄膜晶體管(TFT),可有效地克服非選通時的串擾,使顯示液晶屏的靜態特性與掃描線數無關,因此大大提高了影像質量,TFT-LCD 也被叫做真彩液晶顯示幕,
ST7789V2是一個單芯片TFT-LCD驅動器,該芯片可以直接連接到外部MCU,支持并行8080系列的8位/9位/16位/18位介面,也支持SPI串行通訊介面, 顯示資料可以存盤在240x320x18bits的片上顯示資料RAM中, 它可以在沒有外部操作時鐘的情況下執行顯示資料RAM讀寫操作,以盡量減少功耗,
并行介面占用外部MCU芯片引腳較多,但其通訊速率較快,一般只在需要高速重繪及MCU資源比較豐富的場合使用,SPI串行通訊介面占用MCU芯片引腳較少,通訊速率相對并行介面較慢,但因其占用MCU引腳資源較少被廣泛使用,因此我們也使用SPI通訊的方式和ST7789V2進行通訊,
CubeMX配置
1.GPIO引腳

根據原理圖和LCD資料手冊,我們將引腳配置如下,
- PC3為SPI2_MOSI主出/從入引腳;
- PB13為SPI2_SCK時鐘引腳;
- PC6為LCD_WR_RS片選引腳;
- PC7為LCD_RESET復位引腳;
- PB15為LCD_POWER電源引腳;
- PA9為UART1發送引腳;
- P10為UART1接收引腳;
- PC13為LED引腳,

2.時鐘
采用內部時鐘(默認)配置系統時鐘最高80MHz
3.SPI外設配置


根據LCD資料手冊,選擇4線模式1,資料傳輸模式為MSB,上升沿采樣,根據前面講述,SPI模式一般選模式0或者模式3,分別是
1、CPOL=0,CPHA=0(空閑時時鐘線為低,在第一個時鐘邊沿即上升沿進行采樣)
2、CPOL=1,CPHA=1(空閑時時鐘線為高,在第二個時鐘邊沿即上升沿進行采樣)
因為外部芯片的SPI作業模式是固定的,但STM32的作業模式是可以配置的,因此需要將STM32的SPI作業模式配置和外部芯片一致才可以正常通訊,還需要注意外部芯片支持的SPI通訊的速率,STM32設定的SPI通訊速率不能比它高,一般情況下,外部SPI芯片手冊中會說明該芯片是在時鐘邊沿的上升沿采樣還是下降沿采樣,根據此資訊一般STM32會有兩種兩種作業模式可以滿足,選擇任意一種即可,一般偏向于選擇CPOL=1即空閑時時鐘為高的那種,即此處我們選用模式3,

SPI配置好后就可以和ST7789V2芯片進行通訊了,具體發送的命令和需要LCD如何顯示那就要看ST7789V2的芯片手冊,
代碼撰寫
- SPI采用單向方式,也就是只用到三個引腳,即主出/從入,時鐘,片選;
- 移植LCD驅動;
- 使用LCD介面函式顯示英文字串(也可顯示漢字);
- 使用LCD介面函式畫圓,
主要呼叫SPI函式(示例):
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
主要函式代碼(示例):
spi.c中添加如下函式,并注意在對應頭檔案中宣告
/* USER CODE BEGIN 1 */
uint8_t SPI2_WriteByte(uint8_t* data, uint16_t size)
{
return HAL_SPI_Transmit(&hspi2, data, size, 0xff);
}
/* USER CODE END 1 */
main.c中添加用戶代碼
/* USER CODE BEGIN Includes */
#include "LCD.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
LCD_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
LCD_Clear(BLACK);//清屏為黑色
LCD_ShowString(5, 10, 240, 32, 32, "BearPi LCDTest");//顯示字串,字體大小32*32
PutChinese_strings(10,150,"你好",0);
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);//點亮LED燈
HAL_Delay(1000);//延時1秒
LCD_Clear(BLUE);//清屏為藍色
LCD_Draw_Circle(120, 120, 100);//畫圓 半徑r=100
LCD_Draw_Circle(120, 120, 80);//畫圓 半徑r=80
LCD_Draw_Circle(120, 120, 60);//畫圓 半徑r=60
LCD_Draw_Circle(120, 120, 40);//畫圓 半徑r=40
LCD_Draw_Circle(120, 120, 20);//畫圓 半徑r=20
LCD_Draw_Circle(120, 120, 1);//畫圓 半徑r=1
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);//熄滅LED燈
HAL_Delay(1000);//延時1秒
}
/* USER CODE END 3 */
總結
LCD芯片手冊共319頁,大部分內容都介紹的是如何操作其中的暫存器以實作顯示功能的配置,我們可以直接使用開發板廠商寫好的驅動程式使用(網上資料魚龍混雜,也不一定完全正確,因此需要自己邊試邊改),掌握芯片的操作原理即可,當需要實作特定的、炫酷的、網上找不到的功能時再繼續深入去研究,不要重復造輪子,或者一上來就去啃ST7798V2的芯片手冊,
本實驗移植的是小熊派開發板廠商提供的示例中的LCD驅動,
DC為命令/資料標志(0為讀寫命令,1為讀寫資料);CS為片選,RST為復位
3.QSPI通訊協議讀寫FLASH
QSPI簡介
QSPI是Quad SPI的簡寫,是Motorola公司推出的SPI介面的擴展,比SPI應用更加廣泛,在SPI協議的基礎上,Motorola公司對其功能進行了增強,增加了佇列傳輸機制,推出了佇列串行外圍介面協議(即QSPI協議),QSPI 是一種專用的通信介面,連接單、雙或四(條資料線) SPI Flash 存盤介質,
該介面可以在以下三種模式下作業:
間接模式:使用 QSPI 暫存器執行全部操作
狀態輪詢模式:周期性讀取外部 Flash 狀態暫存器,而且標志位置 1 時會產生中斷(如擦除或燒寫完成,會產生中斷)
記憶體映射模式:外部 Flash 映射到微控制器地址空間,從而系統將其視作內部存盤器采用雙閃存模式時,將同時訪問兩個 Quad-SPI Flash,吞吐量和容量均可提高二倍,
QSPI 可以使用 6 個信號連接Flash,分別是四個資料線BK1_IO0~BK1_IO3,一個時鐘輸出CLK,一個片選輸出(低電平有效)BK1_nCS,它們的作用介紹如下:
- BK1_nCS:片選輸出(低電平有效),適用于 FLASH 1,如果 QSPI 始終在雙閃存模式下作業,則其也可用于 FLASH 2從設備選擇信號線,QSPI通訊以BK1_nCS線置低電平為開始信號,以BK1_nCS線被拉高作為結束信號,
- CLK:時鐘輸出,適用于兩個存盤器,用于通訊資料同步,它由通訊主機產生,決定了通訊的速率,不同的設備支持的最高時鐘頻率不一樣,如STM32的QSPI時鐘頻率最大為fpclk/2,兩個設備之間通訊時,通訊速率受限于低速設備,
- BK1_IO0:在雙線 / 四線模式中為雙向 IO,單線模式中為串行輸出,適用于FLASH 1,
- BK1_IO1:在雙線 / 四線模式中為雙向 IO,單線模式中為串行輸入,適用于FLASH 1,
- BK1_IO2:在四線模式中為雙向 IO,適用于 FLASH 1,
- BK1_IO3:在四線模式中為雙向 IO,適用于 FLASH 1,
本實驗:只用到BK1_nCS、CLK、BK1_IO0、BK1_IO1四個信號,
W25Q64串行FLASH簡介
大小:8M(Byte)(128塊(Block),每塊64K位元組,每塊16個扇區(Sector),每個扇區4K位元組,每個扇區16頁,每頁256個位元組)
特點:Flash芯片內的資料只能由1變0,不能由0變1,
W25Q64Flash作業方式:
1)W25Q64 SPI資料傳輸時序
W25Q64支持SPI資料傳輸時序模式0(CPOL = 0、CPHA = 0)和模式3(CPOL = 1、CPHA = 1),模式0和模式3主要區別是當SPI主機硬體介面處于空閑狀態時,SCLK的電平狀態是高電平或者是低電平,對于模式0來說,SCLK處于低電平;對于模式3來說,SCLK處于高電平,不過,在這兩種模式下,芯片都是在SCLK的上升沿采集輸入資料,下降沿輸出資料,
2)W25Q64資料格式
W25Q64資料格式為資料長度8位大小,先發高位,再發低位,
3)W25Q64傳輸速度
W25Q64在標準模式下支持80M bit/s速度,快速模式下支持160M bit/s速度,高速模式下支持320M bit/s速度,
操作原理
通過SPI介面,用標準的SPI協議發送相應指令給flash,然后flash根據命令進行各種相關操作,
- 寫使能:06H
- 讀狀態暫存器指令:05H
- 寫狀態暫存器指令:01H
- 讀資料:03H
- 頁寫:02H
- 扇區擦除指令:20H
- 塊擦除指令:D8H
- 芯片擦除指令:07H
- 掉電指令:B9H
- 讀ID指令:90H
更多指令我們可以查看手冊,編程要點和前邊一樣,配置好引腳和引數進行通信,撰寫基本讀寫函式進行操作,再在此基礎上實作復雜操作,
CubeMX配置
1.GPIO引腳

- CE:CE為片選管腳,低電平有效,上電之后,在執行一條新的指令之前,必須讓/CE管腳先有一個下降沿,
- SO(MISO):SO為串行資料輸出引腳,在CLK(串行時鐘)管腳的下降沿輸出資料,
- WP:WP為寫保護管腳,有效電平為低電平,高電平可讀可寫,低電平僅僅可讀,
- SI(MOSI):SI為串行資料輸入引腳,資料、地址和命令從SI引腳輸入到芯片內部,在CLK(串行時鐘)管腳的上升沿捕獲捕獲資料,
- CLK(SLCK):CLK為串行時鐘引腳,SPI時鐘引腳,為輸入輸出提供時鐘脈沖,
- HOLD:HOLD為保持管腳,低電平有效,當CE為低電平,并且把HOLD拉低時,資料輸出管腳將保持高阻態,并且會忽略資料輸入管腳和時鐘管腳上的信號,把HOLD管腳拉高,器件恢復正常作業,
- VCC:電源2.7V~3.6V,
- GND:地
根據主芯片原理圖我們配置引腳如下:
- QUADSPI_BK1_IO1為PB0引腳
- QUADSPI_BK1_IO0為PB1引腳
- QUADSPI_CLK為PB10引腳
- QUADSPI_BK1_NCS為PB11引腳
- LED為PC13引腳
- UART1發送為PA9引腳
- UART1接收為PA10引腳
- 按鍵KEY1為PB2引腳
- 按鍵KEY2為PB3引腳
2.時鐘
采用內部時鐘(默認)配置系統時鐘最高80MHz
3.QSPI外設配置

代碼撰寫
- 移植QSPI驅動,
- 按鍵KEY1按下時,擦除一個扇區,并在一個位置寫入資料1,
- 按鍵KEY2按下時,擦除相同扇區,并在另一個位置寫入資料2,
- 按復位鍵,查看兩個位置寫入的資料,
主要呼叫QSPI函式(示例):
QSPI_CommandTypeDef;
HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout);
HAL_StatusTypeDef HAL_QSPI_Transmit(QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout);
HAL_StatusTypeDef HAL_QSPI_Receive(QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout);
還有驅動檔案中的介面函式,
主要函式代碼(示例):
/* USER CODE BEGIN 1 */
uint32_t location1=0;
uint32_t location2=4096;
uint32_t W25xID;
uint16_t ReadData[]={0};
uint16_t ReadData_1[]={0};
uint8_t writeData[]={"welcome to IoT"};
uint8_t writeData_1[]={"第一個QuadSPI實驗"};
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
hal_spi_flash_read(ReadData,sizeof(writeData),0);//讀0位的資料
printf("ReadFlashData:%s\r\n",(char*)ReadData);//列印讀出的資料
hal_spi_flash_read(ReadData_1,sizeof(writeData_1),4096);;//讀100位的資料
printf("ReadFlashData_1:%s\r\n",(char*)ReadData_1);//列印讀出的資料
W25xID=hal_spi_flash_get_id();//讀取芯片ID
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)
{
HAL_Delay(100);
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)
{
printf("KEY1 Press \r\n");
printf("W25xID:%x\r\n",W25xID);
/*最小擦除為4096,所以一次就要擦除0-4096位置,然后在0-4096的任意位置寫入資料*/
hal_spi_flash_erase_write(writeData, sizeof(writeData),0);
}
}
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)
{
HAL_Delay(100);
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)
{
printf("KEY2 Press \r\n");
printf("W25xID:%x\r\n",W25xID);
/*最小擦除為4096,所以一次就要擦除0-4096位置,然后在0-4096的任意位置寫入資料*/
hal_spi_flash_erase_write(writeData_1, sizeof(writeData_1),location2);
}
}
}
/* USER CODE END 3 */
總結
對FLASH操作時,一定要注意扇區大小和地址,避免擦除重要資料,
STM32內部Flash有一套自己的操作函式,用時直接呼叫就好,內部flash的地址起始于0x0800,結束地址為起始地址加上內部flash大小,內部構成主要有:主存盤器、系統存盤器、OTP區域以及選項位元組區域,
主存盤器就是平時我們燒寫用到的區域,存放我們寫的垃圾用戶程式,寫入前先擦除之前的垃圾;
系統存盤器里邊固化的是系統代碼,也就是俗稱的韌體,實作引導燒錄等系統級功能;
OTP區域只能寫入一次,容量為512位元組,常用來存盤應用程式的加密密鑰;
選項位元組用于配置FLASH的讀寫保護、電源管理中的BOR級別、軟/硬看門狗功能,共32位元組,可通過修改選項控制暫存器修改,
總結
從上述三個示例中可以學到,一些常用的外部模塊,已經有很多已經實作好的驅動,我們只需要將MCU和模塊的通訊配置好,呼叫對應的介面函式就能通信,如果需要實作復雜的功能,可以使用已實作的驅動檔案,或者自己查閱手冊進行實作,這也是以后自己做產品時的思路,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/279330.html
標籤:其他
上一篇:STM32入門培訓
