STM32 Bluepill GPIO write affects GPIO input level - Page 1
Forum section: electronics / general

author: rcbuck
author_rank: Frequent Contributor
timestamp: April 28, 2021, 06:30:36 pm
content: I am using a STM32 Bluepill module. Running Techman-001's Bluepill Diagnostice V1.62 gives this message "FAIL - JDEC manufacturer id NOT STMicroelectronics".I have GPIOA pins 0-6 configured as push-pull output and GPIOA pin 7 configured as an input with pullup resistor turned on. I have a switch on A7 that when turned on grounds A7 to change the way certain code operates.The STM32F103 family reference manual says this:When the I/O port is programmed as input The output buffer is disabledThe problem I have is A7 operates properly until I perform a GPIOA port write (Ex: GPIOA->ODR = 0x003F;). When I do this it sets A7 low so the next read of A7 returns a 0 instead of the 1 that should be there when the switch is not closed.. It should return 1 regardless of what I write to A7 since the output buffer is disabled according to the ref manual.I have found two work ways to get around the problem. If I read A7 at CPU startup and don't read it again everything works. However, this requires a CPU reset every time the switch is operated. I want to detect the change without a CPU reset. The second work around is to perform this operation: GPIOA->ODR = 0x003F | 0x0080; This will result in the switch change being detected immediately. Not good, but workable.The larger problem concerns GPIOB. I have a couple of inputs there that change at random times plus PB6 and PB7 are being used for the UART. There is fairly constant data coming over the UART. I also have outputs on GPIOB that need to change at certain times. These output changes should no interfere with the inputs on GPIOB. I haven't implemented the code for GPIOB yet so I don't know if the outputs will mess up the inputs. But from what I have seen on GPIOA my guess is they will.Am I doing something wrong or missing something? Or is the issue being caused by a fake STM32F103 on the Bluepill board? Has anyone seen this problem with a real STM32F103?I tried performing a lock on GPIOA by using the GPIOA_LCKR function but it had no effect on A7 being upset by a GPIOA write.

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 28, 2021, 06:34:59 pm
content: When in input "pull" mode, ODR is used to select pullup / pulldown:That's whyGPIOA->ODR = 0x003F | 0x0080works, you're maskingODR.If you write0toODR, you are enabling the pulldown for A7!

author: rcbuck
author_rank: Frequent Contributor
timestamp: April 28, 2021, 07:42:03 pm
content: DavidAlfa, thanks for the reply. I now see what is happening. I missed that in reading through the GPO section.Is there any way to write output data to the entire port other than the ODR register?

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 28, 2021, 08:30:48 pm
content: Not really. Pullups are annoying in stm32F1 series.If you can afford an external 100K resistor, it'll be much easier.BSRR would do the same as it also changes ODR.You will have to keep track of every pin set in pull mode and mask any writes to the ODR register.If the pins configured in Pull mode are static, just write a function or a macro for writing into ODR that appplies the mask automatically, so don't have to put the "| 80" in every call.Maybe:#define write_ODR_A(x) GPIOA->ODR = x | 0x80If the pull pins are dynamic, you will need to store the changes and apply the stored mask upon every write.I made this as example, works nicely.Code:volatile uint16_t A_mask=0, B_mask=0, C_mask=0;void reset_GPIO_Mask(void){A_mask = B_mask = C_mask = 0;}Code:void update_GPIO_Mask(GPIO_TypeDef *port, uint16_t Pin, GPIO_PinState state){switch((uint32_t)port){case (uint32_t)GPIOA:A_mask = state ? (A_mask | Pin) : (A_mask & ~(Pin));break;case (uint32_t)GPIOB:B_mask = state ? (B_mask | Pin) : (B_mask & ~(Pin));break;case (uint32_t)GPIOC:C_mask = state ? (C_mask | Pin) : (C_mask & ~(Pin));break;}}Code:void write_GPIO_ODR(GPIO_TypeDef *port, uint16_t value){switch((uint32_t)port){case (uint32_t)GPIOA:port->ODR = value | A_mask;break;case (uint32_t)GPIOB:port->ODR = value | B_mask;break;case (uint32_t)GPIOC:port->ODR = value | C_mask;break;}}Code:update_GPIO_Mask(GPIOC, Button1_Pin, SET); // Button1_Pin protectedwrite_GPIO_ODR(GPIOC, 0xFFFF);write_GPIO_ODR(GPIOC, 0);update_GPIO_Mask(GPIOC Button1_Pin, RESET); // Button1_Pin unprotectedwrite_GPIO_ODR(GPIOC, 0xFFFF);write_GPIO_ODR(GPIOC, 0);There might be better ways, who want to say anything about my code?:

author: rcbuck
author_rank: Frequent Contributor
timestamp: April 28, 2021, 09:16:30 pm
content: DavidAlfa, your macro solution looks good for 1 or 2 inputs. I have 2 inputs on GPIOA and 4 inputs on GPIOB. The pullup pins are static so the macro would work for all 6 inputs. Just out of curiosity I will test it.I am working with version 1 of my PCB. I have plenty of room to add the 6 pullup resistors for GPIOA and GPIOB. I will temporarily tack solder them in so I can continue with my code. I will add the resistors to version 2 when I order new boards. I will remember to avoid pullups on any future STM32F1xx projects.My understanding is the UART receive line will not be impacted by any GPIO writes. Hopefully that turns out to be the case.

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 28, 2021, 09:41:39 pm
content: GPIO AF mode disconnects the pin from ODR (See "Don't care" in the pic?).Unless you need a huge troughput at the pins, that's an acceptable solution.You see all the problems became nothing, calling a helper to always apply the mask.Best is to avoid the resistors when possible, it'll save resources and reduce the factor for problems.Any resistor breaking or not getting get soldered correctly...Or you suddenly make a change in your design and now you need to enable and disable the pullups.The strange is that F0 famility also uses GPIOx_PUPDR register, like F4 series. I don't know what F2 and F3 use.It's like they did it intentionally to annoys you to the point you go mad,throw all away and buy F4.Only 1 or 2 inputs? You can put whatever the mask you want, covering all the 16 pins in any order:Pin15 ( ... ) Pin0#define MASK_A 0b1010101011110100#define MASK_B 0b0000011100000001#define write_ODR_A(x) GPIOA->ODR = x | MASK_A#define write_ODR_B(x) GPIOB->ODR = x | MASK_B

author: rcbuck
author_rank: Frequent Contributor
timestamp: April 29, 2021, 02:39:12 am
content: DavidAlfa,I used your macro for all 6 inputs. Everything works as planned. I guess I don't need to make a version 2 PCB.Thanks for the help.

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 29, 2021, 06:34:34 am
content: Nice! Good luck with it