本篇詳細(xì)的記錄了如何使用STM32CubeMX配置 STM32G070RBT6 的硬件SPI外設(shè)與 SPI Flash 通信(W25Q64)。
【STM32Cube_09】重定向printf函數(shù)到串口輸出的多種方法。
4. 封裝 SPI Flash(W25Q64)的命令和底層函數(shù)
MCU 通過向 SPI Flash 發(fā)送各種命令 來讀寫 SPI Flash內(nèi)部的寄存器,所以這種裸機驅(qū)動,首先要先宏定義出需要使用的命令,然后利用 HAL 庫提供的庫函數(shù),封裝出三個底層函數(shù),便于移植:
向 SPI Flash 發(fā)送數(shù)據(jù)的函數(shù)
從 SPI Flash 接收數(shù)據(jù)的函數(shù)
發(fā)送數(shù)據(jù)的同時讀取數(shù)據(jù)的函數(shù)
接下來開始編寫代碼~
宏定義操作命令
#define ManufactDeviceID_CMD 0x90
#define READ_STATU_REGISTER_1 0x05
#define READ_STATU_REGISTER_2 0x35
#define READ_DATA_CMD 0x03
#define WRITE_ENABLE_CMD 0x06
#define WRITE_DISABLE_CMD 0x04
#define SECTOR_ERASE_CMD 0x20
#define CHIP_ERASE_CMD 0xc7
#define PAGE_PROGRAM_CMD 0x02
封裝發(fā)送數(shù)據(jù)的函數(shù)
/**
* @brief SPI發(fā)送指定長度的數(shù)據(jù)
* @param buf —— 發(fā)送數(shù)據(jù)緩沖區(qū)首地址
* @param size —— 要發(fā)送數(shù)據(jù)的字節(jié)數(shù)
* @retval 成功返回HAL_OK
*/
static HAL_StatusTypeDef SPI_Transmit(uint8_t* send_buf, uint16_t size)
{
return HAL_SPI_Transmit(&hspi1, send_buf, size, 100);
}
封裝接收數(shù)據(jù)的函數(shù)
/**
* @brief SPI接收指定長度的數(shù)據(jù)
* @param buf —— 接收數(shù)據(jù)緩沖區(qū)首地址
* @param size —— 要接收數(shù)據(jù)的字節(jié)數(shù)
* @retval 成功返回HAL_OK
*/
static HAL_StatusTypeDef SPI_Receive(uint8_t* recv_buf, uint16_t size)
{
return HAL_SPI_Receive(&hspi1, recv_buf, size, 100);
}
封裝發(fā)送數(shù)據(jù)同時讀取數(shù)據(jù)的函數(shù)
/**
* @brief SPI在發(fā)送數(shù)據(jù)的同時接收指定長度的數(shù)據(jù)
* @param send_buf —— 接收數(shù)據(jù)緩沖區(qū)首地址
* @param recv_buf —— 接收數(shù)據(jù)緩沖區(qū)首地址
* @param size —— 要發(fā)送/接收數(shù)據(jù)的字節(jié)數(shù)
* @retval 成功返回HAL_OK
*/
static HAL_StatusTypeDef SPI_TransmitReceive(uint8_t* send_buf, uint8_t* recv_buf, uint16_t size)
{
return HAL_SPI_TransmitReceive(&hspi1, send_buf, recv_buf, size, 100);
}
5. 編寫W25Q64的驅(qū)動程序
接下來開始利用上一節(jié)封裝的宏定義和底層函數(shù),編寫W25Q64的驅(qū)動程序:
讀取Manufacture ID和Device ID
讀取 Flash 內(nèi)部這兩個ID有兩個作用:
檢測SPI Flash是否存在
可以根據(jù)ID判斷Flash具體型號
數(shù)據(jù)手冊上給出的操作時序如圖:
根據(jù)該時序,編寫代碼如下:
/**
* @brief 讀取Flash內(nèi)部的ID
* @param none
* @retval 成功返回device_id
*/
uint16_t W25QXX_ReadID(void)
{
uint8_t recv_buf[2] = {0}; //recv_buf[0]存放Manufacture ID, recv_buf[1]存放Device ID
uint16_t device_id = 0;
uint8_t send_data[4] = {ManufactDeviceID_CMD,0x00,0x00,0x00}; //待發(fā)送數(shù)據(jù),命令+地址
/* 使能片選 */
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);
/* 發(fā)送并讀取數(shù)據(jù) */
if (HAL_OK == SPI_Transmit(send_data, 4)) {
if (HAL_OK == SPI_Receive(recv_buf, 2)) {
device_id = (recv_buf[0] << 8) | recv_buf[1];
}
}
/* 取消片選 */
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);
return device_id;
}
讀取狀態(tài)寄存器數(shù)據(jù)并判斷Flash是否忙碌
上文中提到,SPI Flash的所有操作都是靠發(fā)送命令完成的,但是 Flash 接收到命令后,需要一段時間去執(zhí)行該操作,這段時間內(nèi) Flash 處于“忙”狀態(tài),MCU 發(fā)送的命令無效,不能執(zhí)行,在 Flash 內(nèi)部有2-3個狀態(tài)寄存器,指示出 Flash 當(dāng)前的狀態(tài),有趣的一點是:
當(dāng) Flash 內(nèi)部在執(zhí)行命令時,不能再執(zhí)行 MCU 發(fā)來的命令,但是 MCU 可以一直讀取狀態(tài)寄存器,這下就很好辦了,MCU可以一直讀取,然后判斷Flash是否忙完:
讀取協(xié)議如下:
根據(jù)此協(xié)議實現(xiàn)的讀取狀態(tài)寄存器的代碼如下:
/**
* @brief 讀取W25QXX的狀態(tài)寄存器,W25Q64一共有2個狀態(tài)寄存器
* @param reg —— 狀態(tài)寄存器編號(1~2)
* @retval 狀態(tài)寄存器的值
*/
static uint8_t W25QXX_ReadSR(uint8_t reg)
{
uint8_t result = 0;
uint8_t send_buf[4] = {0x00,0x00,0x00,0x00};
switch(reg)
{
case 1:
send_buf[0] = READ_STATU_REGISTER_1;
case 2:
send_buf[0] = READ_STATU_REGISTER_2;
case 0:
default:
send_buf[0] = READ_STATU_REGISTER_1;
}
/* 使能片選 */
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);
if (HAL_OK == SPI_Transmit(send_buf, 4)) {
if (HAL_OK == SPI_Receive(&result, 1)) {
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);
return result;
}
}
/* 取消片選 */
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);
return 0;
}
然后編寫阻塞判斷Flash是否忙碌的函數(shù):
/**
* @brief 阻塞等待Flash處于空閑狀態(tài)
* @param none
* @retval none
*/
static void W25QXX_Wait_Busy(void)
{
while((W25QXX_ReadSR(1) & 0x01) == 0x01); // 等待BUSY位清空
}
讀取數(shù)據(jù)
SPI Flash讀取數(shù)據(jù)可以任意地址(地址長度32bit)讀任意長度數(shù)據(jù)(最大 65535 Byte),沒有任何限制,數(shù)據(jù)手冊給出的時序如下:
根據(jù)該時序圖編寫代碼如下:
/**
* @brief 讀取SPI FLASH數(shù)據(jù)
* @param buffer —— 數(shù)據(jù)存儲區(qū)
* @param start_addr —— 開始讀取的地址(最大32bit)
* @param nbytes —— 要讀取的字節(jié)數(shù)(最大65535)
* @retval 成功返回0,失敗返回-1
*/
int W25QXX_Read(uint8_t* buffer, uint32_t start_addr, uint16_t nbytes)
{
uint8_t cmd = READ_DATA_CMD;
start_addr = start_addr << 8;
W25QXX_Wait_Busy();
/* 使能片選 */
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);
SPI_Transmit(&cmd, 1);
if (HAL_OK == SPI_Transmit((uint8_t*)&start_addr, 3)) {
if (HAL_OK == SPI_Receive(buffer, nbytes)) {
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);
return 0;
}
}
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);
return -1;
}
寫使能/禁止
Flash 芯片默認(rèn)禁止寫數(shù)據(jù),所以在向 Flash 寫數(shù)據(jù)之前,必須發(fā)送命令開啟寫使能,數(shù)據(jù)手冊中給出的時序如下:
編寫函數(shù)如下:
/**
* @brief W25QXX寫使能,將S1寄存器的WEL置位
* @param none
* @retval
*/
void W25QXX_Write_Enable(void)
{
uint8_t cmd= WRITE_ENABLE_CMD;
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);
SPI_Transmit(&cmd, 1);
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);
W25QXX_Wait_Busy();
}
/**
* @brief W25QXX寫禁止,將WEL清零
* @param none
* @retval none
*/
void W25QXX_Write_Disable(void)
{
uint8_t cmd = WRITE_DISABLE_CMD;
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);
SPI_Transmit(&cmd, 1);
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);
W25QXX_Wait_Busy();
}
擦除扇區(qū)
SPI Flash有個特性:
數(shù)據(jù)位可以由1變?yōu)?,但是不能由0變?yōu)?。
所以在向 Flash 寫數(shù)據(jù)之前,必須要先進行擦除操作,并且 Flash 最小只能擦除一個扇區(qū),擦除之后該扇區(qū)所有的數(shù)據(jù)變?yōu)?0xFF(即全為1),數(shù)據(jù)手冊中給出的時序如下:
根據(jù)此時序編寫函數(shù)如下:
/**
* @brief W25QXX擦除一個扇區(qū)
* @param sector_addr —— 扇區(qū)地址 根據(jù)實際容量設(shè)置
* @retval none
* @note 阻塞操作
*/
void W25QXX_Erase_Sector(uint32_t sector_addr)
{
uint8_t cmd = SECTOR_ERASE_CMD;
sector_addr *= 4096; //每個塊有16個扇區(qū),每個扇區(qū)的大小是4KB,需要換算為實際地址
sector_addr <<= 8;
W25QXX_Write_Enable(); //擦除操作即寫入0xFF,需要開啟寫使能
W25QXX_Wait_Busy(); //等待寫使能完成
/* 使能片選 */
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);
SPI_Transmit(&cmd, 1);
SPI_Transmit((uint8_t*)§or_addr, 3);
HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);
W25QXX_Wait_Busy(); //等待扇區(qū)擦除完成
}
頁寫入操作
向 Flash 芯片寫數(shù)據(jù)的時候,因為 Flash 內(nèi)部的構(gòu)造,可以按頁寫入:
頁寫入的時序如圖:
編寫代碼如下:
/**
* @brief 頁寫入操作
* @param dat —— 要寫入的數(shù)據(jù)緩沖區(qū)首地址
* @param WriteAddr —— 要寫入的地址
* @param byte_to_write —— 要寫入的字節(jié)數(shù)(0-256)
* @retval none
*/
void W25QXX_Page_Program(uint8_t* dat, uint32_t WriteAddr, uint16_t nbytes)
{
uint8_t cmd = PAGE_PROGRAM_CMD;
WriteAddr <<= 8;
W25QXX_Write_Enable();
上一篇:基于STM32芯片創(chuàng)建HelloWorld工程
下一篇:STM32CubeMX | 32-使用硬件FMC驅(qū)動TFT-LCD屏幕(MCU屏)
推薦閱讀
史海拾趣
質(zhì)量是企業(yè)的生命線。常州星海電子自成立以來,便高度重視質(zhì)量管理體系的建設(shè)和完善。公司先后通過了ISO9002、ISO9001:2000、ISO14000等多項國際質(zhì)量管理體系認(rèn)證,以及TS16949體系認(rèn)證。這些認(rèn)證不僅證明了公司在質(zhì)量管理方面的卓越實力,也為公司贏得了眾多國內(nèi)外客戶的信任和贊譽。
勝利公司深知品牌文化對于提升品牌價值的重要性。因此,公司積極開展文化營銷活動,如舉辦羽毛球文化節(jié)、贊助羽毛球賽事等。通過這些活動,公司不僅與消費者建立了深厚的情感聯(lián)系,還提升了品牌的知名度和美譽度。
以上故事均基于虛構(gòu),旨在展示一個品牌成長的可能路徑和方式。如有需要,請結(jié)合實際情況進行調(diào)整和修改。
進入21世紀(jì)后,德利威電子加速了其國際化布局的步伐。2000年,公司在中國大陸東莞成立了德利威電子廠,并成功推動了5S運動(整理、整頓、清掃、清潔、素養(yǎng)),進一步提升了公司的管理水平。同時,公司還取得了ISO-9001國際認(rèn)證標(biāo)準(zhǔn),標(biāo)志著其質(zhì)量管理體系的進一步完善。此外,德利威電子還取得了德國TUV產(chǎn)品安規(guī)認(rèn)證,進一步增強了其產(chǎn)品的國際競爭力。
隨著科技的不斷進步,Delkin Devices始終保持著對新技術(shù)和新產(chǎn)品的敏感度和熱情。公司投入大量資源進行技術(shù)研發(fā)和產(chǎn)品創(chuàng)新,不斷推出具有競爭力的新產(chǎn)品。例如,在固態(tài)硬盤(SSD)技術(shù)興起的時代,Delkin Devices迅速抓住機遇,成功開發(fā)出了一系列高性能、高可靠性的SSD產(chǎn)品,贏得了市場的廣泛認(rèn)可。此外,Delkin Devices還積極拓展產(chǎn)品線,推出了包括讀卡器、存儲卡等在內(nèi)的多種存儲配件,進一步鞏固了其在電子行業(yè)中的領(lǐng)先地位。
聚洵半導(dǎo)體始終堅持以市場為導(dǎo)向、以創(chuàng)新為驅(qū)動的發(fā)展理念。公司不斷加大新產(chǎn)品研發(fā)投入,致力于在低功耗運放、高速運放、儀表放大器等領(lǐng)域取得更多技術(shù)突破。同時,聚洵還積極關(guān)注行業(yè)動態(tài)和市場趨勢,針對醫(yī)療電子、工業(yè)控制、汽車電子等新興領(lǐng)域推出了一系列創(chuàng)新產(chǎn)品。展望未來,聚洵將繼續(xù)保持專注和創(chuàng)新精神,致力于成為國內(nèi)領(lǐng)先的信號鏈模擬芯片設(shè)計公司之一,為電子行業(yè)的發(fā)展貢獻更多力量。
/****** 霹靂游俠掃描燈20模式帶拖尾C程序,ME850調(diào)試正常*****2009/05/30*******/ #include //頭文件 rzmzy(); //紅色走馬左移聲明 rlszy(); //紅色流水左移聲明 rlsyy(); //紅色流水右移聲明 rzmyy(); //紅色走馬右移聲明 rzmnwyd(); //紅色走馬 ...… 查看全部問答∨ |
接入信號發(fā)生器老不能正常顯示頻率,因此我把程序簡化再簡化。。。最后就讓信號發(fā)生器來一個高脈沖,計數(shù)器就加1,但是即使如此數(shù)碼管上的數(shù)還是亂跳。。。我都不知道是什么問題。。。數(shù)碼管顯示程序是無問題的,因為我在其他程序中用過。 我把程 ...… 查看全部問答∨ |
各位大俠,我通過看書只了解到如何初始化寄存器產(chǎn)生一路PWM,但由于需要用到六路PWM。但我不知道怎樣產(chǎn)生六個PWM,望各大俠高手指教,十分感激… 查看全部問答∨ |
單片機系統(tǒng),外設(shè)CF卡存儲器,驅(qū)動寫好了,文件系統(tǒng)FAT16,能正常讀寫。 現(xiàn)在需要把采集的數(shù)據(jù)以報表的格式生成word文檔(其中包括處理數(shù)據(jù)得到的曲線圖,就是這點最可恨)保存進CF卡里。求幫忙指點。 word文檔在磁盤里的存儲格式是什么樣的?特 ...… 查看全部問答∨ |
我想學(xué)習(xí)windows mobile 5/6的 程序開發(fā) 請大家 推薦些書籍資料 可以用mfc 嘛? ~~~~~~~~~~~~~~~~~~~~~~~~~~~… 查看全部問答∨ |
SOCKADDR_IN local; local.sin_family = AF_INET; local.sin_port = htons(1028); local.sin_addr.S_un.S_addr = inet_addr(ip); m_socket = socket(AF_INET,SOCK_STREAM,0); 編譯的時候出現(xiàn)下面的錯誤,請高手指點~~~~ error C2664: \'inet_ ...… 查看全部問答∨ |
用例子的-c系列改的,原來是uart2該為串口1, 用超級終端看不到反應(yīng)了,改用其他串口工具,看到亂碼。 用-b系列改,能看到串口,可操作,但是燒寫flash后沒反應(yīng) 我想-b和-c因為flash不同可能有所不同,但是只發(fā)現(xiàn)pagesize和flashsize上有不 ...… 查看全部問答∨ |
中國有好東西的,不然兩彈一星怎么搞定的? 現(xiàn)在的民企,也有可信賴的元器件,就看我們愿意不愿意支持國產(chǎn)了。 本人愿意以后致力于這方面的工作?!? 查看全部問答∨ |
Beaglebone在CCSv5下Connect Target出現(xiàn)錯誤提示,但還能照常下載程序? 在把需要設(shè)置的都設(shè)置好后,最后Connect Target的時候出現(xiàn)提示: 然后試試LOAD程序到板子里,居然可以正常運行,能夠但不運行。 不知道上面的紅色提示是怎么回事?… 查看全部問答∨ |
系統(tǒng)中不同的地方要不同的電壓,存儲器1.8V,MCU 5V,F(xiàn)PGA 3.3V等等。 這是一款簡單而快速的電平轉(zhuǎn)換電路,可以將輸入時鐘調(diào)節(jié)為適應(yīng)正、負(fù)電壓電平。 電平轉(zhuǎn)換電路包括快速切換的晶體管Q1和Q2。用戶選擇電平轉(zhuǎn)換為高和轉(zhuǎn)換為低,這是直流偏置 ...… 查看全部問答∨ |
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦
- 阿里黑科技落地!夸克AI眼鏡全球首發(fā),高德、淘寶、支付寶都能用
- 化繁為簡, 適配復(fù)雜磁場環(huán)境,MT73xx 3D雙路輸出霍爾鎖存器賦能車規(guī)電機精準(zhǔn)控制
- 9.5億美元收購恩智浦MEMS傳感器業(yè)務(wù),意法半導(dǎo)體 在傳感器領(lǐng)域的地位再升級
- 高性能電動滑板車 BLDC 電機驅(qū)動器:技術(shù)解析與應(yīng)用展望
- 5G工業(yè)網(wǎng)關(guān)的“邊緣計算+AI推理”一體化設(shè)計,PLC協(xié)議解析與缺陷檢測的實時聯(lián)動
- AR眼鏡的“工業(yè)指令投射”系統(tǒng),SLAM的空間定位、PLC數(shù)據(jù)實時疊加顯示
- 多光譜氣體傳感器的抗交叉干擾設(shè)計
- 多模態(tài)融合感知的“語義-幾何”聯(lián)合建模
- 工業(yè)觸摸屏的“壓感-手勢”多模態(tài)交互設(shè)計
- 工業(yè)機器人高精度力控的“雙模融合”傳感器設(shè)計
- 【社區(qū)大講堂】頂嵌與你一起:動手做嵌入式產(chǎn)品
- 快來!月月有獎第27期開始了~
- e絡(luò)盟e選第三期:熱門產(chǎn)品5折起,價格鎖定更安心
- 來聊聊你與MP3的情緣吧!——MP3播放板等你拿
- 端午節(jié)芯幣兌換禮品專場
- 【EEWORLD第二十七屆】2011年06月社區(qū)明星人物揭曉!
- 最熱8月,帶著你的原創(chuàng)一起High!
- 贏京東卡——我家房子“成精”了!與英飛凌一起來探索吧!
- 下載信息娛樂系統(tǒng)精品文章,贏【米家臺燈、小米保溫杯、米家藍牙溫濕計】,開啟MPS汽車技術(shù)進階之旅!
- 恩智浦無線MCU應(yīng)用征集+100套開發(fā)套件申請
- 從創(chuàng)想到落地,人工智能骨齡檢測開啟醫(yī)療AI進階之旅
- 恭喜Marvell汽車千兆以太網(wǎng)技術(shù)超越JASPAR設(shè)定的運行性能基準(zhǔn)
- 英特爾與SiTime聯(lián)手開發(fā)5G調(diào)制解調(diào)器MEMS時鐘解決方案
- 全國各大家電企業(yè)的智能制造之路
- 國產(chǎn)大型無人直升機翔鷹-200獲重大突破
- 北斗芯片銷量超5000萬 國產(chǎn)衛(wèi)星導(dǎo)航的崛起?
- 2017年終高端VR排名:HTC Vive險勝Oculus Rift 一個百分點
- 《中國光電子器件產(chǎn)業(yè)技術(shù)發(fā)展路線圖》重磅發(fā)布
- 華為今年要出貨2億部智能手機的可能性有多大?
- 2018年汽車技術(shù)7大預(yù)測:電池進步 聯(lián)網(wǎng)汽車崛起