2024-11-16 01:12 PM
Hello everyone,
I'm new to embedded development, and I'm trying to implement a kind of hello-world-level project to get acquainted with STM32 MCU architecture using arm assembly. In particular, I'm trying to set up ADC at STM32F103C8T6 Blue Pill board for a LED at PC13, which desired behaviour is blinking with frequency proportional to an external potentiometer's resistance at PA0. In other words, the lower is potentiometer resistance - the higher is blinking frequency and vice versa. Besides having set up all necessary clocks according to the Reference Manual (RM0008, Rev 21), I have also set up PA0 as analog input:
LDR r0, =0x40010800 // GPIOA_CRL, sets up low pins; GPIOA starts from 0x40010800 (Reference Manual, p. 51)
LDR r1, [r0]
BIC r1, 0xF << 0 // Clear MODE0 and CNF0 bits for PA0 -> setting analog mode
STR r1, [r0]
...as well as put ADC1 on through ADON bit:
LDR r0, =0x40012400 + 0x08 // ADC_CR2 register for ADC1 (Reference Manual, p. 240)
LDR r1, [r0]
ORR r1, 1 << 0 // ADON bit - power on ADC1
STR r1, [r0]
The main loop below represents itself a modified version of my first successful "hello-world" code, where the LED at PC13 just blinked several times per second, but now with attempts to start single ADC conversion per iteration and read input values on PA0:
main_loop: // ADC conversion: LDR r0, =0x40012400 + 0x08 LDR r1, [r0] ORR r1, 1 << 22 // SWSTART bit - starts conversion STR r1, [r0]
// Waiting for the convresion to end: wait_adc: LDR r0, =0x40012400 // ADC_SR register for ADC1 (Reference Manual, p. 237) LDR r1, [r0] TST r1, 1 << 1 // Checking the EOC flag - end of conversion (Reference Manual, p. 237) BEQ wait_adc LDR r0, =0x40012400 + 0x4C // ADC_DR - regular data register (Reference Manual, p. 251) LDR r2, [r0] LDR r3, =1000000 // Base blink length, about 300+ ms MUL r0, r2, r3 // Trying to multiply the value from ADC by the base blink length, not sure if it is correct MOV r4, r0 // Waiting: delay_loop: SUBS r4, #1 BNE delay_loop // Toggling PC13: LDR r0, =0x40011000 + 0x0c // GPIOC_ODR (Reference Manual, p. 173) LDR r1, [r0] EOR r1, 1 << 13 // ODR 13 STR r1, [r0] B main_loop
As a result, the LED of interest just continuously glows, without reacting to the position of the potentiometer toggle (10 KOhm, connected to A0 with its middle pin, as well as to GND and +3.3 with two other pins). All electrical connections have been checked for not being damaged.
So far I have managed to figure out that `wait_adc` loop is endless, i.e. EOC flag seems to never be set. As far as I understand, this means that conversion actually never ends.
I have also tried adding a small delay loop right before `wait_adc` to wait for the ADC to stabilize (if I understand its timing correctly, according to the diagram in the Reference Manual, p. 220, Fig. 23), but that did not help.
Linker used is GNU ld. Thumb mode enabled, assembly instructions verified through manuals at arm's official website.
I would be thankful for your help with understanding the reasons of this failure.
Solved! Go to Solution.
2024-11-17 12:37 AM
To have ADC started by SWSTART, you need to set ADC_CR2.EXTSEL to 0b111.
The 'F1 has also a relatively bizarre functionality, where writing ADON twice starts a conversion, too. Read the description of these bits in RM.
JW
2024-11-16 02:07 PM
The reason for this failure would be much easier to catch if the program was written in C. Have you enabled the GPIOA and ADC in RCC?
2024-11-16 02:13 PM
You haven't shown us most of ADC setup, nor its registers' content after the setup, so we can only guess here.
The most common error is not enabling given peripheral's (here: ADC's) clock in RCC.
After you actually succeed with the ADC, 1000000 is too much to be multiplied with the ADC result which is to be between 0 and 4095.
JW
2024-11-16 02:56 PM
Hello @gbm and @waclawek.jan , thanks a lot for your replies.
I have enabled GPIOA in RCC:
LDR r0, =0x40021000 + 0x18 // RCC_APB2ENR register, (Reference Manual, pp. 112-113)
LDR r1, [r0]
ORR r1, 1 << 4 // IOPCEN 4th bit of RCC_APB2ENR, responsible for port C - LED
ORR r1, 1 << 2 // IOPAEN 2th bit of RCC_APB2ENR, responsible for port A - ADC
STR r1, [r0]
...as well as ADC1:
LDR r0, =0x40021000 + 0x18
LDR r1, [r0]
ORR r1, 1 << 9 // bit ADC1EN - enabling ADC1
STR r1, [r0]
...and configured CR2 for ADC1:
LDR r0, =0x40012400 + 0x08 // ADC_CR2 register for ADC1 (Reference Manual, p. 240)
LDR r1, [r0]
ORR r1, 1 << 0 // ADON bit - power on ADC1 (ibid)
STR r1, [r0]
...prior to entering the `main_loop`.
2024-11-17 12:37 AM
To have ADC started by SWSTART, you need to set ADC_CR2.EXTSEL to 0b111.
The 'F1 has also a relatively bizarre functionality, where writing ADON twice starts a conversion, too. Read the description of these bits in RM.
JW
2024-11-17 01:18 PM
Thanks a lot, this worked.
I also needed to enable conversion on external trigger by setting ADC_CR2.EXTTRIG to 1, - combined with your advice regarding ADC_CR2.EXTSEL, this resulted in successful LED blinking according to potentiometer's value at PA0.
Working solution looks like this:
LDR r0, =0x40012400 + 0x08 // ADC_CR2 register for ADC1 (Reference Manual, p. 240)
LDR r1, [r0]
ORR r1, 1 << 20 // EXTTRIG bit - enables conversion on external trigger (ibid)
ORR r1, 0x7 << 17 // EXTSEL[2:0] bits - should be set to 0b111 (0x7) to start conversion with SWSTART further (ibid)
ORR r1, 1 << 22 // SWSTART bit - starts conversion (ibid)
STR r1, [r0]