簡體   English   中英

寫入非易失性 memory 而不中斷 UART 中斷在 STM32F4XX 上的執行

[英]Writing to non-volatile memory without disrupting UART interrupts execution on STM32F4XX

我在 UART 外圍設備上有幾個 OVERRUN 錯誤,因為我在代碼停止時一直接收 UART 數據,因為我正在 flash 上執行寫操作

我正在為 UART 使用中斷,並在應用筆記AN3969中有說明:

EEPROM 仿真固件從內部 Flash 運行,因此在需要 Flash 擦除或編程(EEPROM 初始化、變量更新或頁面擦除)的操作期間,對 Flash 的訪問將停止。 結果,應用程序代碼不被執行,中斷不能被服務。

對於許多應用程序來說,這種行為可能是可以接受的,但是對於具有實時限制的應用程序,您需要從內部 RAM 運行關鍵進程。

在這種情況下:

  1. 重新定位內部 RAM 中的向量表。
  2. 從內部 RAM 執行所有關鍵進程和中斷服務程序。 編譯器提供了一個關鍵字來將函數聲明為 RAM function; function 在系統啟動時從 Flash 復制到 RAM,就像任何初始化變量一樣。 重要的是要注意,對於 RAM function,所有使用的變量和調用的函數都應該在 RAM 中。

所以我在互聯網上搜索並找到了 AN4808 ,它提供了有關如何在 flash 操作時保持中斷運行的示例。

我繼續修改我的代碼:

Linker 腳本:將向量表添加到 SRAM 並定義 a.ramfunc 部分

/* stm32f417.dld */
ENTRY(Reset_Handler)

MEMORY
{
    ccmram(xrw)       : ORIGIN = 0x10000000, LENGTH = 64k
    sram              : ORIGIN = 0x20000000, LENGTH = 112k
    eeprom_default    : ORIGIN = 0x08004008, LENGTH = 16376
    eeprom_s1         : ORIGIN = 0x08008000, LENGTH = 16k
    eeprom_s2         : ORIGIN = 0x0800C000, LENGTH = 16k
    flash_unused      : ORIGIN = 0x08010000, LENGTH = 64k
    flash             : ORIGIN = 0x08020000, LENGTH = 896k
}

_end_stack = 0x2001BFF0;

SECTIONS 
{
  . = ORIGIN(eeprom_default);
  .eeprom_data :
  {
    *(.eeprom_data)
  } >eeprom_default

  . = ORIGIN(flash);

  .vectors :
    {
        _load_vector = LOADADDR(.vectors);
        _start_vector = .;
        *(.vectors)
        _end_vector = .;
    } >sram AT >flash

    .text :
    {
        *(.text)
        *(.rodata)
        *(.rodata*)
        _end_text = .;
    } >flash

    .data : 
    {
        _load_data = LOADADDR(.data);
        . = ALIGN(4);
        _start_data = .;
        *(.data)
    } >sram AT >flash


    .ramfunc :
    {
        . = ALIGN(4);
        *(.ramfunc)
        *(.ramfunc.*)
        . = ALIGN(4);
        _end_data = .;
    } >sram AT >flash

    .ccmram :
    {
        _load_ccmram = LOADADDR(.ccmram);
        . = ALIGN(4);
        _start_ccmram = .;
        *(.ccmram)
        *(.ccmram*)
        . = ALIGN(4);
        _end_ccmram = .;
    } > ccmram AT >flash

    .bss :
    {
        _start_bss = .;
        *(.bss)
        _end_bss = .;
    } >sram

    . = ALIGN(4);

    _start_stack = .;


}

_end = .;
PROVIDE(end = .);

重置處理程序:添加向量表副本 SRAM 並定義 a.ramfunc 部分

void Reset_Handler(void)
{
  unsigned int *src, *dst;

  /* Copy vector table from flash to RAM */
  src = &_load_vector;
  dst = &_start_vector;
  while (dst < &_end_vector)
    *dst++ = *src++;

  /* Copy data section from flash to RAM */
  src = &_load_data;
  dst = &_start_data;
  while (dst < &_end_data)
    *dst++ = *src++;

  /* Copy data section from flash to CCRAM */
  src = &_load_ccmram;
  dst = &_start_ccmram;
  while (dst < &_end_ccmram)
    *dst++ = *src++;

  /* Clear the bss section */
  dst = &_start_bss;
  while (dst < &_end_bss)
    *dst++ = 0;

  SystemInit();
  SystemCoreClockUpdate();

  RCC->AHB1ENR = 0xFFFFFFFF;
  RCC->AHB2ENR = 0xFFFFFFFF;
  RCC->AHB3ENR = 0xFFFFFFFF;
  RCC->APB1ENR = 0xFFFFFFFF;
  RCC->APB2ENR = 0xFFFFFFFF;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOIEN;
  RCC->AHB1ENR |= RCC_AHB1ENR_CCMDATARAMEN;

  main();

  while(1);
}

system_stm32f4xxx.c:未注釋的 VECT_TAB_SRAM 定義

/*!< Uncomment the following line if you need to relocate your vector Table in
     Internal SRAM. */
#define VECT_TAB_SRAM
#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field. 
                                   This value must be a multiple of 0x200. */

添加了 RAMFUNC 的定義來設置節屬性:

#define RAMFUNC __attribute__ ((section (".ramfunc")))

在 UART 相關的 function 和原型之前添加了 RAMFUNC,因此它可以從 RAM 運行。

RAMFUNC void USART1_IRQHandler(void)
{
  uint32_t sr = USART1->SR;
  USART1->SR & USART_SR_ORE ? GPIO_SET(LED_ERROR_PORT, LED_ERROR_PIN_bp):GPIO_CLR(LED_ERROR_PORT, LED_ERROR_PIN_bp);
  if(sr & USART_SR_TXE)
  {

    if(uart_1_send_write_pos != uart_1_send_read_pos)
    {
      USART1->DR = uart_1_send_buffer[uart_1_send_read_pos];
      uart_1_send_read_pos = (uart_1_send_read_pos + 1) % USART_1_SEND_BUF_SIZE;
    }
    else
    {
      USART1->CR1 &= ~USART_CR1_TXEIE;
    }
  }

  if(sr & (USART_SR_RXNE | USART_SR_ORE)) 
  {
    USART1->SR &= ~(USART_SR_RXNE | USART_SR_ORE);
    uint8_t byte = USART1->DR;
    uart_1_recv_buffer[uart_1_recv_write_pos] = byte;
    uart_1_recv_write_pos = (uart_1_recv_write_pos + 1) % USART_1_RECV_BUF_SIZE;
  }
}

我的目標在 RAM 中使用矢量表和 UART function 正常運行,但我仍然在 USART 上出現溢出。 在執行 flash 寫操作時,我也沒有禁用中斷。

我還嘗試從 CCM RAM 而不是 SRAM 運行代碼,但是我在這篇文章中看到代碼不能在 STMF32F4XX 上的 CCM RAM 上執行...

任何想法? 謝謝。

正在進行寫操作時, 任何嘗試從閃存讀取的操作都會導致總線停頓。

為了不被閃存寫入所阻止,我認為不僅中斷代碼,而且中斷功能也必須從RAM運行,否則內核將無法進入可能發生中斷的狀態。

嘗試將閃存處理代碼重新定位到RAM。

如果可能的話,我建議您切換到具有兩個獨立的閃存組的MCU,例如與引腳和軟件兼容的427/429/437/439系列。 您可以將一個存儲區專用於編程代碼,將另一個存儲區專用於類似EEPROM的數據存儲,然后寫入第二個存儲區將不會干擾從第一個存儲區運行的代碼。

正如建議的那樣,可能需要從 RAM 執行代碼,或者更確切地說,確保在寫入過程中不執行任何 flash 讀取操作。

要進行測試,您可能需要為 ram 而不是 flash 編譯整個可執行文件。 IE 將所有內容放入 ram 中,根本不使用 flash。

然后,您可以使用 gdb 加載二進制文件並開始執行...測試您的 uart 並確保它按預期工作。 至少通過這種方式,您可以確定 flash 未被使用。

一些微控制器具有 READ WILE WRITE 部分,它們同時執行多個操作沒有問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM