Reading GPIO Input on STM Discovery Board

I am trying to read an 8-bit value sent in parallel with the STM32L476VG MCU Discovery board. Bits 7 and 6 of data are sent to pins PC15 and PC14 respectively, and bits 6-0 are sent to pins PE15-PE10. I tested the wires on these pins on an oscilloscope to ensure there is a signal on the board. I'm pretty sure the corresponding GPIO pins are properly initialized as input:

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)
}

      

I'm trying to read 8-bit data using this function, which is called whenever a flag is set (which indicates that the data from the ADC is ready to be processed):

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;
}

      

However, according to the debug, adc_data is always 0 for some reason. Even changing this to this didn't work:

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

      

I feel like there is something ridiculously obvious that I am missing here, but my professor and his assistants couldn't figure it out either.

+3


source to share


1 answer


Check that the RCC and MODER registers are used correctly.
Try adding some delay after setting the RCC.

Perhaps your compiler is optimizing something and the following condition is not met:

When the peripheral clock is not active, peripheral read or write access registers are not supported.
The enable bit has a sync mechanism to create a sync clock for the peripheral.
After the enable bit is set, there will be 2 clock cycles before the clock is active. Note:
After enabling the peripheral clock, the software must wait for a delay before accessing the peripheral registers.

Edit:

Note. Below is the approach to the solution and seems to be wrong. See comments for details.

I just compiled the above init_adc_gpio function and my compiler generates the following assembler instructions (-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]

      

As you can see, the compiler reorders the instructions. Therefore, the registers are set incorrectly. Note: the sequence is actually correct. The way the demonstrator is showing can be misleading.

To solve this problem, you can use an explicit compiler that prevents the compiler from changing the order of commands:

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)
}

      

Simple nop instructions before setting the MODER registers should also do this.

Note. The compiler limit results in slightly different assembly code. But both are still true.

-1


source







All Articles