-1

I want to create my own driver on stm32f1 board. I want to control MCU build_in led but I can't solve. I am trying to make PortC pin 13 pin as output (push-pull) mode, after that I want to set bit. but code isn't work

#include<stdint.h>
#define RCC_BASE_ADDR 0x40021000UL //RCC BASE ADDRESS
#define RCC_APB2_ENR_OFFSET 0x18UL 
#define RCC_APB2_ENR_ADDR (RCC_BASE_ADDR + RCC_APB2_ENR_OFFSET)
//#####################################
#define GPIOC_BASE_ADDR 0x40011000UL
#define GPIO_CTR_OFFSET 0x00UL //GPIO controll register
#define GPIO_CTR_ADDR (GPIOC_BASE_ADDR + GPIO_CTR_OFFSET)
#define GPIO_MODE_OUT_OFFSET 0x0CUL //GPIO OUTPUT MODE
#define GPIO_MODE_OUT_ADDR (GPIOC_BASE_ADDR + GPIO_MODE_OUT_OFFSET)
#define GPIO_BSRR_OFFSET 0x10UL //GPIO BIT SET RESET REGISTER
#define GPIO_BSRR_ADDR (GPIOC_BASE_ADDR + GPIO_BSRR_OFFSET)
int main()
{
 
 
 uint32_t *pRCCAPB2EnReg = (uint32_t *)RCC_APB2_ENR_ADDR;
 uint32_t *pGPIOCtr = (uint32_t *)GPIO_CTR_ADDR;
 uint32_t *pGPIOModeOut = (uint32_t *)GPIO_MODE_OUT_ADDR;
 uint32_t *pGPIOBSRR = (uint32_t *)GPIO_BSRR_ADDR;
 
 *pRCCAPB2EnReg |= (1<<4);
 
 *pGPIOCtr |= (0<<3);
 *pGPIOCtr |= (0<<2);
 *pGPIOCtr |= (0<<1);
 *pGPIOCtr |= (1<<0);
 
 *pGPIOModeOut |= (1<<13);
 
 *pGPIOBSRR |= (1<<13);
 
 return 0;
}
hcheung
4,4373 gold badges16 silver badges28 bronze badges
asked Sep 20, 2023 at 11:56
1
  • Also check there is no clock for the modules you need. Commented Sep 20, 2023 at 13:43

2 Answers 2

1

First, add a couple waste cycles after you enable GPIO port clock for changes to take effect. It can be anything really, as long as it's a couple of cycles (not nops tho, CPU is free to ignore nops as per ARM). Reading the bit you just set will be enough.

But it's not the main problem here. Let's open the reference manual RM0008 at page 172, GPIO registers.

You use the wrong GPIO control register to configure the GPIO. You use CRL with the offset of 0x00 (from GPIO Base), but it's only for the pins 0-7. Pins 8-15 are configured in the register CRH with the offset of 0x04.

enter image description here

We need to configure GPIOC pin 13. It will be in the register CRH, fields CNF13 (2 bits wide, starting at position 22) and MODE13 (2 bits wide, starting at position 20), which sit next to each other. The reset state of these fields is 0b0100 (CNF configured as floating input, MODE configured as input). We want to change it into output. First, we set MODE to anything non-zero (different speeds of output), such as 0b01. Note that the reset value of the registers in question is NOT all zeroes as per reference manual. Some bits are set upon boot, so you need to take extra care with that (such as clearing field bits before setting them).


If you don't want to bother with generalizing the code for an arbitrary pin number, you can do it manually for just this one pin:

*pGPIOctrh = (*pGPIOctrh & ~(0x0F << 20)) | (0x01 << 20);

This one line sets Pin 13 as GPIO output. First I clear both fields at the same time, then write the new value for the MODE, while CNF stays zeroed out (which is our intention).


Your code should look closer to this then:

uint32_t *pRCCAPB2EnReg = (uint32_t *)RCC_APB2_ENR_ADDR;
uint32_t *pGPIOCtr = (uint32_t *)GPIO_CTRL_ADDR; //CTR LOW
uint32_t *pGPIOCth = (uint32_t *)GPIO_CTRH_ADDR; //CTR HIGH
uint32_t *pGPIOModeOut = (uint32_t *)GPIO_MODE_OUT_ADDR;
uint32_t *pGPIOBSRR = (uint32_t *)GPIO_BSRR_ADDR;
 
*pRCCAPB2EnReg |= (1<<4);
while(!(*pRCCAPB2EnReg & (1 << 4))); //waste a couple cycles for the clock to settle
 
*pGPIOctrh = (*pGPIOctrh & ~(0x0F << 20)) | (0x01 << 20); //set MODE and CNF
 
*pGPIOBSRR |= (1<<13);
while(1); //this at the end

Also, main() should never return. Otherwise the CPU crashes, and everything is reset to reset state (makes GPIO inputs). Hence while(1) at the end.

Additionally, you don't have to allocate pointers at all, you can use macros that you cast to pointers and dereference all on one line, if you wish so.

EDIT: all registers need to be used as volatiles. So it's (volatile uint32_t*) - pointers to volatile integers, because the contents of the registers may change outside the main CPU thread (by interrupts or by MCU itself during its own stuff), for example registers that contain flags have them set by MCU. You want the compiler to force the thread to retrieve the freshest value from the register whenever you request it, and not use the value that got stored in the internal CPU registers, because it could be outdated. More on volatiles: Why is a point-to-volatile pointer, like "volatile int * p", useful? StackOverflow

answered Sep 20, 2023 at 14:56
Sign up to request clarification or add additional context in comments.

2 Comments

hey, why do you need to reset both bits first? (*pGPIOctrh & ~(0x0F << 20) why not setting new values straight?
@aleXela using logical Bit OR 1 you can only convert bit 0 into bit 1. You need logical Bit AND 0 to flip bit from 1 to 0. So if some bit was previously 1, and you need it to be 0, you need to do this value & ~bitmask. It resets all bits to zero (without writing them into the register, only as a temp computation value), and then OR sets only the bits that need to be 1. For example, if previously the bit field was 0b1100, and I want it to be 0b0001, then using OR I will get 0b1101.
0
  1. I am beginner and that was happy time for me.
  2. This is my code and it works fine.

#include<stdint.h>
#define RCC_BASE_ADDR 0x40021000UL //RCC BASE ADDRESS
#define RCC_APB2_ENR_OFFSET 0x18UL 
#define RCC_APB2_ENR_ADDR (RCC_BASE_ADDR + RCC_APB2_ENR_OFFSET)
#define GPIOC_BASE_ADDR 0x40011000UL //0x40011000
#define GPIO_CTRH_OFFSET 0x04UL //GPIO controll register
#define GPIO_CTRH_ADDR (GPIOC_BASE_ADDR + GPIO_CTRH_OFFSET)
#define GPIO_MODE_OUT_OFFSET 0x0CUL //GPIO OUTPUT MODE
#define GPIO_MODE_OUT_ADDR (GPIOC_BASE_ADDR + GPIO_MODE_OUT_OFFSET)
void delay()
{
 for (int i = 0; i < 200000; i++);
}
int main()
{
 volatile uint32_t *pRCCAPB2EnReg = (uint32_t *)RCC_APB2_ENR_ADDR;
 volatile uint32_t *pGPIOCtrH = (uint32_t *)GPIO_CTRH_ADDR;
 volatile uint32_t *pGPIOModeOut = (uint32_t *)GPIO_MODE_OUT_ADDR; 
 
 *pRCCAPB2EnReg |= (1<<4); //enable apb2 for GPIOC
 
 *pGPIOCtrH |= (0b0011<<20); //cfnBit 00 modeBit 11 50mhz
 
 *pGPIOModeOut |= (1<<13); 
 
 while(1)
 {
 *pGPIOModeOut = (1<<13); //bit set register High
 delay();
 *pGPIOModeOut = (0<<13); //bit reset register HIGH
 
 delay();
 }
}
answered Sep 21, 2023 at 9:25

7 Comments

1) remove *pGPIOModeOut |= (1<<13); entirely. This sets mode of Pin 3, not pin 13. It changed the wrong register. You treat CNF and MODE as if they belong to different registers, but they're both one and the same register (two registers with mixed functions). You don't need that at all. *pGPIOCtrH |= (0b0001<<20); //cfn 00 mode 01 this sets the mode.
2) You don't clear CNF bit. *pGPIOCtrH |= (0b0001<<20); //cfn 00 mode 01 will actually end up producing 0b0101 (CNF = 01), not 0b0001, because bit 0b0100 is set upon boot. This will configure the pin as general purpose output open drain, not push-pull. 3) add something after you activate the clock. Yes, most of the time it works without any extras, but it couldn't hurt to have something in there. One day it may cause a problem, and you'll go crazy looking for it.
Depending on your hardware configuration, you could be lucky that this (incorrect code - according to stated goals) works for you. Your LED could be electrically connected in such a way, that your incorrect open drain configuration works identically to push-pull in this specific case (when LED is connected to the power line on one end and GPIO on the other, it can work with both open drain and push-pull).
3) you WILL run into weird problems and bugs because you don't use volatile. This works here because you don't have interrupts or peripherals with flags. As soon as you use UART - even without interrupts - not using volatiles can ruin your day, because some flag in the register will be set, but the code will actually use old value of the flag register and will never read the updated flags register.
STM32F1 is cortex m3 processor and this one is using different kind of configuration register. As I saw Cortex m-4 processor use separate registers to control GPIO pins, Mode register, Speed register, GPIO port pull-up/pull-down register, but in cortex m-3 stm32f103 CPU there is only configuration register instead of, speed mode and pull U/D. So first I need to enable clock for GPIO port, then choose GPIO config as output and even there I need to choose speed and mode, into the same register. *pGPIOCtrH |= (0b0001<<20); this is configuration of GPIO mode, speed and pull U/D, mode. @Ilya
|

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.