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;
}
-
Also check there is no clock for the modules you need.artless-noise-bye-due2AI– artless-noise-bye-due2AI2023年09月20日 13:43:15 +00:00Commented Sep 20, 2023 at 13:43
2 Answers 2
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.
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
2 Comments
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.- I am beginner and that was happy time for me.
- 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();
}
}
7 Comments
*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.*pGPIOCtrH |= (0b0001<<20); this is configuration of GPIO mode, speed and pull U/D, mode. @Ilya