STM32 UART2 BRR Not Giving Expected Results - c

I am currently working with an STM32F407VG on the Discovery board. I am going through the peripherals and trying to get each one working by manipulating the registers only (no HAL).
When I go to initialize UART2 it is transmitting the character I write to the DR but it is doing so ~12x faster than expected, I'm shooting for 9600 baud. I measured this using an oscilloscope (~8-9 us per bit) and playing with the baud rate in Putty (111,111 baud to show the actual character).
I am maxing out the clock speed of the chip 168 MHz SYSCLK, APB1 Prescaler "/4", APB2 Prescaler "/2". I am pretty sure my clocks are at what they are supposed to be, to verify I set up TIMER12, which shares the APB1 clock and set the prescaler to 8400 and had an interrupt generated every time there was a compare match (CCR = 5000) and overflow. I measured this with the oscilloscope and I am getting a 1 Hz square wave as expected which means that the APB1 for Timer 12 is at 84 MHz.
Here is my clock init code:
void SysClockConfig ( void ){
//setting up the MCO output to see the clock signal
//RCC->CFGR |= ( 6 << 24 ) | (3 << 21 ) | ( 4 << 27 );
//1. ENABLE HSE and wait for the HSE to become Ready
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));
// 2. Set the POWER ENABLE CLOCK and VOLTAGE REGULATOR
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
// 3. Configure the FLASH PREFETCH and the LATENCY Related Settings
FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_5WS;
//4. Configure the PRESCALARS HCLK, PCLK1, PCLK2
// AHB PR
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
// APB1 PR
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
// APB2 PR
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
//5. Configure the MAIN PLL
RCC->PLLCFGR = (PLL_M << RCC_PLLCFGR_PLLM_Pos) | (PLL_N << RCC_PLLCFGR_PLLN_Pos) | (PLL_Q << RCC_PLLCFGR_PLLQ_Pos) | (RCC_PLLCFGR_PLLSRC_HSE);
//6. Enable the PLL and wait for it to become ready
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
//7. Set source
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
This is my UART2 config code:
void UART2_Config ( void )
{
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; //clock UART
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //clock GPIOA
GPIOA->MODER |= ( 2 << GPIO_MODER_MODER2_Pos ) | ( 2 << GPIO_MODER_MODER3_Pos ); //set PA2 and PA3 alternate function
GPIOA->OSPEEDR |= ( 3 << GPIO_OSPEEDR_OSPEED2_Pos ) | ( 3 << GPIO_OSPEEDR_OSPEED3_Pos ); //clock GPIO pin at fastest speed
GPIOA->AFR[0] |= ( 7 << GPIO_AFRL_AFSEL2_Pos ) | ( 7 << GPIO_AFRL_AFSEL3_Pos ); //set PA2 and PA3 to alt func UART2
USART2->CR1 = 0;
USART2->CR1 |= USART_CR1_UE; //UART Enable
//USART2->BRR = (0x16 << USART_BRR_DIV_Mantissa_Pos) | (0xc << USART_BRR_DIV_Fraction_Pos); //Set Baud rate
USART2->BRR = 4300;
//USART2->CR1 |= USART_CR1_RE; //Receiver enable
USART2->CR1 |= USART_CR1_TE; //Transmitter enable
//Baud rate is off by a factor of 12ish
}
Finally, my main and while loop with the function for sending a character:
void UART2_SendChar ( uint8_t c )
{
USART2->DR = c;
while ( !(USART_SR_TC));
}
int main ( void )
{
SysClockConfig();
GPIO_Config();
TIM10_Config();
TIM12_Config();
UART2_Config();
//NVIC_SetPriority (TIM1_UP_TIM10_IRQn, 1);
//NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
NVIC_SetPriority (TIM8_BRK_TIM12_IRQn, 1);
NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);
while ( 1 )
{
//GPIOD->BSRR |= (1<<12);
//delay(2000000);
//GPIOD->BSRR |= (1<<28);
delay(4000000);
UART2_SendChar('a');
}
}
I can't find anything in the reference manual to explain this behavior. That tells me I am doing something wrong but I can't seem to track it down. On a final note, I had Putty set up to receive 9600 baud and played around with the BRR and setting it to a value of 4300 output the desired character. Plugging that value into the equation for baud in the reference manual gave me an insane system clock 660 MHz, again telling me I am missing something probably pretty obvious.

I've answered a similar question here recently, so let me copy that one:
BRR is a Q12.4 format fixed point number, which needs one additional operation when OVER8 mode is selected.
OP's question is about STM32F4, but if one inspects STM32F030 reference manual, he/she can find the same logic but represented by a different formula. IMO, that one (F030) is more clear and easier to understand.
To convert uint16_t (Q16.0) to Q12.4, you need to multiply it by 16.
Basically, for OVER16 case, BRR is simply becomes PeripheralClock / BaudRate.
For OVER8 case, you first need to calculate a temporary variable as temp = 2 * PeripheralClock / BaudRate. When writing it into BRR, you need to right-shift its lower 4 bits once.
See the example code:
void setBaudRate(uint32_t baud, bool over8)
{
const uint32_t pClock = 42000000ull; // Hard-code or call a function to obtain it
uint32_t usartDiv = (over8) ? (2 * pClock / baud) : (pClock / baud);
uint32_t reg = USART1->BRR;
reg &= ~0xffff; // Clear the lower 16 bits
if (over8) {
reg |= (usartDiv & 0xfff0) | ((usartDiv & 0xf) >> 1);
}
else { // over16
reg |= usartDiv;
}
USART1->BRR = reg;
}
So, for your case, the correct value of BRR is 4375.

Related

Unable to read multiple channels from single ADC with DMA [bare metal]

I'm trying to read two ADC channels sequentially from my STM32F407ZGT6 using DMA. I'm just trying to get the values from two potentiometers independently on each channel. Although the program doesn't crash, I does not update my variable's value (sensor_val).
I'm using DMA2_Stream0 Channel 0, since I'm using ADC1. For my ADC1, I'm using PB1 (channel 9) and PA1 (channel 1). I tried to follow this tutorial, except that I do not want to trigger my ADC from a timmer just yet, and I've been also checking the example on this question. My ADC callback also never gets called. As far as my understanding goes, the sequence of calls should be:
ADC1->SR EOC --> ADC->CR1 EOCIE --> DMA2_Stream0_IRQHandler() --> dma_ADC_callback()┐
⮤─────────────────────────────────────────────────────────┘
Maybe I do need to include a periodic call to read the ADC?
All the ADC/DMA functions are on their separate .c/.h files. I've already tried to declare sensor_val as a global variable or as an extern variable from the adc.c file, and both give the same result. Here is an approximate mwe of my code:
#include <stdint.h> //uint32_t
#include <stdio.h> //printf
#include "stm32f407xx.h"
#define set(val, pos) ((val) << (pos))
#define msk(size, pos) (((1UL << (size)) - 1UL) << (pos))
static uint32_t sensor_val[2];
static void dma_ADC_callback(void);
void gpio_init(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Port A
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; //Port B
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Port C
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; //Port D
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; //Port F
RCC->AHB1ENR;
//PORT B
GPIOA->MODER |= set(m,(2)); //ADC || PA1 || ADC123_IN1
GPIOB->MODER |= set(m,(2)); //ADC || PB1 || ADC12_IN9
}
void adc_init(void)
{
/*Enable clock access to ADC*/
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
// RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;
/* Config ADC parameters*/
/* Regular Sequence Register 3
* Since the sequence starts from
* the back, we need to set channels
* from SQ3[0] to SQ1[19]
*/
ADC1->SQR3 |= set(0b1001,0); //sets channel PB1 (ADC12_IN9) as 1st conversion
ADC1->SQR3 |= set(0b0001,5); //sets channel PA1 (ADC123_IN1) as 2nd conversion
ADC1->SQR1 |= set(0b0001,ADC_SQR1_L_Pos); //tells the channel sequence lenght = 2
/*If using more than one channel
* SCAN is required
*/
ADC1->CR1 |= set(1,ADC_CR1_SCAN_Pos);
/*Adjust ADC sample time
* The resulting frequency is
* APB2/#cycles:
* 000: 3 cycles
* 001: 15 cycles
* 010: 28 cycles
* 011: 56 cycles
* 100: 84 cycles
* 101: 112 cycles
* 110: 144 cycles
* 111: 480 cycles = 42MHz/480 = 87.5kHz
* */
ADC1->SMPR2 |= set(0b111,ADC_SMPR2_SMP0_Pos); //channel 0
ADC1->SMPR2 |= set(0b111,ADC_SMPR2_SMP9_Pos); //channel 9
/*Turn Interruption On*/
ADC1->CR1 |= set(1,ADC_CR1_EOCIE_Pos);
/*Enable ADC*/
ADC1->CR2 |= set(1,ADC_CR2_ADON_Pos);
}
void adc_start_conversion(void)
{
ADC1->CR2 |= set(1,ADC_CR2_EOCS_Pos); //enables multi-channel conversion
ADC1->CR2 |= set(1,ADC_CR2_CONT_Pos); //enables continuous conversion
ADC1->CR2 |= set(1,ADC_CR2_SWSTART_Pos); //starts conversion
}
/*ADC1 DMA2 => DMA2_Ch0_Stream0 and 4*/
/*ADC2 DMA2 => DMA2_Ch1_Stream2 and 3*/
/*ADC3 DMA2 => DMA2_Ch2_Stream0 and 1*/
void dma2_stream0_init(uint32_t memo, uint32_t periph, uint32_t len)
{
/*Enable clock acces to DMA*/
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
/*Diable DMA2 Stream 0*/
DMA2_Stream0->CR &= ~DMA_SxCR_EN;
/*Clear all interrupt flags of Stream 0*/
DMA1->LIFCR |= DMA_LIFCR_CFEIF0;
DMA1->LIFCR |= DMA_LIFCR_CDMEIF0;
DMA1->LIFCR |= DMA_LIFCR_CTEIF0;
DMA1->LIFCR |= DMA_LIFCR_CHTIF0;
/*Set the source buffer*/ //Memory Address
DMA2_Stream0->M0AR = memo;
/*Set destination buffer*/ //Peripherial Address
DMA2_Stream0->PAR = periph;
/*Set the length*/
DMA2_Stream0->NDTR = len;
/*Set Control options
* Select Stream0_CH0 |
* Prioritu Lvl = High |
* Memory Increment On |
* Circular mode on |
* Direction Per->Mem (0b00)|
* Enable Transfer Complete interrupt
*/
DMA2_Stream0->CR &= ~(DMA_SxCR_CHSEL |
DMA_SxCR_PL |
DMA_SxCR_MSIZE |
DMA_SxCR_PSIZE |
DMA_SxCR_PINC);
DMA2_Stream0->CR |= (set(0,DMA_SxCR_CHSEL_Pos) |
set(2,DMA_SxCR_PL_Pos) |
set(1,DMA_SxCR_MINC_Pos) |
set(1,DMA_SxCR_CIRC_Pos) |
set(0,DMA_SxCR_DIR_Pos) |
set(1,DMA_SxCR_TCIE_Pos)
);
/*Enable direct mode and disable FIFO*/
DMA2_Stream0->FCR = 0;//set(0,DMA_SxFCR_FEIE_Pos);
/*!! Enable DMA1 Stream 6*/
DMA2_Stream0->CR |= set(1,DMA_SxCR_EN_Pos);
/*Enable ADC transmitter DMA*/
ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos);
/*DMA Interrupt enable in NVIC*/
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
void DMA2_Stream0_IRQHandler(void)
{
/*Check for transfer complete interrupt*/
if(DMA2->LISR & msk(1,DMA_LISR_TCIF0_Pos))
{
//Clear flag
DMA2->LIFCR |= msk(1,DMA_LIFCR_CTCIF0_Pos);
//Callback
dma_ADC_callback();
}
}
static void dma_ADC_callback(void)
{
printf("[ Readings Pots.: | %li | %li ]\r\n", sensor_val[0], sensor_val[1]);
}
int main(void)
{
/*Setup*/
//clock_init_168(); //SYSCLK = 168MHz, AHB = 84MHz, APB1 = 42MHz, APB2 = 84MHz
//init_systick_MS(SYSTICK_LOAD_VAL_MS);
gpio_init();
dma2_stream0_init((uint32_t)&sensor_val, (uint32_t)&ADC1->DR, 2);
adc_init();
adc_start_conversion(); //continuous conversion
char count = 0;
for(;;)
{
debug_msg("count : %d", __PRETTY_FUNCTION__, count);
delayMS(500);
count++;
}
}
I finally found a way to fix it!
First of all, the ADC has to be enabled before the DMA. With the code above, ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos) wouldn't get set because I was trying to set the DMA before the ADC. Hence, my interruption never got called. The correct order of function calls is:
adc_init();
dma2_stream0_init((uint32_t)&sensor_val, (uint32_t)&ADC1->DR, 2);
adc_start_conversion();
However, after ADC_CR2_DMA was getting set, my callback got called, but only once. So I had to disable DMA selection with ACD1->CR2 = ADC_CR2_DDS, so DMA would issue conversion requests recurrently:
/*Enable ADC transmitter DMA*/
ADC1->CR2 |= (set(1,ADC_CR2_DMA_Pos) | //<<make sure ADC is already enabled
set(1,ADC_CR2_DDS_Pos)); //<<without DDS, the DMA does a single conversion
Once I did that, my callback got called and I was reading data, but only sensor_val[0] was being updated. That was happening because I had PSIZE == 00, which is the value used to set MSIZE in direct mode. Since I'm using DMA for ADC (maximum 12-bit resolution), I changed sensor_val to uint16_t and PSIZE = 0b01:
DMA2_Stream0->CR |= (set(0,DMA_SxCR_CHSEL_Pos) |
set(2,DMA_SxCR_PL_Pos) |
set(1,DMA_SxCR_PSIZE_Pos) | //<<sets MSIZE=PSIZE in direct mode
set(1,DMA_SxCR_MINC_Pos) |
set(1,DMA_SxCR_CIRC_Pos) |
set(0,DMA_SxCR_DIR_Pos) |
set(1,DMA_SxCR_TCIE_Pos));
And voilá!
NOTES
PINC should be kept at reset value for this configuration, otherwise ADC will expect data with more than 16-bits.
If you need slower rates of acquisition, instead of setting DDS, a DMA request function can be created where the DMA bit is reset and set again:
void adc_new_dma_conversion(void)
{
/*DMA has to be reset first
* then re-enabled to generate
* a new DMA request
*/
ADC1->CR2 &= ~set(1,ADC_CR2_DMA_Pos);
ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos);
}

Stm32 Time Base Interrupt (without any library)

I use Stm32f103c8t6 processor and I want to make 1 second counter. Normaly I dont use tımer update ınterrupt and my counter counts correctly but when I add timer update ınterrupt in code my counter doesnt count correctly. So fast increment. And ı cant remove update interrupt flag (UIF) in TIM1's SR register. If I remove this flag my code is entering infinty loop. I cant any solution for this problem. Thanks for help
This part is bring in startup_stm32f10x_md.s
/**
* #brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
*
* #param None
* #retval : None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop -----------------------------> my code is stuck here
.size Default_Handler, .-Default_Handler
And this is a my code.
#include <stddef.h>
#include "stm32f10x.h"
void CLK_Config(){
RCC-> CR = 0x00000000; //CR Reset
RCC-> CR |= 0x00010000; //HSE enable
while(!(RCC-> CR & 0x00020000)); //HSE FLAG control
RCC-> CR |= 0x00080000; //CSS enable
RCC-> CR |= 0x01000000; //PLL On
RCC-> CFGR |= 0x00010000; //HSE Select PLL input
RCC-> CFGR |= 0x001C0000; //PLL Multi With 9 = 72 Mhz
RCC-> CFGR |= 0x00000002; //PLL Select as SYSCLK
RCC-> CFGR |= 0x00000400; //APB1 Clock divided by 2
RCC-> APB1ENR |= 0x18000000; //APB1 BKP Clock Enable
PWR-> CR |= 0x0100; //PWR BKP Access Enable
RCC-> APB2ENR |= 0x00000001; //APB2 AFIO Clock Enable
}
void TIM1_Config(){
RCC-> APB2ENR |= 0x00000800; //TIM1 CLK Enable
TIM1-> CR1 |= 0x0085; //Update Request Source, Counter Enable
TIM1-> DIER = 0x0001; //Update Interrupt Enable
TIM1-> ARR = 0x1F40; //8000 is set as Auto Reload Value
TIM1-> PSC = 0x2327; //9000 is set as Prescaler Value for 1 sn Formula: 1sn=1Hz=(PCLK/(PSC*ARR))
}
void USART1_Config(){
RCC-> APB2ENR |= 0x00000004; //GPIOA CLK Enable
GPIOA-> CRH |= 0x00000AA0; //GPIOA 10,9 Push-Pull Alternate Function 2Mhz
RCC-> APB2ENR |= 0x00004000; //USART1 CLK Enable
USART1-> BRR |= 0x00001D4C; //USART1 Baund Rate 9600
USART1-> CR1 |= 0x000020C8; //USART, TXE Interrupt, TC Interrupt, Transmitter Enable
}
void Interrupt_Config(){
NVIC-> ISER[0] |= 0x02000000; //NVIC TIM1 UP
NVIC-> ISER[1] |= 0x00000020; //NVIC USART1 Global Interrupt
NVIC-> IP[25] = 0x10; //TIM1 UP Interrupt Priority 2. 25th Interrupt
NVIC-> IP[37] = 0x40; //USART1 Global Interrupt Priority 5. 37th Interrupt
}
uint8_t count1sec; // Global variable
int main(void)
{
CLK_Config();
Interrupt_Config();
TIM1_Config();
USART1_Config();
while (1)
{
if(count1sec != 0){
USART1-> DR = count1sec;
}
}
}
void TIM1_UP_IRQHandler(){
TIM1-> SR = 0x00000000;
NVIC-> ICPR[0] = 0x02000000;
count1sec += 1;
}
I'm not able to detect the exact cause of your problem, but I can provide some suggestions and make some guesses.
1) Do not use magic numbers! Use predefined bit names. Here is an example:
USART2->BRR = 0x1a0; // 115200 bps # 24 MHz (OVER8=1, ONEBIT=1)
USART2->CR1 |= USART_CR1_OVER8 // Oversampling is reduced for higher baud rates
| USART_CR1_IDLEIE // Idle line detection interrupt is enabled
| USART_CR1_TE // Transmitter is enabled
| USART_CR1_RE; // Receiver is enabled
USART2->CR3 |= USART_CR3_ONEBIT // One bit mode for increased clock deviation tolerance
| USART_CR3_DMAT // DMA for TX
| USART_CR3_DMAR; // DMA for RX
USART2->CR1 |= USART_CR1_UE; // Enable USART2
2) Use CMSIS functions to access NVIC functionality. Here is an example:
NVIC_SetPriority(TIM6_DAC_IRQn, 2);
NVIC_EnableIRQ(TIM6_DAC_IRQn);
3) You have Auto-reload preload enable bit set in TIM1->CR1 register. This normally delays the update of the ARR register until the next update event. I'm not sure how it works during the initial run of the timer, but I suggest to avoid using it until you're sure everything works fine.
4) You normally don't need to clear pending bits in NVIC. I suggest removing that code from the ISR. Clearing the flags in the peripheral registers is all you need and you're already doing it with TIM1->SR = 0 line.
5) The reason you stuck in Infinite_Loop is probably the USART TX interrupt. It gets fired but as you didn't supply a ISR for it, it falls into the Default_Handler. You load DR manually in the main loop, so in this case you don't even need USART TX interrupts.
I have same problem today.
I'm using STM32H743zi.
In my case, after clearing SR, at least 13 'nop' required.
I don't know why.
My final solution is:
void TIM16_IRQHandler(void)
{
TIM16->SR = 0 ;
volatile uint32_t sr = TIM16->SR ;
UNUSED(sr) ;
}
Reading back make it work, but I don't know why.
I try to disable MPU and/or DCACHE, but it have no difference.

Data Not Being Stored in Receive Data Register, (UART RXNE Flag not setting)

I have been programming the stm32l412kb nucleo board, attempting to achieve basic UART communication. Transmission from the board works great but the board is not appearing to receive any data.
For the software side, I have tried using standard HAL code in a few ways different, in both interrupt and non-interrupt mode. I have tied a more basic approach (shown below). From debugging line by line I have found that the Receive Data register (RDR) is not filling (and consequently the flag which sets when there is data there is not setting). This has been the error in each case.
This aim of this code is to send back the character entered.
#include "stm32l4xx.h"
int main(void)
{
/* USER CODE BEGIN 1 */
/*The Usart2 peripheral needs its clock to be enabled.*/
RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
/*The 72 MHz APB1 bus clock with a 9600baud rate gives a baud rate for the register of 0x1D4C*/
USART2->BRR = 0x1D4C;
/*For USART2 we need to enable the overall UART (U) driver, the transmission lines(T) and the reading lines(R). UART Enable is last.*/
USART2->CR1 |= USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;
/*Setting transmission pin*/
GPIOA->MODER |= GPIO_MODE_AF_PP;
GPIOA->OSPEEDR |= GPIO_SPEED_FREQ_HIGH;
/* USER CODE END 1 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (USART2->ISR & USART_ISR_RXNE) //if RX is not empty
{
char temp = USART2->RDR; //fetch the data received
USART2->TDR = temp; //send it back out
while (!(USART2->ISR & USART_ISR_TC)); //wait for TX to be complete
}
}
return 0;
}
To send the data I have used RealTerm Serial Capture and have tried also the stm32cubeIDE console. One possibility of the source of the problem is that the DataSheet says
"In the USART, the start bit is detected when a specific sequence of samples is recognized. This sequence is: 1 1 1 0 X 0 X 0 X 0 0 0 0."
I have not coded any way of leading my data with this, however, from all the examples I have seen from a couple of books as well as videos, they did not need to think about this and it worked perfectly. Could it be a hardware problem? Is there something I'm not initialising? I have even tried different cables.
Many thanks in advance for any help,
Harry
/*********************************UPDATE**************************************/
First and foremost, thank you very much for the help, I now understand basics such as how to use the datasheet to configure the registers. It is much appreciated. I have updated my code but still the problem remains.
So I have updated my configuration as so:
/*Configuring GPIO Pins*/
/*Clearing whatever is held in the mode registers for pins 2 and 3 (Inverting with their masks.)*/
GPIOA -> MODER &= ~(GPIO_MODER_MODE2_Msk | GPIO_MODER_MODE3_Msk);
/*The 2 bits 10 are being shifted to the position which configures Mode of pin 2 and also for pin 3 in the mode register.
*(10 is alterntive function mode).*/
GPIOA -> MODER |= (0b10 << GPIO_MODER_MODE2_Pos) | (0b10 << GPIO_MODER_MODE3_Pos);
/*Clearing whatever is held in the output speed registers for pins 2 and 3*/
GPIOA -> OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED2_Msk | GPIO_OSPEEDR_OSPEED3_Msk);
/*Setting the speed of pins 2 and 3 to be very high(11)*/
GPIOA -> OSPEEDR |= (0b11 << GPIO_OSPEEDR_OSPEED2_Pos) | (0b11 << GPIO_OSPEEDR_OSPEED3_Pos);
/*Clearing whatever is held in the alternative function registers for pins 2 and 3.*/
GPIOA -> AFR[0] &= ~(GPIO_AFRL_AFSEL2_Msk | GPIO_AFRL_AFSEL3_Msk);
/*Setting the pins 2 and 3 to their alternative functions(TX and RX)*/
GPIOA -> AFR[0] |= (7 << GPIO_AFRL_AFSEL2_Pos) | (7 << GPIO_AFRL_AFSEL3_Pos);
/*Clock Configuration*/
/*Enabling the USART2 peripheral clock.*/
RCC->APB1ENR1 &= ~(RCC_APB1ENR1_USART2EN_Msk);
RCC->APB1ENR1 |= (0b1 << RCC_APB1ENR1_USART2EN_Pos);
/*Enabling the GPIOA port peripheral clock*/
RCC->AHB2ENR &= ~(RCC_AHB2ENR_GPIOAEN_Msk);
RCC->AHB2ENR |= (0b1 << RCC_AHB2ENR_GPIOAEN_Pos);
/*USART Configuartion*/
/*The 72 MHz APB1 bus clock with a 9600baud rate gives a baud rate for the register of 0x1D4C*/
USART2->BRR = 0x1D4C;
/*For USART2 we need to enable the overall UART (U) driver, the transmission lines(T) and the reading lines(R). UART Enable is last.*/
USART2->CR1 &= ~(USART_CR1_RE_Msk | USART_CR1_TE_Msk | USART_CR1_UE_Msk);
USART2->CR1 |= USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;
Which has greatly developed my understanding of how to properly configure the device. However, I'm still having a problem with the overall aim of the code to bounce back a character, as the data is still not being read by the MCU. I will pursue on and update if it's successful. I'm thankful for any further suggestions.
this does not initialize the GPIO MODER or OPEEDR regiters.
GPIOA->MODER |= GPIO_MODE_AF_PP;
GPIOA->OSPEEDR |= GPIO_SPEED_FREQ_HIGH;
GPIO_MODE_AF_PP & GPIO_SPEED_FREQ_HIGH are HAL definitions and cant be used on the register level.
You need to set the appropriate values for every pin you use:
It will never receive or send anything as you forgot to set the GPIO -> AF registers and the hardware is not connected to the pins internally.
You can find the alternate functions mappin in the Datasheet
and the AF GPIO registers in the Reference Manual
this sequence should be:
GPIOA -> MODER &= ~(GPIO_MODER_MODE2_Msk | GPIO_MODER_MODE3_Msk);
GPIOA -> MODER |= (0b10 << GPIO_MODER_MODE2_Pos) | (0b10 << GPIO_MODER_MODE3_Pos);
GPIOA -> OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED2_Msk | GPIO_OSPEEDR_OSPEED23Msk);
GPIOA -> OSPEEDR |= (0b11 << GPIO_OSPEEDR_OSPEED2_Pos) | (0b11 << GPIO_OSPEEDR_OSPEED3_Pos);
GPIOA -> AFR[0] &= ~(GPIO_AFRL_AFSEL2_Msk | GPIO_AFRL_AFSEL3_Msk);
GPIOA -> AFR[0] |= (7 << GPIO_AFRL_AFSEL2_Pos) | (7 << GPIO_AFRL_AFSEL3_Pos);

Getting DMA USART to work on STM32L053R8T6

I am having an issue in getting my computer (virtual COM port, to be exact) to communicate with my STM32L053R8T6 (Nucleo) board by DMA and USART. Here is my code for the DMA and USART part:
#include "Device/Include/stm32l0xx.h" // Device header
#include "JB.h"
#include <string.h>
#define PCLK 32000000
#define BAUD 19200
uint8_t stringtosend[] = "test\n";
uint8_t stringtoreceive[] = " ";
void ENABLE_UART_DMA(void){
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //enable periph.clk for DMA1
/**Enabling DMA for transmission
* DMA1, Channel 4 mapped for USART2TX
* USART2 TDR for peripheral address
* stringtosend for data address
* Memory increment, memory to peripheral | 8-bit transfer | transfer complete interrupt**/
DMA1_CSELR->CSELR = (DMA1_CSELR->CSELR & ~DMA_CSELR_C4S) | (4 << (3 * 4));
DMA1_Channel4->CPAR = (uint32_t)&(USART2->TDR);
DMA1_Channel4->CMAR = (uint32_t)stringtosend;
DMA1_Channel4->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE;
/**Enabling DMA for reception
* DMA1, Channel 5 mapped for USART2RX
* USART2 RDR for peripheral address
* stringtoreceive for data address
* Data size given
* Memory increment, peripheral to memory | 8-bit transfer | transfer complete interrupt**/
DMA1_CSELR->CSELR = (DMA1_CSELR->CSELR & ~DMA_CSELR_C5S) | (4 << (4 * 4));
DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);
DMA1_Channel5->CMAR = (uint32_t)stringtoreceive;
DMA1_Channel5->CNDTR = sizeof(stringtoreceive);
DMA1_Channel5->CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_EN;
NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0); //NVIC enabled, max priority, channels 4-7
NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
}
void CONFIGURE_UART_PARAM(void){
RCC->IOPENR |= ( 1ul << 0); //Enable GPIOA clock
RCC->APB1ENR |= ( 1ul << 17); //Enable USART#2 clock
GPIOA->AFR[0] &= ~((15ul << 4* 3) | (15ul << 4* 2) ); //Clear PA2,PA3
GPIOA->AFR[0] |= (( 4ul << 4* 3) | ( 4ul << 4* 2) ); //Set PA2,PA3
GPIOA->MODER &= ~(( 3ul << 2* 3) | ( 3ul << 2* 2) ); //Same as above
GPIOA->MODER |= (( 2ul << 2* 3) | ( 2ul << 2* 2) );
USART2->BRR = PCLK/BAUD;
USART2->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; //Enable DMA mode in transmit and receive
/*UART enabled for transmission and reception*/
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
while((USART2->ISR & USART_ISR_TC) != USART_ISR_TC)
{
/* add time out here for a robust application */
}
USART2->ICR = USART_ICR_TCCF;
}
void CONFIGURE_EXTI(void){
SYSCFG->EXTICR[0] = ((SYSCFG->EXTICR[0] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC); //clear EXTICR and set to PC13(B1)
EXTI->FTSR |= EXTI_FTSR_TR13; //falling edge trigger
EXTI->IMR |= EXTI_IMR_IM13; //unmask
NVIC_SetPriority(EXTI4_15_IRQn, 0); //def interrupt
NVIC_EnableIRQ(EXTI4_15_IRQn);
}
/*************************************************************************************************************************************************************************************************************************/
/*************************************************************************************************************************************************************************************************************************/
/*Interrupt Handlers*/
void DMA1_Channel4_5_6_7IRQHandler(void){
if((DMA1->ISR & DMA_ISR_TCIF4) == DMA_ISR_TCIF4){
DMA1->IFCR = DMA_IFCR_CTCIF4; //Clear Channel 4 Transfer Complete flag
}
else if((DMA1->ISR & DMA_ISR_TCIF5) == DMA_ISR_TCIF5){
DMA1->IFCR = DMA_IFCR_CTCIF5; //Clear Channel 5 Transfer Complete flag
DMA1_Channel5->CCR &= ~DMA_CCR_EN;
DMA1_Channel5->CNDTR = sizeof(stringtoreceive);/* Data size */
DMA1_Channel5->CCR |= DMA_CCR_EN;
}
}
void EXTI4_15_IRQHandler(void){
if(!(GPIOC->IDR & (1 << 13))){
/* Clear EXTI 13 flag */
EXTI->PR = EXTI_PR_PIF13;
/* start 8-bit transmission with DMA */
DMA1_Channel4->CCR &= ~DMA_CCR_EN; //channel disable
DMA1_Channel4->CNDTR = sizeof(stringtosend);/* Data size */
DMA1_Channel4->CCR |= DMA_CCR_EN; //channel enable
}
}
//void EXTI4_15_IRQHandler(void){
// if((EXTI->PR & EXTI_PR_PIF13) == EXTI_PR_PIF13){
// /* Clear EXTI 13 flag */
// EXTI->PR = EXTI_PR_PIF13;
//
// /* start 8-bit transmission with DMA */
// DMA1_Channel4->CCR &= ~DMA_CCR_EN; //channel disable
// DMA1_Channel4->CNDTR = sizeof(stringtosend);/* Data size */
// DMA1_Channel4->CCR |= DMA_CCR_EN; //channel enable
// }
//}
Now then, this specific code is based on an example from the STM32L0 snippets package 1.20, USART/Communcation Using DMA. USART 1 was simply redefined to USART 2 (as that is the one used by the virtual COM port), and the DMA channels were redefined according to that as well. However, the problem here is very simple: it will only print stringtosend once (would like to do it every time button B1 is pressed), and will not receive data by RX either - as if it completely ignores the DMA interrupt handler - which I am not sure how to test (no trace features available on this board). What I have seems to reflect the reference manual well enough, and all the main does is:
int main(){
SystemCoreClockInit();
CONFIGURE_UART_PARAM();
ENABLE_UART_DMA();
pushbutton_def();
CONFIGURE_EXTI();
while(1){
}
...which should just react to the defined interrupts, however it does not, and for the life of me, I cannot see why. I would love if you could help me - I would also like to avoid HAL or LL APIs - this is not a complex enough project to warrant their usage (several inputs, outputs, comms between two boards by USART/DMA), plus I would prefer to learn working closer to the register level.
Thanks!
edit (in response to Berendi's suggestions):
1. GPIOC was defined in another file, called with pushbutton_def():
RCC->IOPENR |= (1UL << 2); //enable GPIOC
I understand exactly what you mean by your explanation (indeed, the register referred by those two is "the same", 0x00000020U), but I am not sure as to how to redefine it: here is my attempt after looking at the reference manual (SYSCFG part) and the source (still, it does not work):
SYSCFG->EXTICR[3] = ((SYSCFG->EXTICR[3] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC);
As suggested, I have added USART2->ICR = USART_ICR_TCCF; to the EXTIhandler, right after the DMA channels. I have kept it in the USART definition. The message is still only being sent once, though.
GPIOC is not enabled
Here,
RCC->IOPENR |= ( 1ul << 0); //Enable GPIOA clock
you should enable GPIOC too.
EXTI13 is mapped to PA13
Here,
SYSCFG->EXTICR[0] = ((SYSCFG->EXTICR[0] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC); //clear EXTICR and set to PC13(B1)
you are setting the configuration register for EXTI0-EXTI3, actually mapping EXTI1 to PC1. EXTI13 remains mapped to PA13, which is actually SWDIO, connected to the onboard debugger. I guess the traffic on SWDIO triggers the EXTI interrupt, the handler checks PC13 which is always reading 0 because the port is disabled, and enables DMA. DMA transmit works only once though, because
USART_ISR_TC is not cleared in the interrupt
but only once at startup. You should move this line
USART2->ICR = USART_ICR_TCCF;
to the EXTI interrupt handler.
I'm not sure why receiving doesn't work, perhaps the DMA handler has no chance to run, because EXTI is constantly retriggered by SWD traffic. Both interrupts have the same priority, the one with lower interrupt number wins, which is the EXTI handler. If it's always retriggered before it finishes, then it will be called again, not letting the other handler to run.

STM32F030 PWM Setup Issues

I'm trying to get PWM functioning on two pins of my STM32030R8T6, it's on a Nucleo development board and I'm using Keil. For learning, I've mostly been following the material on this website, but with adaptations as that site uses a different MCU. There really isn't much to setting up the PWM so I'm not quite sure what I've done wrong, I know the timer is working because the on-board LED blinks 1.5 times per second, but when I monitor the Ch1 and Ch2 output pins with my scope I get nothing. I'm pretty sure the pins are correctly set in Alternate Function Push-Pull because they're set the same as the MCO pin which is functioning and showing 24 MHz (Though my cheap scope has some problems determining that...). I've attached all of my relevant and even remotely possibly relevant code. And for your convenience:
UM0360 Reference Manual (STM32F030...)
I'd post links to the Nucleo User Manual and Device Datasheet as well but I can't post more than two links, since this is my first question and my reputation is less than ten.
Any help on what I might be doing wrong is appreciated, I'm sure it's something stupid.
#include "stm32f0xx.h"
void Initializations(void);
int main(void)
{
Initializations();
while(1)
{
/* Toggle onboard LED whenever timer overflows */
if((TIM3->SR & TIM_SR_UIF))
{
TIM3->SR &= ~TIM_SR_UIF;
GPIOA->ODR ^= GPIO_ODR_5;
}
}
}
void Initializations(void)
{
/* CLK CONFIG */
RCC->CFGR |= RCC_CFGR_HPRE_DIV2 |
RCC_CFGR_PPRE_DIV16 |
RCC_CFGR_MCO_SYSCLK |
RCC_CFGR_PLLMUL6;
/* Activate PLL, wait */
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
RCC->CFGR |= RCC_CFGR_SW_PLL;
/* Enable IO CLKs */
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
/* Enable peripheral CLKs */
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
/* PIN INITIALIZATIONS */
GPIOA->MODER |= GPIO_MODER_MODER5_0 | // Onboard LED (General output)
GPIO_MODER_MODER2_1 | // USART2 TX (Alternate function)
GPIO_MODER_MODER3_1 | // USART2 RX (Alternate function)
GPIO_MODER_MODER6_1 | // TIM3 CH1 (Alternate function)
GPIO_MODER_MODER7_1 | // TIM3 CH2 (Alternate function)
GPIO_MODER_MODER8_1 | // MCO (Alternate function)
GPIO_MODER_MODER9_1 | // USART1 TX (Alternate function)
GPIO_MODER_MODER10_1; // USART1 RX (Alternate function)
/* TIMER INITS */
TIM3->PSC = 7;
TIM3->ARR = 59999;
/* CCM1 */
TIM3->CCMR1 |= TIM_CCMR1_OC1M_0 |
TIM_CCMR1_OC1M_1;
TIM3->CCR1 |= 4499;
TIM3->CCER |= TIM_CCER_CC1E; // Enable Ch1
/* CCM2 */
TIM3->CCMR1 |= TIM_CCMR1_OC2M_0 |
TIM_CCMR1_OC2M_1;
TIM3->CCR2 |= 29999;
TIM3->CCER |= TIM_CCER_CC2E; // Enable Ch2
TIM3->CR1 |= TIM_CR1_CEN; // Enable TIM3
/* USART INITS */
RCC->CFGR3 |= RCC_CFGR3_USART1SW_0; // Clock USART1 from SYSCLK
}
In addition to setting the pin to use an alternate function, you must also set which alternate function to use.
This is described in section 8.3.2 (pdf page 128) of the document you linked.
These are the AFRL (for pins 0-7) and AFRH (for pins 8-15) registers on the port.
For example, based on your code, and if TIM3 uses alternate function 2 and is on pins 6 and 7, (and assuming the alternate code was currently 0) you'd do
GPIOA->AFRL |= (2 << (6 * 4)) | (2 << (7 * 4));
If it isn't 0 or you want to be sure, mask off the bits first (each pin gets 4 bits).
(Note, your header may name registers differently than mine, and your alternate functions may also be different; I usually work with STM32F407 or STM32F334. To find the table of alternate functions to see which one you need, you'll have to look that up in the datasheet for the particular chip you are using, as opposed to the family reference manual which you linked above)
The more general form is
mode << (pin * 4)
for AFRL and
mode << ((pin - 8) * 4)
for AFRH.

Resources