2021-10-06 09:55 AM
Hello everyone!
My setup uses a STM32407VG. I have been using for some time now and have gotten used to how to read the datasheet and use some of features. Today I am trying to get UART4 transmitting some data.
Here is how I went about getting it all setup:
I did step through the debugger (via STLINK) and it appears I am setting all of the correct bits. But when I land on that UE bit, I get the error (in the console) Target is not responding, retrying… until it times out. Below is a code snippet.
/* UART Functions */
void UART4_Config()
{
// Enable GPIO C
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
// Enable the UART (PC10=TX PC11=RX)
RCC->APB1ENR |= DKO_UART4_ENABLE;
// UART4 TX PIN
GPIOC->MODER &= ~(GPIO_MODER_MODE10); // Clear pin mode field
GPIOC->MODER |= GPIO_MODER_MODER10_1; // Set pin mode field as alternate
GPIOC->OTYPER &= ~(GPIO_OTYPER_OT10); // Clear output type field (push-pull by default)
GPIOC->OSPEEDR &= ~(GPIO_OTYPER_OT10); // Clear output speed field (low by default)
GPIOC->OSPEEDR |= GPIO_OSPEEDR_OSPEED10_0; // Set to medium speed
GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPD10); // Clear pullup/ pulldown mode field (none as default)
GPIOC->AFR[1] &= ~(1U << 8); // Set alternate function to be AF8, bit 8
GPIOC->AFR[1] &= ~(1U << 9); // Set alternate function to be AF8, bit 9
GPIOC->AFR[1] &= ~(1U << 10); // Set alternate function to be AF8, bit 10
GPIOC->AFR[1] |= (1U << 11); // Set alternate function to be AF8, bit 11
// UART4 SPECIFIC CONFIG
UART4->BRR = 2188; // Set baudrate to be 19200; formula is BRR = [pclk + (baud/2)] / baud
UART4->CR1 = (1U << 3); // Set the TE bit (transmitter enable bit)
UART4->CR1 |= (1U << 13); // Set the UE bit (uart enable bit)
}
I never used a UART other than on things like BASIC STAMP, PICAXE, and ARDUINO. It is a little more setup with bare metal STM32F407 but it looked pretty doable. Any hints would be greatly appreciated! Thanks for your time!
Solved! Go to Solution.
2021-10-06 10:16 AM
Might not be it, but the RM says to enable UE first, then do everything else.
Code is easier to read if you use standard defines than custom bits
UART4->CR1 |= (1U << 13); --> UART4->CR1 |= USART_CR1_UE;
Custom board? Make sure PC10 isn't connected to anything else that will disrupt the debugger. Set it as GPIO and toggle it.
2021-10-06 10:16 AM
Might not be it, but the RM says to enable UE first, then do everything else.
Code is easier to read if you use standard defines than custom bits
UART4->CR1 |= (1U << 13); --> UART4->CR1 |= USART_CR1_UE;
Custom board? Make sure PC10 isn't connected to anything else that will disrupt the debugger. Set it as GPIO and toggle it.
2021-10-06 12:03 PM
Hey thanks for the reply! I properly corrected the order and also used the standard defines where it was missing.
void UART4_Config()
{
// Enable GPIO C
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
// UART4 TX PIN GPIO SETUP
GPIOC->MODER &= ~(GPIO_MODER_MODE10); // Clear pin mode field
GPIOC->MODER |= GPIO_MODER_MODER10_1; // Set pin mode field as alternate
GPIOC->OTYPER &= ~(GPIO_OTYPER_OT10); // Clear output type field (push-pull by default)
GPIOC->OSPEEDR &= ~(GPIO_OTYPER_OT10); // Clear output speed field (low by default)
GPIOC->OSPEEDR |= GPIO_OSPEEDR_OSPEED10_0; // Set to medium speed
GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPD10); // Clear pullup/ pulldown mode field (none as default)
GPIOC->AFR[1] &= ~(1U << 8); // Set alternate function to be AF8, bit 8
GPIOC->AFR[1] &= ~(1U << 9); // Set alternate function to be AF8, bit 9
GPIOC->AFR[1] &= ~(1U << 10); // Set alternate function to be AF8, bit 10
GPIOC->AFR[1] |= (1U << 11); // Set alternate function to be AF8, bit 11
// STEP 0 - Enable the bus to UART4
RCC->APB1ENR |= DKO_UART4_ENABLE;
// STEP 1 - Enable the UART (PC10=TX PC11=RX) by setting the UE bit (uart enable bit)
UART4->CR1 |= (USART_CR1_UE);
// STEPS 2, 3, and (not 4) - sets the M, SOP, and TE bits
UART4->CR1 |= (USART_CR1_TE);
// STEP 6 - Set baudrate to be 19200; formula is BRR = [pclk + (baud/2)] / baud
UART4->BRR = 2188;
}
Yes its a custom board. I checked to make sure I was able to toggle it when its configured as an output (push-pull). I got alternating signals when toggling it in a while loop. So the pin seems to be okay. I should note there is a 1kohm pullup on it for a different configuration in my project (open drain mode for 1-wire bit banging). It doesn't seem to hurt it when in strictly output mode. Since UART is configured as push-pull as well, I think its okay.
When I run the altered code, I can set the UE bit fine now. The target does not respond when it executes its last line (trying to return from the function). So I tried commenting in and out some code. I found that UART4->BRR was causing problems with the debugger. Maybe changing the baudrate messes up debugging? It seems so. So I let the code run and ran an infinite while loop..... I finally get data! But garbage data. I am using my protocol analyzer within the logic analyzer I am using and it keeps getting "framing error". Hey I am closer :)
2021-10-06 12:10 PM
Hmmmm yeah there must be something weird going on. Okay so call me crazy but I hear clicking in the MCU. You know when like power MOSFETs are shorting and constantly are turning on and off due to some weird error condition? I hear something similar. It stop when comment out the uart config function I made. Going to poke around some more to see what else I find out.
2021-10-06 12:17 PM
You're still not doing the correct order of initialization. TE should be last.
I would test the code on known good hardware such as a nucleo board. Upload your schematic if you want.
2021-10-06 12:41 PM
Sorry about that! I was careless with the pasting of the up to date code. Here it is:
void UART4_Config()
{
// Enable GPIO C
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
// UART4 TX PIN GPIO SETUP
GPIOC->MODER &= ~(GPIO_MODER_MODE10); // Clear pin mode field
GPIOC->MODER |= GPIO_MODER_MODER10_1; // Set pin mode field as alternate
GPIOC->OTYPER &= ~(GPIO_OTYPER_OT10); // Clear output type field (push-pull by default)
GPIOC->OSPEEDR &= ~(GPIO_OTYPER_OT10); // Clear output speed field (low by default)
GPIOC->OSPEEDR |= GPIO_OSPEEDR_OSPEED10_0; // Set to medium speed
GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPD10); // Clear pullup/ pulldown mode field (none as default)
GPIOC->AFR[1] &= ~(1U << 8); // Set alternate function to be AF8, bit 8
GPIOC->AFR[1] &= ~(1U << 9); // Set alternate function to be AF8, bit 9
GPIOC->AFR[1] &= ~(1U << 10); // Set alternate function to be AF8, bit 10
GPIOC->AFR[1] |= (1U << 11); // Set alternate function to be AF8, bit 11
// STEP 0 - Enable the bus to UART4
RCC->APB1ENR |= DKO_UART4_ENABLE;
// STEP 1 - Enable the UART (PC10=TX PC11=RX) by setting the UE bit (uart enable bit)
UART4->CR1 |= (USART_CR1_UE);
// STEP 2 - Set the M bit
UART4->CR1 |= (USART_CR1_M);
// STEP 3 - Set the stop bits
UART4->CR1 |= (USART_CR2_STOP_1);
// STEP 4
// NONE
// STEP 5 - Set baudrate to be 19200; formula is BRR = [pclk + (baud/2)] / baud
UART4->BRR = 4376;//2188;
// STEP 6 - Set the TE bit
UART4->CR1 |= USART_CR1_TE;
}
I think the 1kohm pullups really messed up the UART. Took some hot air and removed them, did the trick. Now I am finally getting some clear data. Thank you for the help! Where can I place some money to buy you a coffee? :p
2021-10-06 01:13 PM
If the purpose of using register level coding is to be efficient, you might consider folding the multiple LOAD/STORE (RMW) operations to achieve that end.
The Compiler can also pre-compute constants, so math doesn't become part of the code, improving clarity, and eases porting/changes.
2021-10-06 01:16 PM
Oh interesting. I will read up on this. The register level coding is honestly more for education. Though I have been using it more and more for my own projects. I dunno I find it easier to understand a bottom up approach when learning. Starting with the HAL, I felt disconnected.
2021-10-06 01:39 PM
Glad you got it solved. However, I can't see why a 1kOhm pullup should be causing any issues here. It's almost as if the power supply is insufficient, which causes a power rail to drop and the debugger to lose contact when it tries to drive PC10 low. Removing the pullup would reduce the load and mitigate the issue. But PC10 can drive 3.3mA no problem under normal scenarios.
The SET_BIT, CLEAR_BIT, and less so the MODIFY_REG macros are useful to avoid typos and improve readability. I find a missing "~" hard to spot as my eyes gloss over simple statements. I may be in the minority here in preferring them.
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))
#define READ_BIT(REG, BIT) ((REG) & (BIT))
#define CLEAR_REG(REG) ((REG) = (0x0))
#define WRITE_REG(REG, VAL) ((REG) = (VAL))
#define READ_REG(REG) ((REG))
#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
> Where can I place some money to buy you a coffee?
No need, but thank you for the offer. Pay it forward.
2021-10-06 07:07 PM
I actually didn't know about these. Are they portable?
Yes I am not sure why the pullups were causing the issue. I have nothing else connected to that tx line. I will look into it more as I play around.
Thanks again for the help! I am already paying it forward by helping someone understand ADC circuits and opamps tomorrow over a Discord call so rest assured the good deed will be paid back :)