【问题标题】:Reading input GPIO on STM Discovery board读取 STM Discovery 板上的输入 GPIO
【发布时间】:2017-10-07 06:28:46
【问题描述】:

我正在尝试读取并行发送到 STM32L476VG MCU 探索板的 8 位值。数据的第 7 位和第 6 位分别被发送到引脚 PC15 和 PC14,而第 6-0 位被发送到引脚 PE15-PE10。我在示波器上测试了这些引脚的电线,以确保实际上有信号进入电路板。我很确定有问题的 GPIO 引脚已正确初始化为输入:

void init_adc_gpio (void) {
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;        // Enable clock for GPIOC
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOEEN;        // GPIOE
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOHEN;        // GPIOH
    GPIOC->MODER &= (uint32_t)0x0FFFFFFFU;  // Pins 14-15 of C -> input (2 most significant bits of ADC data)
    GPIOE->MODER &= (uint32_t)0x000FFFFFU;  // Pins 10-15 of E -> input (6 least significant bits of ADC data)
    GPIOH->MODER &= (uint32_t)0xFFFFFFFCU;  // Pin 0 of H -> input (ADC data ready flag)
}

我正在尝试使用此函数读取 8 位数据,只要设置了标志(表示来自 ADC 的数据已准备好进行处理),就会调用该函数:

uint8_t read_adc_data (void) {
  uint8_t adc_data;
  adc_data = ((GPIOC->IDR & (uint32_t)0x0000C000U) >> 8);
  adc_data |= ((GPIOE->IDR & (uint32_t)0x0000FC00U) >> 10);
  return adc_data;
}

但是,根据调试,由于某种原因,adc_data 始终为 0。即使将其更改为此也不起作用:

uint8_t read_adc_data (void) {
  uint8_t adc_data;
  adc_data = (GPIOC->IDR >> 8) | (GPIOE->IDR >> 10);
  return adc_data;
}

我觉得我在这里遗漏了一些非常明显的东西,但我的教授和他的助手们也想不通。

【问题讨论】:

  • 向上搜索一级。 GPIOx->IDR 寄存器是否包含正确的值?

标签: c input stm32 gpio


【解决方案1】:

检查RCC和/或MODER寄存器是否正确应用。
设置 RCC 寄存器后尝试添加一些延迟。

也许你的编译器优化了一些东西,但不满足以下条件:

当外设时钟未激活时,不支持外设寄存器读取或写入访问。
使能位具有同步机制,可为外设创建无毛刺时钟。
设置使能位后,在时钟激活之前有 2 个时钟周期延迟。
注意:
为外设启用时钟后,软件必须等待延迟才能访问外设寄存器

编辑:

注意:以下是一种解决方法,似乎是错误的。 有关详细信息,请参阅 cmets。

我刚刚编译了上面的 init_adc_gpio 函数,我的编译器生成了以下汇编指令(-O3):

  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;        // Enable clock for GPIOC
0x080004d0  ldr r3, [pc, 60]    ; (0x8000510 <init_adc_gpio+64>) 
  GPIOE->MODER &= (uint32_t)0x000FFFFFU;  // Pins 10-15 of E -> input (6 least significant bits of ADC data)
0x080004d2  ldr r0, [pc, 64]    ; (0x8000514 <init_adc_gpio+68>) 
0x080004d4  ldr r2, [r3, 76]    ; 0x4c 
  GPIOH->MODER &= (uint32_t)0xFFFFFFFCU;  // Pin 0 of H -> input (ADC data ready flag)
0x080004d6  ldr r1, [pc, 64]    ; (0x8000518 <init_adc_gpio+72>) 
0x080004d8  orr.w r2, r2, 4 
  void init_adc_gpio(void) {
0x080004dc  push {r4} 
0x080004de  str r2, [r3, 76]    ; 0x4c 
  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOEEN;        // GPIOE
0x080004e0  ldr r2, [r3, 76]    ; 0x4c 
  GPIOC->MODER &= (uint32_t)0x0FFFFFFFU;  // Pins 14-15 of C -> input (2 most significant bits of ADC data)
0x080004e2  ldr r4, [pc, 56]    ; (0x800051c <init_adc_gpio+76>) 
0x080004e4  orr.w r2, r2, 16 
0x080004e8  str r2, [r3, 76]    ; 0x4c 
  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOHEN;        // GPIOH
0x080004ea  ldr r2, [r3, 76]    ; 0x4c 
0x080004ec  orr.w r2, r2, 128   ; 0x80 
0x080004f0  str r2, [r3, 76]    ; 0x4c 
0x080004f2  ldr r3, [r4, 0] 
0x080004f4  bic.w r3, r3, 4026531840    ; 0xf0000000 
0x080004f8  str r3, [r4, 0] 
0x080004fa  ldr r3, [r0, 0] 
  }
0x080004fc  ldr.w r4, [sp], 4 
0x08000500  ubfx r3, r3, 0, 20 
0x08000504  str r3, [r0, 0] 
0x08000506  ldr r3, [r1, 0] 
0x08000508  bic.w r3, r3, 3 
0x0800050c  str r3, [r1, 0] 
0x0800050e  bx  lr
0x08000510  .word   0x40021000 ; [RCC]
0x08000514  .word   0x48001000 ; [GPIOE]
0x08000518  .word   0x48001c00 ; [GPIOH]
0x0800051c  .word   0x48000800 ; [GPIOC]

如您所见,编译器对指令重新排序。因此寄存器设置不正确。 注意:其实顺序是对的。反汇编程序显示它的方式可能会误导某人。

要解决此问题,您可以使用 explicit compiler barrier 来防止编译器重新排序命令:

void init_adc_gpio(void) {
  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;     // Enable clock for GPIOC
  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOEEN;    // GPIOE
  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOHEN;    // GPIOH
  asm("" ::: "memory"); // prevent instruction reordering
  GPIOC->MODER &= (uint32_t)0x0FFFFFFFU;  // Pins 14-15 of C -> input (2 most significant bits of ADC data)
  GPIOE->MODER &= (uint32_t)0x000FFFFFU;  // Pins 10-15 of E -> input (6 least significant bits of ADC data)
  GPIOH->MODER &= (uint32_t)0xFFFFFFFCU;  // Pin 0 of H -> input (ADC data ready flag)
}

设置MODER寄存器之前的简单nop指令也应该这样做。

注意:编译器障碍导致汇编代码略有不同。但两者仍然正确。

【讨论】:

  • 在上面的代码中存在至少 4 个(可能更多)时钟周期的隐式延迟,因为在为每个外设设置启用位之后,之前还有 2 个读取-修改-写入操作再次触摸该外围设备。
  • 外设寄存器在控制器头文件中声明为volatile,世界上没有编译器会优化对它们的访问。
  • 你是对的,这应该不是问题。但尽管如此,我会检查这些寄存器是否设置正确。在运行时也是如此。否则不会有其他问题。
  • 您所看到的是编译器重新排序来自文字池的地址加载(ldr 指令与 PC 相对寻址)。对始终声明为volatile 的硬件寄存器的访问永远不会重新排序。如果插入内存屏障完全改变了汇编代码,我会感到惊讶。
  • 我修好了!事实证明,文件 LED.c 和 LED.h 是罪魁祸首。 LED.c 初始化了端口 E 的 GPIO 引脚,我打算将其用于数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多