1. 程式人生 > 其它 >13-CubeMx+Keil+Proteus模擬STM32 - Flash ROM

13-CubeMx+Keil+Proteus模擬STM32 - Flash ROM

本文例子參考《STM32微控制器開發例項——基於Proteus虛擬模擬與HAL/LL庫》
原始碼:https://github.com/LanLinnet/STM33F103R6

專案要求

微控制器將由串列埠收到的1位元組資料存入Flash ROM的指定地址;按下按鈕BTN,微控制器將儲存在Flash ROM指定地址的位元組資料通過串列埠傳送。串列埠通訊引數:波特率為19200bit/s,無校驗。

硬體設計

  1. 第一節的基礎上,在Proteus中新增電路如下圖所示。其中我們添加了:串列埠元件COMPIM,用於連線計算機虛擬串列埠;

    除錯過程也可以新增一個虛擬儀器VIRTUAL TERMINAL,用來檢視微控制器收到的串列埠資料,具體參考

    第11節
    由於要實現串列埠通訊,我們要將其波特率、字長、校驗方式、停止位等都設定一下,具體引數如下圖所示
    COMPIM設定

  2. Flash ROM簡介:STM32微控制器Flash ROM(程式儲存器)的作用是存放使用者編寫的微控制器程式(機器碼),但是其除了用來存放微控制器的程式外,也可以用來儲存一些既可以修改又能斷電儲存的資料,如裝置或模組的設定引數。但是在實際中,由於STM32微控制器的Flash ROM擦除次數有限,因此不建議在Flash ROM擦寫,可以通過外擴\(E^2 PROM\)、FRAM、儲存卡等方式實現保護產品設定引數的目的。不過為了熟悉Flash ROM操作,本節我們使用Flash ROM來儲存資料。
    1)STM32F103R6微控制器具有32KB的FlashROM,地址為0x0800 0000 ~ 0x0800 7FFF,每KB為一頁,共32頁。
    2)Flash ROM資料寫入步驟:Flash ROM解鎖 → 擦除扇區 → 向指定地址寫入資料 → Flash ROM鎖定。
    3)Flash ROM資料讀取沒有繁瑣的步驟,直接讀取即可。

  3. 開啟CubeMX,建立工程。
    首先,設定PA5為GPIO_Input
    然後,點選“Connectivity”列表中的“USART”進行串列埠配置。將Mode設定為Asynchronous(非同步),波特率設為19200Bits/s,字長設為8Bits,校驗設為None,停止位設為1,資料傳送設為Receive and Transmit(接收與傳送)。設定完成後,會看到右側的PA9和PA10引腳被自動設定為USART1_TXUSART1_RX,即USART1的傳送端和接收端。

    隨後,再點選“NVIC Settings”,選中USART global interrupt,使能Enabled串列埠1的中斷功能。

  4. 點選“Generator Code”生成Keil工程。

軟體編寫

  1. 本次我們需要實現串列埠助手傳送單位元組資料,微控制器收到資料後存入Flash ROM,按鍵按下後將儲存的資料通過串列埠發回串列埠助手,需要用到Flash ROM相關函式其API文件如下:
    HAL_FLASH_Unlock 解鎖Flash ROM函式

    HAL_FLASH_Lock 鎖定Flash ROM函式

    HAL_FLASHEx_Erase 擦除Flash ROM指定部分函式

    HAL_FLASH_Program 將資料寫入Flash ROM函式

    其中,TypeErase形參有以下2個巨集定義

    TypeProgram有以下3個巨集定義

  2. 點選“Open Project”在Keil中開啟工程,雙擊“main.c”檔案。

  3. 首先我們需要在main.c檔案中的最前面設定全域性變數、宣告自定義函式。

    /* Private macro -------------------------------------------------------------*/
    /* USER CODE BEGIN PM */
    #define _FLASH_ADD 0x08006400  //寫入Flash ROM首地址(Page 25)
    /* USER CODE END PM */
    
    /* Private variables ---------------------------------------------------------*/
    
    /* USER CODE BEGIN PV */
    uint8_t rf = 0;		//自定義串列埠接收完畢標誌
    uint8_t RcvBuf[1];		//接收緩衝
    uint8_t SndBuf[1];		//傳送緩衝
    /* USER CODE END PV */
    
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    /* USER CODE BEGIN PFP */
    void FlashErase(uint32_t Add);		//宣告自定義Flash ROM擦除函式
    void FlashWrite(uint32_t Add, uint16_t Dat);		//宣告自定義Flash ROM寫函式
    uint16_t FlashRead(uint32_t Add);		//宣告自定義Flash ROM讀函式
    /* USER CODE END PFP */
    

    然後,在main函式中中插入程式碼如下,定義中間變數,開啟串列埠1接收中斷

    /* USER CODE BEGIN 1 */
    uint16_t flash_wdat;  //寫入Flash資料儲存變數
    /* USER CODE END 1 */
    
    /* USER CODE BEGIN 2 */
    //開啟串列埠1接收中斷,接收資料存入RcvBuf陣列,陣列長度為1
    HAL_UART_Receive_IT(&huart1,RcvBuf,1);
    /* USER CODE END 2 */
    

    隨後,在/* USER CODE BEGIN 4 *//* USER CODE END 4 */中插入接收完畢回撥函式、自定義的Flash頁擦除函式、Flash寫函式、Flash讀函式程式碼如下

    /* USER CODE BEGIN 4 */
    //串列埠接收完畢回撥函式
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      if(huart==&huart1)		//如果串列埠1接收完畢
      {
        rf = 1;		//標誌位置1
      }
    }
    
    /*Flash頁擦除
    *-Add表示待擦除頁的首地址
    *-Flash必須整頁擦除,也就是整頁的每個地址單元內容都為FFH才能寫入新資料
    */
    void FlashErase(uint32_t Add)
    {
      uint32_t page_error = 0;  	//錯誤指標
      FLASH_EraseInitTypeDef erase_initstruct = 
      {
        .TypeErase = FLASH_TYPEERASE_PAGES,		//擦除方式為頁擦除
        .NbPages = 1,		//頁數量為1頁
        .PageAddress = Add		//擦除頁起始地址
      };
      HAL_FLASH_Unlock();		//解鎖Flash ROM
      HAL_FLASHEx_Erase(&erase_initstruct, &page_error);		//擦除
      HAL_FLASH_Lock();		//鎖定Flash ROM
    }
    
    /*Flash寫函式
    *-寫入一個Half Word(16位)型資料
    *-Add表示Flash ROM地址
    *-Dat表示寫入資料(16位)
    *-注意:寫入時,高位元組在高地址
    */
    void FlashWrite(uint32_t Add, uint16_t Dat)
    {
      HAL_FLASH_Unlock();
      HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, Add, Dat);		//將資料寫入Flash
      HAL_FLASH_Lock();
    }
    
    /*Flash讀函式
    *-返回一個Half Word(16位)型資料
    *-Add表示Flash ROM地址
    */
    uint16_t FlashRead(uint32_t Add)
    {
      uint16_t dat;
      dat = *(uint16_t *)Add;
      return dat;
    }
    /* USER CODE END 4 */
    

    最後,在while(1)中插入程式碼如下,進行Flash和串列埠相關操作

    /* USER CODE BEGIN WHILE */
    while (1)
    {
      if(rf == 1)		//串列埠接收完畢
      {
        rf = 0;		//標誌位清0
        flash_wdat = RcvBuf[0];		//將接收到的資料存入寫Flash變數中
        FlashErase(_FLASH_ADD);		//擦除指定部分
        FlashWrite(_FLASH_ADD, flash_wdat);		//寫入Flash
        HAL_UART_Receive_IT(&huart1, RcvBuf, 1);		//每次接收前都需要呼叫一次
      }
      if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET)		
      {
        HAL_Delay(25);		//消抖
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET)		//如果按鍵按下
        {
          SndBuf[0] = (uint8_t)FlashRead(_FLASH_ADD);		//讀Flash中值並存入傳送緩衝
          HAL_UART_Transmit(&huart1, SndBuf, 1, 10);		//由串列埠1傳送緩衝中的值
          while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET);		//等待按鍵鬆開
        }
      }
    /* USER CODE END WHILE */
    
    /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
    

聯合除錯

  1. 點選執行,生成HEX檔案。

  2. 在Proteus中載入相應HEX檔案,點選執行。

  3. 開啟串列埠除錯助手“XCOM”,選擇COM4,設定相應的波特率、停止位、資料位、奇偶校驗等,勾選“16進位制顯示”和“16進位制傳送”,點選“開啟串列埠”。在傳送框輸入“00”,點選“傳送”。在Proteus中我們可以看到“VIRTUAL TERMINAL”接收到資料“00”。按下按鍵,同時再觀察串列埠除錯助手“XCOM”,可以看到接收視窗收到資料“00”。同理,傳送“AA”和“BB”也能得到相應的結果。