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.
source to share
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.
source to share