【问题标题】:Writing to non-volatile memory without disrupting UART interrupts execution on STM32F4XX在 STM32F4XX 上写入非易失性存储器而不中断 UART 中断执行
【发布时间】:2019-07-21 07:31:40
【问题描述】:

我在 UART 外围设备上有几个 OVERRUN 错误,因为我一直在接收 UART 数据,而 我的代码因为我在闪存上执行写操作而停止

我正在为 UART 使用中断,并在应用笔记AN3969 中对此进行了解释:

EEPROM 仿真固件从内部 Flash 运行,因此可以访问 闪存将在需要闪存擦除的操作期间停止或 编程(EEPROM 初始化、变量更新或页擦除)。作为 结果,应用程序代码没有被执行并且中断 无法提供服务。

这种行为对于许多应用程序来说可能是可以接受的,但是对于 具有实时约束的应用程序,您需要运行关键的 从内部 RAM 处理。

在这种情况下:

  1. 在内部 RAM 中重新定位向量表。
  2. 从内部 RAM 执行所有关键进程并中断服务例程。编译器提供了一个关键字来声明函数 作为 RAM 功能;该函数从 Flash 复制到 RAM 系统启动就像任何初始化变量一样。重要的是要 请注意,对于 RAM 函数,所有使用的变量和调用 函数应该在 RAM 中。

所以我在互联网上搜索并找到了AN4808,它提供了有关如何在闪存操作时保持中断运行的示例。

我继续修改我的代码:

链接器脚本将向量表添加到 SRAM 并定义 .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 = .);

Reset Handler添加了向量表副本 SRAM 并定义了一个 .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 相关函数和原型之前添加了 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 功能正常运行,但我仍然在 USART 上出现溢出。在执行闪存写入操作时,我也没有禁用中断。

我也尝试从 CCM RAM 而不是 SRAM 运行代码,但我在 this post 上看到代码无法在STMF32F4XX 上的 CCM RAM 上执行...

有什么想法吗?谢谢。

【问题讨论】:

    标签: interrupt stm32 ram uart stm32f4


    【解决方案1】:

    任何在写入操作正在进行时尝试从闪存读取都会导致总线停止。

    为了不被闪存写入阻塞,我认为不仅中断代码,而且被中断的函数也必须从 RAM 中运行,否则内核无法进入可能中断的状态。

    尝试将闪存处理代码重新定位到 RAM。

    如果可能,我建议切换到具有两个独立闪存组的 MCU,例如引脚和软件兼容的 427/429/437/439 系列。您可以将一个 bank 用于编程代码,另一个用于类似 EEPROM 的数据存储,然后写入第二个 bank 不会干扰从第一个 bank 运行的代码。

    【讨论】:

    • 我有一种感觉,即使您从 RAM 运行,在 STM32 上的闪存写入期间的某个时间点也会出现停顿,这是不可避免的。我不记得确切的问题。也许克利福德会看到他所知道的这一点。
    • 是的,我尝试将 Flash 写入处理代码从 Flash 扇区移到 RAM,但到目前为止没有任何成功。
    • 如果有其他从闪存运行的活动中断处理程序,disable all of them。被调用的函数,GPIO_SET()GPIO_CLR() 是否在 RAM 中?是否从 EEPROM 仿真代码调用的函数,例如StdPeriph 也在 RAM 中调用?
    • @berendi GPIO_CLR 和 SET 实际上是用于设置输出引脚状态的宏。我将尝试将所有调用的子函数放入 RAM 中,然后告诉你它是如何进行的。谢谢。 # define GPIO_CLR(port,pin) (port-&gt;BSRRH = (1 &lt;&lt; pin)) # define GPIO_SET(port,pin) (port-&gt;BSRRL = (1 &lt;&lt; pin))
    • 好吧,我被困住了......有没有一种好方法可以识别哪些函数应该放在 RAM 中,以及哪些函数留在闪存中导致总线停止?
    猜你喜欢
    • 2021-06-19
    • 2019-11-09
    • 2021-11-21
    • 1970-01-01
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多