正点原子STM32
正點原子B站視頻地址:https://www.bilibili.com/video/BV1Lx411Z7Qa?p=4&spm_id_from=pageDriver
目錄
- STM32命名規則
- STM32芯片解讀
- 開發環境搭建(MDK - 就是ARM的keil,需破解 + 支持包 + CH340串口驅動+ JLINK驅動)
- 程序下載方法 (ISP串口下載 + JLINK下載更方便)
- 新建工程模板——基于固件庫
- 新建工程模板——基于寄存器
- GPIO相關配置寄存器
- 端口復用、重映射、中斷
- JLINK在線調試
- JTAG/SWD調試原理
- 軟件仿真
- ST-LINK下載與調試程序(硬件)
- 獨立看門狗IWDG
- 獨立看門狗概述
- 寄存器和庫函數
- 代碼實現
- 窗口看門狗WWDG
- 窗口看門狗概述
- 寄存器和庫函數
- 代碼實現
- 2.4G無線射頻通信模塊
- NRF24L01簡介
- 寄存器介紹
- 硬件連接
- 源碼講解
- 485通信實驗
- 485接口原理
- FLASH模擬EEPROM
- FLASH介紹
- FLASH操作(讀、寫、擦除)
- 寄存器
- 常用庫函數
- 程序編寫
- 串口IAP實驗(在程序中編程In Application Programming)
- STM32IAP介紹
- IAP運作流程
- IAP配置
- 實驗程序
- APP 程序起始地址設置方法
- 中斷向量表的偏移量設置方法
- 藍牙模塊HC05使用
- 模塊介紹
- 模塊AT指令集
- **與上位機串口通信**
- **與手機藍牙調試助手通信**
- 與單片機連接通信
STM32命名規則
STM32芯片解讀
開發環境搭建(MDK - 就是ARM的keil,需破解 + 支持包 + CH340串口驅動+ JLINK驅動)
上圖在MDK環境中點擊下載即可,或者去官網下載:
程序下載方法 (ISP串口下載 + JLINK下載更方便)
ISP串口下載
以前學習的是電腦USB端連接CH340芯片。
正點原子還設計了一鍵下載電路,不用來回撥動跳線帽。
JLINK程序下載
需要先安裝JLINK驅動,前面講過了
在MDK中配置一下JLINK
新建工程模板——基于固件庫
視頻開發環境:MDK5
固件庫版本:V3.5
解壓正點原子文件夾內的固件庫包–C:\Users\朱冠霖\Desktop\【正點原子】戰艦STM32F103開發板\【正點原子】戰艦STM32F103開發板資料 資料盤(A盤)\8,STM32參考資料\1,STM32F1xx固件庫
新建工程模板——基于寄存器
GPIO相關配置寄存器
端口復用、重映射、中斷
JLINK在線調試
JTAG/SWD調試原理
軟件仿真
ST-LINK下載與調試程序(硬件)
獨立看門狗IWDG
獨立看門狗概述
寄存器和庫函數
上圖prer表示預分屏值
代碼實現
wdg.h
#ifndef __WDG_H #define __WDG_H #include "sys.h"void IWDG_Init(u8 prer,u16 rlr); void IWDG_Feed(void);#endifwdg.c
#include "wdg.h"//初始化獨立看門狗 //prer:分頻數:0~7(只有低3位有效!) //分頻因子=4*2^prer.但最大值只能是256! //rlr:重裝載寄存器值:低11位有效. //時間計算(大概):Tout=((4*2^prer)*rlr)/40 (ms). void IWDG_Init(u8 prer,u16 rlr) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能對寄存器IWDG_PR和IWDG_RLR的寫操作(取消寫保護)IWDG_SetPrescaler(prer); //設置IWDG預分頻值:設置IWDG預分頻值為64IWDG_SetReload(rlr); //設置IWDG重裝載值rlrIWDG_ReloadCounter(); //按照IWDG重裝載寄存器的值rlr重裝載IWDG計數器IWDG_Enable(); //使能IWDG } //喂獨立看門狗 void IWDG_Feed(void) { IWDG_ReloadCounter();//reload }main.c
#include "led.h" #include "delay.h" #include "key.h" #include "sys.h" #include "usart.h" #include "wdg.h"int main(void) { delay_init(); //延時函數初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶占優先級,2位響應優先級uart_init(115200); //串口初始化為115200LED_Init(); //初始化與LED連接的硬件接口KEY_Init(); //按鍵初始化 delay_ms(500); //讓人看得到滅IWDG_Init(4,625); //預分頻數為64,重載值為625,溢出時間為1s LED0=0; //點亮LED0while(1){if(KEY_Scan(0)==WKUP_PRES){ //調用函數IWDG_ReloadCounter()一樣的 IWDG_Feed();//如果WK_UP按下,則喂狗,燈一直亮 不喂狗則程序一直復位燈一直閃爍}delay_ms(10);}; }窗口看門狗WWDG
窗口看門狗概述
寄存器和庫函數
代碼實現
wdg.h
#ifndef __WDG_H #define __WDG_H #include "sys.h"void IWDG_Init(u8 prer,u16 rlr); void IWDG_Feed(void);void WWDG_Init(u8 tr,u8 wr,u32 fprer);//初始化WWDG void WWDG_Set_Counter(u8 cnt); //設置WWDG的計數器 void WWDG_NVIC_Init(void); #endifwdg.c
#include "wdg.h" #include "led.h"void IWDG_Init(u8 prer,u16 rlr) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能對寄存器IWDG_PR和IWDG_RLR的寫操作IWDG_SetPrescaler(prer); //設置IWDG預分頻值:設置IWDG預分頻值為64IWDG_SetReload(rlr); //設置IWDG重裝載值IWDG_ReloadCounter(); //按照IWDG重裝載寄存器的值重裝載IWDG計數器 IWDG_Enable(); //使能IWDG } //喂獨立看門狗 void IWDG_Feed(void) { IWDG_ReloadCounter(); //重載計數值 }//保存WWDG計數器的設置值,默認為最大. u8 WWDG_CNT=0x7f; //初始化窗口看門狗 //tr :T[6:0],計數器值 往下計數 //wr :W[6:0],窗口值 //fprer:分頻系數(WDGTB),僅最低2位有效 //Fwwdg=PCLK1/(4096*2^fprer). void WWDG_Init(u8 tr,u8 wr,u32 fprer) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG時鐘使能WWDG_CNT=tr&WWDG_CNT; //初始化WWDG_CNT. 與運算取后7位 WWDG_SetPrescaler(fprer);設置IWDG預分頻值WWDG_SetWindowValue(wr);//設置窗口值WWDG_Enable(WWDG_CNT); //使能看門狗 , 設置 counter . WWDG_ClearFlag();//清除提前喚醒中斷標志位 WWDG_NVIC_Init();//初始化窗口看門狗 NVICWWDG_EnableIT(); //開啟窗口看門狗中斷 } //重設置WWDG計數器的值 void WWDG_Set_Counter(u8 cnt) {WWDG_Enable(cnt);//使能看門狗 , 設置 counter . }//窗口看門狗中斷服務程序 void WWDG_NVIC_Init() {NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG中斷NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶占2,子優先級3,組2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //搶占2,子優先級3,組2 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure);//NVIC初始化 }void WWDG_IRQHandler(void){WWDG_SetCounter(WWDG_CNT);//喂狗 當禁掉此句后,窗口看門狗將產生復位WWDG_ClearFlag(); //清除提前喚醒中斷標志位LED1=!LED1; //LED1狀態翻轉(指示喂狗成功)}main.c
#include "led.h" #include "delay.h" #include "key.h" #include "sys.h" #include "usart.h" #include "wdg.h"int main(void){ delay_init(); //延時函數初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置中斷優先級分組為組2:2位搶占優先級,2位響應優先級uart_init(115200); //串口初始化為115200LED_Init();KEY_Init(); //按鍵初始化 LED0=0;delay_ms(300); //計數器從0x7F到0x40,觸發中斷喂狗,LED1燈翻轉WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);//計數器值為7f,上窗口寄存器為5f(下窗口固定),分頻數為8 while(1){LED0=1; } }2.4G無線射頻通信模塊
NRF24L01簡介
寄存器介紹
硬件連接
源碼講解
24101.h
24101.c
#include "24l01.h" #include "lcd.h" #include "delay.h" #include "spi.h" #include "usart.h" // //本程序只供學習使用,未經作者許可,不得用于其它任何用途 //ALIENTEK戰艦STM32開發板 //NRF24L01驅動代碼 //正點原子@ALIENTEK //技術論壇:www.openedv.com //修改日期:2012/9/13 //版本:V1.0 //版權所有,盜版必究。 //Copyright(C) 廣州市星翼電子科技有限公司 2009-2019 //All rights reserved //const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //發送地址 const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01};//初始化24L01的IO口 void NRF24L01_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE); //使能PB,G端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PB12上拉 防止W25X的干擾GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化指定IOGPIO_SetBits(GPIOB,GPIO_Pin_12);//上拉 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8; //PG8 7 推挽 GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化指定IOGPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PG6 輸入 GPIO_Init(GPIOG, &GPIO_InitStructure);GPIO_ResetBits(GPIOG,GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8);//PG6,7,8上拉 SPI2_Init(); //初始化SPI SPI_Cmd(SPI2, DISABLE); // SPI外設不使能SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI設置為雙線雙向全雙工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //SPI主機SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //發送接收8位幀結構SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //時鐘懸空低SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //數據捕獲于第1個時鐘沿SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信號由軟件控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //定義波特率預分頻的值:波特率預分頻值為16SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //數據傳輸從MSB位開始SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值計算的多項式SPI_Init(SPI2, &SPI_InitStructure); //根據SPI_InitStruct中指定的參數初始化外設SPIx寄存器SPI_Cmd(SPI2, ENABLE); //使能SPI外設NRF24L01_CE=0; //使能24L01NRF24L01_CSN=1; //SPI片選取消 } //檢測24L01是否存在 //返回值:0,成功;1,失敗 u8 NRF24L01_Check(void) {u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};u8 i;SPI2_SetSpeed(SPI_BaudRatePrescaler_4); //spi速度為9Mhz(24L01的最大SPI時鐘為10Mhz) NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,buf,5);//寫入5個字節的地址. NRF24L01_Read_Buf(TX_ADDR,buf,5); //讀出寫入的地址 for(i=0;i<5;i++)if(buf[i]!=0XA5)break; if(i!=5)return 1;//檢測24L01錯誤 return 0; //檢測到24L01 } //SPI寫寄存器 //reg:指定寄存器地址 //value:寫入的值 u8 NRF24L01_Write_Reg(u8 reg,u8 value) {u8 status; NRF24L01_CSN=0; //使能SPI傳輸status =SPI2_ReadWriteByte(reg);//發送寄存器號 SPI2_ReadWriteByte(value); //寫入寄存器的值NRF24L01_CSN=1; //禁止SPI傳輸 return(status); //返回狀態值 } //讀取SPI寄存器值 //reg:要讀的寄存器 u8 NRF24L01_Read_Reg(u8 reg) {u8 reg_val; NRF24L01_CSN = 0; //使能SPI傳輸 SPI2_ReadWriteByte(reg); //發送寄存器號reg_val=SPI2_ReadWriteByte(0XFF);//讀取寄存器內容NRF24L01_CSN = 1; //禁止SPI傳輸 return(reg_val); //返回狀態值 } //在指定位置讀出指定長度的數據 //reg:寄存器(位置) //*pBuf:數據指針 //len:數據長度 //返回值,此次讀到的狀態寄存器值 u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len) {u8 status,u8_ctr; NRF24L01_CSN = 0; //使能SPI傳輸status=SPI2_ReadWriteByte(reg);//發送寄存器值(位置),并讀取狀態值 for(u8_ctr=0;u8_ctr<len;u8_ctr++)pBuf[u8_ctr]=SPI2_ReadWriteByte(0XFF);//讀出數據NRF24L01_CSN=1; //關閉SPI傳輸return status; //返回讀到的狀態值 } //在指定位置寫指定長度的數據 //reg:寄存器(位置) //*pBuf:數據指針 //len:數據長度 //返回值,此次讀到的狀態寄存器值 u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len) {u8 status,u8_ctr; NRF24L01_CSN = 0; //使能SPI傳輸status = SPI2_ReadWriteByte(reg);//發送寄存器值(位置),并讀取狀態值for(u8_ctr=0; u8_ctr<len; u8_ctr++)SPI2_ReadWriteByte(*pBuf++); //寫入數據 NRF24L01_CSN = 1; //關閉SPI傳輸return status; //返回讀到的狀態值 } //啟動NRF24L01發送一次數據 //txbuf:待發送數據首地址 //返回值:發送完成狀況 u8 NRF24L01_TxPacket(u8 *txbuf) {u8 sta;SPI2_SetSpeed(SPI_BaudRatePrescaler_8);//spi速度為9Mhz(24L01的最大SPI時鐘為10Mhz) NRF24L01_CE=0;NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//寫數據到TX BUF 32個字節NRF24L01_CE=1;//啟動發送 while(NRF24L01_IRQ!=0);//等待發送完成sta=NRF24L01_Read_Reg(STATUS); //讀取狀態寄存器的值 NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中斷標志if(sta&MAX_TX)//達到最大重發次數{NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器 return MAX_TX; }if(sta&TX_OK)//發送完成{return TX_OK;}return 0xff;//其他原因發送失敗 } //啟動NRF24L01發送一次數據 //txbuf:待發送數據首地址 //返回值:0,接收完成;其他,錯誤代碼 u8 NRF24L01_RxPacket(u8 *rxbuf) {u8 sta; SPI2_SetSpeed(SPI_BaudRatePrescaler_8); //spi速度為9Mhz(24L01的最大SPI時鐘為10Mhz) sta=NRF24L01_Read_Reg(STATUS); //讀取狀態寄存器的值 NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中斷標志if(sta&RX_OK)//接收到數據{NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//讀取數據NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器 return 0; } return 1;//沒收到任何數據 } //該函數初始化NRF24L01到RX模式 //設置RX地址,寫RX數據寬度,選擇RF頻道,波特率和LNA HCURR //當CE變高后,即進入RX模式,并可以接收數據了 void NRF24L01_RX_Mode(void) {NRF24L01_CE=0; NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);//寫RX節點地址NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自動應答 NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);//使能通道0的接收地址 NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //設置RF通信頻率 NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//選擇通道0的有效數據寬度 NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);//設置TX發射參數,0db增益,2Mbps,低噪聲增益開啟 NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的參數;PWR_UP,EN_CRC,16BIT_CRC,接收模式 NRF24L01_CE = 1; //CE為高,進入接收模式 } //該函數初始化NRF24L01到TX模式 //設置TX地址,寫TX數據寬度,設置RX自動應答的地址,填充TX發送數據,選擇RF頻道,波特率和LNA HCURR //PWR_UP,CRC使能 //當CE變高后,即進入RX模式,并可以接收數據了 //CE為高大于10us,則啟動發送. void NRF24L01_TX_Mode(void) { NRF24L01_CE=0; NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//寫TX節點地址 NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //設置TX節點地址,主要為了使能ACK NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自動應答 NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址 NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//設置自動重發間隔時間:500us + 86us;最大自動重發次數:10次NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //設置RF通道為40NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f); //設置TX發射參數,0db增益,2Mbps,低噪聲增益開啟 NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e); //配置基本工作模式的參數;PWR_UP,EN_CRC,16BIT_CRC,接收模式,開啟所有中斷NRF24L01_CE=1;//CE為高,10us后啟動發送 }spi.c
main.c
485通信實驗
485接口原理
FLASH模擬EEPROM
FLASH介紹
FLASH操作(讀、寫、擦除)
寄存器
常用庫函數
程序編寫
stmflash.h
#ifndef __STMFLASH_H__ #define __STMFLASH_H__ #include "sys.h" // //本程序只供學習使用,未經作者許可,不得用于其它任何用途 //ALIENTEK戰艦STM32開發板 //STM32 FLASH 驅動代碼 //正點原子@ALIENTEK //技術論壇:www.openedv.com //修改日期:2012/9/13 //版本:V1.0 //版權所有,盜版必究。 //Copyright(C) 廣州市星翼電子科技有限公司 2009-2019 //All rights reserved //// //用戶根據自己的需要設置 #define STM32_FLASH_SIZE 512 //所選STM32的FLASH容量大小(單位為K) #define STM32_FLASH_WREN 1 //使能FLASH寫入(0,不是能;1,使能) ////FLASH起始地址 #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址 //FLASH解鎖鍵值u16 STMFLASH_ReadHalfWord(u32 faddr); //讀出半字 void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址開始寫入指定長度的數據 u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len); //指定地址開始讀取指定長度數據 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite); //從指定地址開始寫入指定長度的數據 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead); //從指定地址開始讀出指定長度的數據//測試寫入 void Test_Write(u32 WriteAddr,u16 WriteData); #endifstmflash.c
#include "stmflash.h" #include "delay.h" #include "usart.h"// //本程序只供學習使用,未經作者許可,不得用于其它任何用途 //ALIENTEK戰艦STM32開發板 //STM32 FLASH 驅動代碼 //正點原子@ALIENTEK //技術論壇:www.openedv.com //修改日期:2012/9/13 //版本:V1.0 //版權所有,盜版必究。 //Copyright(C) 廣州市星翼電子科技有限公司 2009-2019 //All rights reserved ////讀取指定地址的半字(16位數據) //faddr:讀地址(此地址必須為2的倍數!!) //返回值:對應數據. u16 STMFLASH_ReadHalfWord(u32 faddr) {return *(vu16*)faddr; } #if STM32_FLASH_WREN //如果使能了寫 //不檢查的寫入(寫之前要擦除,檢查是否擦除了,這里是已經檢查過了直接寫入) //WriteAddr:起始地址 //pBuffer:數據指針 //NumToWrite:半字(16位)數 void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u16 i;for(i=0;i<NumToWrite;i++){FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);WriteAddr+=2;//地址增加2.} } //從指定地址開始寫入指定長度的數據 //WriteAddr:起始地址(此地址必須為2的倍數!!) //pBuffer:數據指針 //NumToWrite:半字(16位)數(就是要寫入的16位(半字)數據的個數.) #if STM32_FLASH_SIZE<256 #define STM_SECTOR_SIZE 1024 //字節 #else #define STM_SECTOR_SIZE 2048 #endif u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字節 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) {u32 secpos; //扇區地址u16 secoff; //扇區內偏移地址(16位字計算)u16 secremain; //扇區內剩余地址(16位字計算) u16 i; u32 offaddr; //去掉0X08000000后的地址 基地址 flash的size(宏定義512)if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址,地址越界FLASH_Unlock(); //解鎖offaddr=WriteAddr-STM32_FLASH_BASE; //實際偏移地址.secpos=offaddr/STM_SECTOR_SIZE; //扇區地址(落在第幾頁sector) 0~127 for STM32F103RBT6secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇區內的偏移(2個字節為基本單位.)secremain=STM_SECTOR_SIZE/2-secoff; //扇區剩余空間大小 if(NumToWrite<=secremain)secremain=NumToWrite;//不大于該扇區范圍while(1) { //讀到buf數組里面 STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//讀出整個扇區的內容for(i=0;i<secremain;i++)//校驗數據{if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除 擦除成功都是FF }if(i<secremain)//需要擦除{FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除這個扇區for(i=0;i<secremain;i++)//復制到flash里面{STMFLASH_BUF[i+secoff]=pBuffer[i]; }STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//寫入整個扇區 }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//寫已經擦除了的,直接寫入扇區剩余區間. if(NumToWrite==secremain)break;//寫入結束了 就是沒跨越頁else//寫入未結束 跨頁了{secpos++; //扇區地址增1secoff=0; //偏移位置為0 pBuffer+=secremain; //指針偏移WriteAddr+=(secremain*2); //寫地址偏移 NumToWrite-=secremain; //字節(16位)數遞減if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一個扇區還是寫不完else secremain=NumToWrite;//下一個扇區可以寫完了} }; FLASH_Lock();//上鎖 } #endif//從指定地址開始讀出指定長度的數據 //ReadAddr:起始地址 //pBuffer:數據指針 //NumToWrite:半字(16位)數 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) {u16 i;for(i=0;i<NumToRead;i++){pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//讀取2個字節(半字)ReadAddr+=2;//偏移2個字節. } }// //WriteAddr:起始地址 //WriteData:要寫入的數據 void Test_Write(u32 WriteAddr,u16 WriteData) {STMFLASH_Write(WriteAddr,&WriteData,1);//寫入一個字 }main.c
#include "led.h" #include "delay.h" #include "key.h" #include "sys.h" #include "lcd.h" #include "usart.h" #include "stmflash.h"/************************************************ALIENTEK戰艦STM32開發板實驗34FLASH模擬EEPROM 實驗 技術支持:www.openedv.com淘寶店鋪:http://eboard.taobao.com 關注微信公眾平臺微信號:"正點原子",免費獲取STM32資料。廣州市星翼電子科技有限公司 作者:正點原子 @ALIENTEK ************************************************///要寫入到STM32 FLASH的字符串數組 const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"}; #define SIZE sizeof(TEXT_Buffer) //數組長度 #define FLASH_SAVE_ADDR 0X08070000 //設置FLASH 保存地址(必須為偶數,且其值要大于本代碼所占用FLASH的大小+0X08000000)int main(void){ u8 key;u16 i=0;u8 datatemp[SIZE];delay_init(); //延時函數初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置中斷優先級分組為組2:2位搶占優先級,2位響應優先級uart_init(115200); //串口初始化為115200LED_Init(); //初始化與LED連接的硬件接口KEY_Init(); //初始化按鍵LCD_Init(); //初始化LCD POINT_COLOR=RED; //設置字體為紅色 LCD_ShowString(30,50,200,16,16,"WarShip STM32"); LCD_ShowString(30,70,200,16,16,"FLASH EEPROM TEST"); LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(30,110,200,16,16,"2015/1/18"); LCD_ShowString(30,130,200,16,16,"KEY1:Write KEY0:Read");while(1){key=KEY_Scan(0);if(key==KEY1_PRES) //KEY1按下,寫入STM32 FLASH{LCD_Fill(0,170,239,319,WHITE);//清除半屏 LCD_ShowString(30,170,200,16,16,"Start Write FLASH....");STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);LCD_ShowString(30,170,200,16,16,"FLASH Write Finished!");//提示傳送完成}if(key==KEY0_PRES) //KEY0按下,讀取字符串并顯示{LCD_ShowString(30,170,200,16,16,"Start Read FLASH.... ");STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);LCD_ShowString(30,170,200,16,16,"The Data Readed Is: ");//提示傳送完成LCD_ShowString(30,190,200,16,16,datatemp);//顯示讀到的字符串}i++;delay_ms(10); if(i==20){LED0=!LED0;//提示系統正在運行 i=0;} } }串口IAP實驗(在程序中編程In Application Programming)
STM32IAP介紹
IAP運作流程
IAP配置
bootloader的三個作用(程序要實現的功能):
1、接收APP的bin文件
2、將這個文件寫到flash的某個區域
3、實現跳轉
實驗程序
usart.h
#ifndef __USART_H #define __USART_H #include "stdio.h" #include "sys.h" #define USART_REC_LEN 55*1024 //定義最大接收字節數 55K字節,bin文件不能大于55k 實際應用中根據工程需要修改 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收extern u8 USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節.末字節為換行符 extern u16 USART_RX_STA; //接收狀態標記 extern u16 USART_RX_CNT; //接收的字節數 //如果想串口中斷接收,請不要注釋以下宏定義 void uart_init(u32 bound); #endifusart.c
#include "sys.h" #include "usart.h" // //如果使用ucos,則包括下面的頭文件即可. #if SYSTEM_SUPPORT_OS #include "includes.h" //ucos 使用 #endif// //加入以下代碼,支持printf函數,而不需要選擇use MicroLIB #if 1 #pragma import(__use_no_semihosting) //標準庫需要的支持函數 struct __FILE { int handle; }; FILE __stdout; //定義_sys_exit()以避免使用半主機模式 void _sys_exit(int x) { x = x; } //重定義fputc函數 int fputc(int ch, FILE *f) { while((USART1->SR&0X40)==0);//循環發送,直到發送完畢 USART1->DR = (u8) ch; return ch; } #endif /*使用microLib的方法*//* int fputc(int ch, FILE *f) {USART_SendData(USART1, (uint8_t) ch);while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} return ch; } int GetKey (void) { while (!(USART1->SR & USART_FLAG_RXNE));return ((int)(USART1->DR & 0x1FF)); } */#if EN_USART1_RX //如果使能了接收 //串口1中斷服務程序 //注意,讀取USARTx->SR能避免莫名其妙的錯誤 u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收緩沖,最大USART_REC_LEN個字節,并限定數組存放在SRAM區域的起始地址為0X20001000(前面留出一定空間給bootloader使用) //接收狀態 //bit15, 接收完成標志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字節數目 u16 USART_RX_STA=0; //接收狀態標記 u16 USART_RX_CNT=0; //接收的字節數 void uart_init(u32 bound) {//GPIO端口設置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘//USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串口接受中斷USART_Cmd(USART1, ENABLE); //使能串口1 }void USART1_IRQHandler(void)//每接收一個字節都要執行中斷服務函數 {u8 res; #ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定義了,說明使用ucosII了.OSIntEnter(); #endifif(USART1->SR&(1<<5))//接收到數據{ res=USART1->DR; if(USART_RX_CNT<USART_REC_LEN){USART_RX_BUF[USART_RX_CNT]=res;//接收到的數據存放在buf里,后續寫入指定flash區域USART_RX_CNT++; }} #ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定義了,說明使用ucosII了.OSIntExit(); #endif } #endifiap.h
#ifndef __IAP_H__ #define __IAP_H__ #include "sys.h" typedef void (*iapfun)(void); //定義一個函數類型的參數.#define FLASH_APP1_ADDR 0x08010000 //第一個應用程序起始地址(存放在FLASH)//保留0X08000000~0X0800FFFF的空間(64K 根據實際大小來)為IAP使用void iap_load_app(u32 appxaddr); //執行flash里面的app程序,起始地址為appxaddr void iap_load_appsram(u32 appxaddr); //執行SRAM里面的app程序 void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen); //在flash指定地址appxaddr開始,寫入bin #endifiap.c
#include "sys.h" #include "delay.h" #include "usart.h" #include "stmflash.h"//要對flash進行操作,引入頭文件 #include "iap.h"iapfun jump2app; u16 iapbuf[1024]; //appxaddr:應用程序的起始地址 //appbuf:應用程序CODE. //appsize:應用程序大小(字節). void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize) {u16 t;u16 i=0;u16 temp;u32 fwaddr=appxaddr;//當前寫入的地址u8 *dfu=appbuf;for(t=0;t<appsize;t+=2){ temp=(u16)dfu[1]<<8;temp+=(u16)dfu[0]; dfu+=2;//偏移2個字節iapbuf[i++]=temp; if(i==1024){i=0;STMFLASH_Write(fwaddr,iapbuf,1024); fwaddr+=2048;//偏移2048 16=2*8.所以要乘以2.}}if(i)STMFLASH_Write(fwaddr,iapbuf,i);//將最后的一些內容字節寫進去. }//跳轉到應用程序段 //appxaddr:用戶代碼起始地址. void iap_load_app(u32 appxaddr) {if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //檢查棧頂地址是否合法.{ jump2app=(iapfun)*(vu32*)(appxaddr+4); //用戶代碼區第二個字為程序開始地址(復位地址) MSR_MSP(*(vu32*)appxaddr); //初始化APP堆棧指針(用戶代碼區的第一個字用于存放棧頂地址)jump2app(); //跳轉到APP.} }main.c
#include "led.h" #include "delay.h" #include "key.h" #include "sys.h" #include "lcd.h" #include "usart.h" #include "stmflash.h" #include "iap.h"int main(void) { u8 t;u8 key;u16 oldcount=0; //老的串口接收數據值u16 applenth=0; //接收到的app代碼長度u8 clearflag=0; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶占優先級,2位響應優先級uart_init(115200); //串口初始化為115200delay_init(); //延時初始化 LED_Init(); //初始化與LED連接的硬件接口KEY_Init(); //初始化按鍵LCD_Init(); //初始化LCD POINT_COLOR=RED;//設置字體為紅色 LCD_ShowString(30,50,200,16,16,"Warship STM32"); LCD_ShowString(30,70,200,16,16,"IAP TEST"); LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(30,110,200,16,16,"2015/1/27"); LCD_ShowString(30,130,200,16,16,"KEY_UP:Copy APP2FLASH");LCD_ShowString(30,150,200,16,16,"KEY2:Erase SRAM APP");LCD_ShowString(30,170,200,16,16,"KEY1:Run FLASH APP");LCD_ShowString(30,190,200,16,16,"KEY0:Run SRAM APP");POINT_COLOR=BLUE;//顯示提示信息POINT_COLOR=BLUE;//設置字體為藍色 while(1){if(USART_RX_CNT){if(oldcount==USART_RX_CNT)//新周期內,沒有收到任何數據,認為本次數據接收完成.{applenth=USART_RX_CNT;oldcount=0;USART_RX_CNT=0;printf("用戶程序接收完成!\r\n");//程序接收完成printf("代碼長度:%dBytes\r\n",applenth);}else oldcount=USART_RX_CNT; }t++;delay_ms(10);if(t==30){LED0=!LED0;t=0;if(clearflag){clearflag--;if(clearflag==0)LCD_Fill(30,210,240,210+16,WHITE);//清除顯示}} key=KEY_Scan(0); //按鍵掃描if(key==WKUP_PRES)//按鍵按下{if(applenth){printf("開始更新固件...\r\n"); LCD_ShowString(30,210,200,16,16,"Copying APP2FLASH...");if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)//判斷是否為0X08XXXXXX.{ iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//將bin程序寫入到FLASH LCD_ShowString(30,210,200,16,16,"Copy APP Successed!!");printf("固件更新完成!\r\n"); }else {LCD_ShowString(30,210,200,16,16,"Illegal FLASH APP! "); printf("非FLASH應用程序!\r\n");}}else {printf("沒有可以更新的固件!\r\n");LCD_ShowString(30,210,200,16,16,"No APP!");}clearflag=7;//標志更新了顯示,并且設置7*300ms后清除顯示 }if(key==KEY2_PRES)//按鍵2按下{if(applenth){ printf("固件清除完成!\r\n"); LCD_ShowString(30,210,200,16,16,"APP Erase Successed!");applenth=0;//置0清除固件}else {printf("沒有可以清除的固件!\r\n");LCD_ShowString(30,210,200,16,16,"No APP!");}clearflag=7;//標志更新了顯示,并且設置7*300ms后清除顯示 }if(key==KEY1_PRES)//按鍵1按下{printf("開始執行FLASH用戶代碼!!\r\n");if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判斷是否為0X08XXXXXX.{ iap_load_app(FLASH_APP1_ADDR);//跳轉執行FLASH APP代碼}else {printf("非FLASH應用程序,無法執行!\r\n");LCD_ShowString(30,210,200,16,16,"Illegal FLASH APP!"); } clearflag=7;//標志更新了顯示,并且設置7*300ms后清除顯示 }if(key==KEY0_PRES)//按鍵0按下{printf("開始執行SRAM用戶代碼!!\r\n");if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判斷是否為0X20XXXXXX.{ iap_load_app(0X20001000);//執行SRAM地址程序}else {printf("非SRAM應用程序,無法執行!\r\n");LCD_ShowString(30,210,200,16,16,"Illegal SRAM APP!"); } clearflag=7;//標志更新了顯示,并且設置7*300ms后清除顯示 } } }APP 程序起始地址設置方法
詳細參照開發手冊。
中斷向量表的偏移量設置方法
藍牙模塊HC05使用
模塊介紹
模塊AT指令集
與上位機串口通信
與手機藍牙調試助手通信
藍牙模塊接收到手機端發來的數據,通過串口發送出去,在上位機使用串口調試助手可以查看到。
與單片機連接通信
總結
- 上一篇: 扫雷游戏(实现了网页版的扫雷游戏的所有逻
- 下一篇: iOS开发学习-nonatomic和at