0
votes

I am running standard CMSIS as released by ST with libc included and all the jazz. Here is the code that isn't working.

void __attribute__ ((constructor)) uart4_init() {
  // Enable the clock for uart4
  RCC->APB1ENR |= 1 << 19;
  // Enable the clock for GPIO_C, which needs to be configured to pass-through UART4
  RCC->AHB1ENR |= 1 << 2;

  // Set up the GPIOC 11 and 10 pins for UART use
  // Set to alternate function mode
  GPIOC->MODER &= ~(0b1111 << 20);
  GPIOC->MODER |= 0b1010 << 20;
  // Finally set Alternate Function to UART4, to pass through the UART
  GPIOC->AFR[1] &= ~(0b11111111 << 8);
  GPIOC->AFR[1] |= 0b10001000 << 8;

  // Set up the UART port for reasonable sending settings
  // Here follows explanations on all bits set to 1:
    // Enable the serial port
    // Set to nine bits, so we have one to spare for parity
    // (Leave default wakeup method, since unused?)
    // Enable parity
    // Enable odd parity (better at detecting speed mismatch)
    // (Do not set parity error interrupt on)
    // (Do not set transfer buffer empty interrupt on)
    // (Do not set transmission complete, since that is DMA specific)
    // (Do not set recieve buffer not empty interrupt on)
    // (Do not set idle interrupt since we don't use it)
    // (Do not enable transmitter until setup is fully done)
    // (Do not enable reciever until setup is fully done)
    // (Do not enable mute-mode, we don't use it)
    // (Do not send a break character)
  UART4->CR1 = 0b11011000000000; // Set everything up
  // The confusing part, setting the baud rate, is in separate function
  uart4_set_baud(b9600);
  // Enable sending and receiving
  UART4->CR1 |= 0b1100;
}

I have verified that RCC_APB1ENR bit 19 is set, but the control registers of UART4 don't seem to have any clock and thus don't take the assignments.

(gdb) print/x *0x40023840
$23 = 0x80000

After reading working code (HAL code, so practically obfuscated) and documentation I still cannot figure this out.

Edit: Cleaned up the code following old_timers advice. Should be a usable example now.

Edit2: Removed the interrupts to make more generally usable as base example. Especially since those interrupt bits also net you to enable the UART interrupts through NVIC.

2
you definitely dont want to enable interrupts until you have the basics working. what do you mean by dont seem to have clocks, what does the scope show? have you setup the system clock in the hal/cmsis way so that that high level baud function works? - old_timer
because of all this magic (and risky, its CMSIS) , disassemble the code and confirm the correct registers are being used, correct bits, etc. - old_timer
you dont need to $= and then |= the uart config register, write it then if you want orr in the tx and rx enable bits. - old_timer
what board are you using is pc10 used? no need to mess with pull ups no need to mess with gpio speed, change mode and set alternate functino register, done with gpio. - old_timer
before messing with the uart I always blink an led using a timer to verify the base clock is doing what I think it is doing and there are no hidden divide by twos, etc. then I work up to uart. the few times I dont have a scope I use another mcu board and look for and measure state changes vs a timer while generating a square wave out of the uart (N81 0x55) and see if it is off, measure all the state changes as needed... - old_timer

2 Answers

2
votes
  1. When you use CMSIS use also the the human readable defines.

    RCC->APB1ENR |= 1 << 19; 
    

It is very easy to make a mistake in the number and very quickly you will forget what this line does.

 RCC->APB1ENR |= RCC_APB1ENR_UART4EN;

Shows exacltly what what it does.

You have much better examples here like:

 UART4->CR1 = 0b11011110100000;

or

 UART4->CR1 |= 0b1100;

I think that

 UART4 -> CR1 |= USART_CR1_RE | USART_CR1_TE; 

Is much easier to read and much much more error safe.

  1. After enabling the clock you need to provide the delay or read-back.

I do not use HAL - but I really like low level HAL macros for example

 __HAL_RCC_UART4_CLK_ENABLE(); 

Which correct enable the clock (remember that many STM32 have bugs in the RCC domain and require special procedure which is described in errata pages)

#define __HAL_RCC_UART4_CLK_ENABLE()  do { \
                                      __IO uint32_t tmpreg = 0x00U; \
                                      SET_BIT(RCC->APB1ENR, RCC_APB1ENR_UART4EN);\
                                      /* Delay after an RCC peripheral clock enabling */ \
                                      tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_UART4EN);\
                                      UNUSED(tmpreg); \
                                      } while(0U)

This macro is same effective as simple register assignment + readback but does it correctly and its name describes what it does.

0
votes

After some guided disassembling (Thanks old_timer!) I have now concluded that I am reading from the wrong addresses in my debugging, so the things I thought weren't working were working all along.

Is essence I read from 0x40024c00 when UART4 is at 0x40004c00...

After checking correcting that I find that CR1 does take the value of 0x37a0 that I have written in binary (and becomes 0x37ac when enabled) and that BRR takes the expected value of 1666 (which should be 9600 baud, if I understood the documentation right...).

After that I can check the status register after sending a test byte to the card and conclude that I have received data and validate that to check that my baud is close enough, so it is working now (enough for continued testing).