cancel
Showing results for 
Search instead for 
Did you mean: 

Register Level programming questions

SHobb.1
Associate II

I am just starting out with STM32 MCUs. Because I like pain, I am trying to do register level programming to make sure I really understand what the code is doing (rather than using automagically generated code). My environment is Visual Studio with VisualGDB, compiling with the ARM toolchain (I think? Not sure how to confirm that).

Current hardware is a NUCLEO-F103RB (hopefully soon switching to a NUCLEO-G071RB).

The purpose of this thread is to ask [potentially dumb] questions that arise. I hope some people with more experience than me can weigh in and point me in the right direction.

First question:

I am trying to write the SystemInit function to configure my clock tree. Because my board has an STM32F103RB, I am referring to RM0008 (rev 21). In particular, Chapter 8 (starting on p. 123). There appear to be two registers documented in RM0008 that are NOT in sthm32f103xb.h from CMSIS: RCC_AHBSTR and RCC_CFGR2. Is that because those two registers aren't present in the STM32F103RB (the RM is shared across several devices)? Looking at the clock tree diagram on p. 20 of the STM32F103xB data sheet, it is slightly different from the clock tree on p. 126 of RM0008 --> in particular, PLL2 and PLL3 are not present. If this is the case, it would make the RMs a lot more readable if there was some flag indicating a particular register is only present in certain devices covered by the RM.

20 REPLIES 20
SHobb.1
Associate II

Still troubleshooting here, I tried to abandon the SWSTART as a trigger, and just used ADON:

void adc_start(void)
 
{
 
 ADC1->SR = 0; //Clear status register
 
// ADC1->CR2 |= ADC_CR2_SWSTART;         //Does not work
 
// ADC1->CR2 |= ADC_CR2_SWSTART | ADC_CR2_ADON; //Does not work
 
 ADC1->CR2 |= ADC_CR2_ADON;           //Works
 
}

I also removed the second write to ADON in the config function. Now getting successful conversions (EOC is being set as expected). So... it now works, but I do NOT know why it wasn't working before. I confirmed in the debugger that the EXTTRIG bits were set to all 1s (which per the RM, should be for SW triggers).

0693W00000NrB7QQAV.jpg 

To recap: the latest code (included here) is successfully triggering conversions. When I try to do it with SWSTART instead, it does not. I cannot explain why.

If any other bit in this register apart from ADON is changed at the same time

> The strange thing is when I try to debug, I never see the SWSTART bit get set in CR2, even with

> breakpoints at line 4 (above), or immediately afterward. Page 241 of RM0008 states, "This bit is set by

> software to start conversion and cleared by hardware as soon as conversion starts". I am not sure if I

> am not setting it properly or if it is getting cleared by hardware before I get to see it.

In either case it's bad documentation. Not surprised.

JW

SHobb.1
Associate II

So... I've made the jump to an STM32G081 (on an eval card). The advice I received was correct -- while similar, there are enough differences to make the software significantly different. I have successfully set up my clock tree and blinked some LEDs (at expected rates - hence why I think the clocks are correct). I believe the UART clock is 64MHz. I am dividing it by 8 (I think) to get 8MHz for my usart_ker_ckpres. I am now [again] trying to get USART tx to work. I am able to successfully transmit one character. However, I am not receiving anything after that. II think it has to be something with how I am checking "tx done" flags, but I can't spot it. Any advice would be much appreciated.

Configuration:

/**
 * @brief UART3 GPIO Configuration (Tx: PC10, Rx: PC11)
 */
void uart_UART3_GPIO_config(void)
{
  //PC10-Tx, PC11-Rx
  //Enable PortC Clock
  RCC->IOPENR |= RCC_IOPENR_GPIOCEN;
  //Mode to AF (UART3)
  GPIOC->MODER &= ~(GPIO_MODER_MODE10 | GPIO_MODER_MODE11);
  GPIOC->MODER |= (GPIO_MODER_MODE11_1 | GPIO_MODER_MODE10_1);
  //Set Output(Tx) to push-pull
  GPIOC->OTYPER &= ~GPIO_OTYPER_OT10;
  //Set output to "high speed"
  GPIOC->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED10;
  GPIOC->OSPEEDR |= GPIO_OSPEEDR_OSPEED10_1;
  //Disable pullups on input
  GPIOC->PUPDR &= ~GPIO_PUPDR_PUPD11;
  //Map alternate functions (see DS12232, p. 50)
  //PC10 --> AF0 = USART3_TX, PC11 --> AF0 = USART3_RX
  GPIOC->AFR[1] &= ~(GPIO_AFRH_AFSEL10 | GPIO_AFRH_AFSEL11);
}
 
/**
 * @brief UART3 Peripheral Configuration
 */
void uart_UART3_config(void)
{
  //115200, 8N1
  //Enable USART3 clock
  RCC->APBENR1 |= RCC_APBENR1_USART3EN;
  //Set DW length, oversample by 16, parity ctrl disabled, parity even
  USART3->CR1 &= ~(USART_CR1_M1 | USART_CR1_M0 | USART_CR1_OVER8 | USART_CR1_PCE | USART_CR1_PS);
  //Enable Transmit
  USART3->CR1 |= USART_CR1_TE;
  //One stop bit
  USART3->CR2 &= ~USART_CR2_STOP;
  //Disable hardware flow control
  USART3->CR3 &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
  //Set Prescaler to 64/8 = 8MHz
  USART3->PRESC &= ~USART_PRESC_PRESCALER;
  USART3->PRESC |= USART_PRESC_PRESCALER_2;
  //Set baud rate to 115200 = Fclk/(16*USARTDIV)
  //    USARTDIV = Fclk/BR = 8MHz/115200 = 69.4444
  //      69 = 0x45
  //      0.44 --> 0.4444*16 = 7.1104 => 7
  //USART3->BRR |= ((0x45UL << 4) | (0x7UL << 0));
//  USART3->BRR = 0x341;  //Baud: 9600
  USART3->BRR = 0x046;  //Baud: 115200
 
  //Clear unnecessary flags/enables
  USART3->CR2 &= ~(USART_CR2_LINEN | USART_CR2_CLKEN);
  USART3->CR3 &= ~(USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN);
 
  //Enable USART
  USART3->CR1 |= USART_CR1_UE;
}

Transmit:

/**
 * @brief UART3 Transmit Function
 */
bool uart_UART3_transmit(uint8_t *data, uint8_t len, uint32_t timeout)
{
  //Wait on TXE to start transmit
  //Write to DR as TXE flag is HIGH (Tx buffer Empty)
  uint8_t dataIdx = 0;
  uint32_t startTick = rcc_msGetTicks();
 
  while(dataIdx < len)
  {
    if(USART3->ISR & USART_ISR_TXE_TXFNF) //Tx buffer empty
    {
      USART3->TDR = data[dataIdx];
      dataIdx++;
    }
    else {    //Manage timeout
      if((rcc_msGetTicks() - startTick) >= timeout)
      {
        gpio_LED_writeRed(LED_ON);
        return(false);
      }
    }
  }
  //Wait for done flag
  while(USART3->ISR & USART_ISR_TCBGT)
  {
    if((rcc_msGetTicks() - startTick) >= timeout)
    {
      gpio_LED_writeOrange(LED_ON);
      return(false);
    }
  }
  return(true);
}

Usage:

int main(void)
{
  uint8_t txChar[] = "JELLO";
 
  cfgPwr();                   //Needs to happen first to allow higher clock speeds
  rcc_HSE_config();
  rcc_SysTick_config(64000);  //Clock is 64MHz, want 1ms tick (1kHz)
  uart_UART3_GPIO_config();
  uart_UART3_config();
  gpio_LED_config();
 
  while(1)
  {
    gpio_LED_toggleGreen();
    rcc_msDelay(250);
//    gpio_LED_toggleOrange();
    rcc_msDelay(250);
//    gpio_LED_toggleRed();
    rcc_msDelay(250);
    gpio_LED_toggleBlue();
    rcc_msDelay(250);
//    printf("Hello world!\r\n");
    uart_UART3_transmit(txChar, 5, 100);
  }
}

Is there something seriously wrong with my uart_UART3_transmit function or am I missing a critical bit in my configuration function?

P.S. I am [somewhat] confident in my BRR setting (I know it's hard coded right now...), because when I change the first letter, I receive the right character. In the "usage" example above, I see J (0x4A). If I change txChar to be "HELLO", I see H (0x48) in my terminal window.

What happens if you kick out the timeout?

What happens if you stop in debugger, observe USART_ISR and manually feed data into USART_TDR?

JW

SHobb.1
Associate II

JW,

I'm not sure what you mean by "kick out the timeout". I inserted some lines to turn on LEDs if various timeout conditions occurred, and they were not being executed. I eventually solved the problem:

USART3 on my chip (STM32G081RBT6) only has "Basic" functionality. Per Table 180 in RM0444 (p. 1001), the "Basic feature set" does not include a prescalar. So my BRR was in fact, set incorrectly. Because I am configured to oversample by 16 and I thought I had set my prescaler to divide my clock by 8 (from 64MHz to 8MHz), it must have somehow worked out that I was getting the first character transmitted (maybe combining iterations? I'm too lazy to do the math to figure out). In any case, by now knowing my actual clock is 64MHz (not 8MHz), I adjusted my BRR:

  //USART3 has basic functionality - no prescaler
  //Set baud rate to 115200 (OVER8 = 0, oversampling by 16)
  //    USARTDIV = Fclk/BR = 64MHz/115200 = 555.56 = 556 = 0x22C
  USART3->BRR = 0x22C;

And now my USART is transmitting normally (as expected) and I see everything in my terminal window.

You mentioned previously it is more elegant to calculate the baud rate in the code rather than hard code it. Could you give an example?

I personally would revert to the non-working code to find a definitive explanation, why one byte got transmitted even with incorrect BRR setting, but YMMV.

​By "kick out" I meant remove.

#define SYSTEM_CLK 64000000

#define USART3_BAUD 115200

USART3->BRR = (SYSTEM_CLK +(USART3_BAUD /2)) / USART3_BAUD;

constants are folded (computed) at compile time, i.e. this computation is "free".

JW​

SHobb.1
Associate II

Why do you add the "+ (USART3_BAUD/2)"?

The formula on p. 1017 of RM0444 just shows:

0693W00000NrRTXQA3.jpgDoes constant folding still work with functions like "round" or "ceil"?

Could I do something like:

USART3->BRR = (UINT32_t)round(SYSTEM_CLK/USART3_BAUD);

Strictly speaking, compilers are not required to perform constant folding in expressions outside certain situations, e.g. initializers, bitfield width declarators etc. But it would be foolish from even the most rudimentary compiler not to do so with expressions which are evaluated to be used in plain assignment to volatile variable, and do conform to requirements for the special usage outlined above (constant expressions, C99 6.6), so it is generally expected to be the case.

However, introducing floats and function calls is outside of strict requirements for constant expressions. Compilers still may evaluate them compile-time, but whether they do, and what exactly is the outcome (we are here talking about the minutiae of floating-point, such as rounding of mantissa) is compiler-dependent.

The "+ (USART3_BAUD/2)" is there to perform the rounding entirely in integer arithmetics.

JW

Hi @Community member​ and thank you for bringing this typo to my attention.

I have passed this internally for correction.

Thanks

Imen

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen