I've been attempting to set the PLL clock frequency on a STM32H7B3 board, and I was able to do so by setting the DIVN multiplier (RCC_PLL1DIVR_N1) in the code below. The last time I was able to do this successfully was with the multiplier set to 1. When I tried downloading the code with multiplier set to 0x18F (= 399), I got the above error message. I now realize that the max I should have tried was probably DIVN = 280 due to the sys_ck specs, but nonetheless, the error happened upon downloading the code, not running it. I tried using the reset button, but to no avail. I now can't connect to the board and I don't know what else to try.
RCC -> CFGR &= 0; // Reset register
int32_t cfgr = RCC -> CFGR;
int32_t sws_pll1 = RCC_CFGR_SWS_PLL1;
int32_t status = cfgr & sws_pll1;
while (!status) {
cfgr = RCC -> CFGR;
sws_pll1 = RCC_CFGR_SWS_PLL1;
status = cfgr & sws_pll1;
} // System clock switch status: Wait until PLL1 is system clock; TODO: hangs*/
// RCC source control register
RCC->CR |= RCC_CR_HSION; // HSI clock enable
while (!(RCC->CR & RCC_CR_HSIRDY)); // Wait until HSI clock is ready
/* -------- PLL Config -------- */
// RCC PLLs clock source selection register
RCC -> PLLCKSELR |= RCC_PLLCKSELR_PLLSRC_HSI; // Select HSI as PLL clock source (hsi_ck)
// Note: Must have PLL1ON = 0 for modifying prescaler
RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1; // Reset prescaler for PLL1 to disabled
RCC -> PLLCKSELR |= RCC_PLLCKSELR_DIVM1_5; // Set prescaler for PLL1 to divsion by 32
// RCC PLL1 fractional divider register
RCC -> PLL1FRACR = 0; // Set FRACN to 0
// RCC PLLs configuration register
RCC -> PLLCFGR |= RCC_PLLCFGR_PLL1FRACEN; // PLL1 franctional latch enable
RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLL1VCOSEL; // Select PLL1 output frequency range: wide VCO range from 128 to 560 MHz
RCC -> PLLCFGR |= RCC_PLLCFGR_PLL1RGE_3; // Select PLL1 input reference frequency range: between 8 and 16 MHz
// Note: Must have PLL1ON = 0 and PLL1RDY = 0 for enabling divider output
RCC -> PLLCFGR |= RCC_PLLCFGR_DIVP1EN; // PLL1 DIVP divider output enable
RCC -> PLLCFGR |= RCC_PLLCFGR_DIVQ1EN; // PLL1 DIVQ divider output enable
RCC -> PLLCFGR |= RCC_PLLCFGR_DIVR1EN; // PLL1 DIVR divider output enable
// RCC PLL1 dividers configuration register
// Note: Must have PLL1ON = 0 and PLL1RDY = 0 for writing bits
RCC -> PLL1DIVR &= 0; // Reset register
RCC -> PLL1DIVR |= (0x1 << RCC_PLL1DIVR_N1_Pos) & RCC_PLL1DIVR_N1; // DIVN = 0x18F = 399
RCC -> PLL1DIVR |= (0x1 << RCC_PLL1DIVR_P1_Pos) & RCC_PLL1DIVR_P1; // DIVP = 1
RCC -> PLL1DIVR |= (0x1 << RCC_PLL1DIVR_Q1_Pos) & RCC_PLL1DIVR_Q1; // DIVQ = 1
RCC -> PLL1DIVR |= (0x1 << RCC_PLL1DIVR_R1_Pos) & RCC_PLL1DIVR_R1; // DIVR = 1
// RCC source control register
RCC -> CR |= RCC_CR_PLL1ON; // PLL1 enable
//while((RCC -> CR & RCC_CR_PLL1RDY) == 0); // Wait until PLL1 clock is ready; TODO: hangs
Update: I ran JLinkSTM32, but it printed the following:
Connecting to J-Link via USB...O.K.
Using SWD as target interface.
Target interface speed: 1000 kHz.
VTarget = 3.299V
Reset target...O.K.
Reset option bytes to factory settings...
Option bytes reset to factory settings.
Resetting option bytes failed.
Press any key to exit.
Run the command line JLinkSTM32.exe utility (installed with the J-Flash tools), select option 9 (For STM32H7xxxx) and hope (takes a few nervous seconds).
If successful, it may have bulk-erased the flash and will have reset the option bytes for the whole chip. It has got me out of a number of inexplicable lock-out situations not recoverable via the J-Flash GUI tool.
Related
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.
I'm attempting to use the STM32L011K4's DMA controller to communicate with slave devices over I2C. Currently, I have no slave devices and am just trying to get the microcontroller to send the start condition out onto the I2C bus, but that is not happening.
When I run this code in debugging mode through the STM32CubeIDE, I notice that the start bit is set, but it never clears even though the reference manual says it should be cleared by hardware once the start condition occurs (page 656 for I2C_CR2).
Monitoring the SDA and SCL lines on my oscilloscope also show that they are a logical 1. Note: I'm using the NUCLEO-L011K4 on a breadboard, so the IO pins are tied to Vref through 1k resistors. All configuration registers appear to contain the desired value when the code is stuck sending the start condition, so I don't believe they are getting clobbered by a random line of code.
I'm not sure what's preventing the start condition from being sent, so any help would be greatly appreciated.
STM32L011K4 Datasheet:
https://www.st.com/content/ccc/resource/technical/document/datasheet/42/c0/ab/e5/71/7a/47/0b/DM00206508.pdf/files/DM00206508.pdf/jcr:content/translations/en.DM00206508.pdf
STM32L011K4 Reference Manual: https://www.st.com/resource/en/reference_manual/dm00108282-ultralowpower-stm32l0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
Initialization code:
void Init_I2C1_DMA() {
/* Basic I2C Initialization for 100 kHz I2C, 24 MHz SYSCLK, /1 APB1 scaler */
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // Enable peripheral clock for I2C1
I2C1->CR1 &= ~(I2C_CR1_PE); // Disable I2C1
I2C1->CR1 = 0; // Reset CR1
I2C1->TIMINGR = 0; // Reset timer settings
/* APB1 clock (I2C1 clock) is set in RCC_CFGR reg -- keep at divide by 1 -- 24 MHz SYSCLK
* Refer to table 103 for timing value source. t_presc was found to be 250 ns for 100 kHz I2C, so PRESC was set to match that for SYSCLK = 24 MHz
* All subsequent settings are copied from table 103 from STM32L011K4 reference manual
*/
I2C1->TIMINGR |= (0x5 << 28)|(0x4 << 20)|(0x2 << 16)|(0x0F << 8)|(0x13 << 0);
/* Desired settings:
* RXDMAEN enable, ANF enable.
*/
I2C1->CR1 |= (0x8 << I2C_CR1_DNF_Pos)|I2C_CR1_ERRIE;
I2C1->CR2 = 0; // Reset contents (ACKs are enabled by default)
NVIC_EnableIRQ(I2C1_IRQn);
NVIC_SetPriority(I2C1_IRQn, 0);
/* DMA initialization */
/* Since this is peripheral to memory, we use I2C1_RX, which is available on DMA channels 3,7. We used channel 3, but 7 would work the same. */
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable peripheral clock for DMA1
DMA1_Channel3->CCR &= ~(0x00000001); // Disable Channel 3 DMA
// Configure DMA channel mapping
DMA1_CSELR->CSELR &= ~0x00000F00; // Channel 3 re-mapping mask
DMA1_CSELR->CSELR |= 0x00000600; // Channel 3 re-mapped to I2C1_RX
/* Configure NVIC for DMA */
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);
return;
}
void I2C1_DMA_Start_Read(uint8_t SlaveAddress, uint8_t RegisterAddress, int* MemoryBaseAddress, int BufferSize) {
// We need to put the device address out on the serial line before we can hand it over to the DMA
I2C1->CR1 &= ~(I2C_CR1_PE); // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
DMA1_Channel3->CCR &= ~(0x00007FFF); // Channel 3 DMA mask
// Configure DMA Channel 3 for 16 bit memory and peripheral, and other aliased settings (reference manual page 249, 10.4.3)
DMA1_Channel3->CCR |= (0b01 << 10)|(0b01 << 8)|DMA_CCR_MINC|DMA_CCR_TEIE|DMA_CCR_TCIE;
DMA1_Channel3->CPAR = (uint32_t) RegisterAddress;
DMA1_Channel3->CMAR = (uint32_t) MemoryBaseAddress;
DMA1_Channel3->CNDTR = (uint16_t) BufferSize;
I2C1->CR1 &= (~I2C_CR1_TXDMAEN); // Disable TX DMA for I2C1
I2C1->CR1 |= I2C_CR1_RXDMAEN; // Enable RX DMA for I2C1
// I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address for DMA read
while(!(I2C1->ISR & I2C_ISR_TXE));
I2C1->TXDR |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address for DMA read
I2C1->CR2 |= I2C_CR2_RD_WRN;
DMA1_Channel3->CCR |= DMA_CCR_EN; // Activate DMA channel 3
I2C1->CR1 |= I2C_CR1_PE; // Enable I2C1
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
// ???
return;
}
According with reference manual(p. 604)
you need uncomment I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); and comment I2C1->TXDR |= ((uint8_t) (SlaveAddress << 1)); for set slave address, and you need set the number of bytes to be transferred.
I can't see your initialisation of GPIO. Check is GPIO settings right (Alternative function and open-drain mode).
Also in reference manual written this
PE must be kept low during at least 3 APB clock cycles in order to perform the software
reset. This is ensured by writing the following software sequence: - Write PE=0 - Check
PE=0 - Write PE=1.
I think you should try to do so.
Also I advise using 4.7k resistor for pulling to VDD.
At the moment I use TIM2 on the stm32f4 - Discovery Board to count pulses (rising edges). How can I adjust the thresholds for the rising edges ? I want to count 1 V pulses. At the moment it is just possible to count 2V pulses. I'm not able to find something about this in the documentation.
Here is the code of my timer function.
void timer_2_pulse_counter_gpioa1_Init(){
RCC->AHB1ENR |= 0x01; // 1: IO port A clock enabled
//RCC->AHB1ENR |= 0x10; // 1: IO port E clock enabled
// APB1 peripheral reset register
RCC->APB1ENR |= 0x01; // 1: enable TIM2
// GPIO port mode register (GPIOx_MODER)
GPIOA->MODER |= 0x00000008; // 10: Alternate function mode PA1 => AF mode
GPIOA->AFR[0] |= 0x00000010; // 1000: Must refer to AF1 (alternate function for TIM1/ TIm2)
GPIOA->PUPDR |= 0x00000008; // Sets pull down resistor for PA1
// CCMR!: capture/compare mode register 1
TIM2->CCMR1 |= 0x0100; // CC2 channel is configured as input, IC2 is mapped on TI2
// SMCR: Slave Mode control register
TIM2->SMCR |= 0x0007; // Bits[2:0] 111: External Clock Mode 1 - Rising edges of the selected trigger clock the counter.
TIM2->SMCR |= 0x0060; // Bits[6:4] 110: selected Trigger: Filtered Timer Input 2 (TI2FP2)
TIM2->ARR = 0xFFFF; // Set the timer reset on the highest possible value
TIM2->CR1 |= 0x0001; //0001 Enable Timer
}
Many thanks in advance for your support!
Digital input cannot "trigger" at particular voltage set by the programmers level. But you can use it in the analog mode using ADC "analogue watchdog" mode.
If your micro has a built in comparator (many STM32Fxxxxs have one) you can use it to set the "trigger" voltage.
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.
I have a quite strange problem. I use the same micro in three projects. Two of them use HSI clock source, one HSE. Power is supplied exactly the same way. HSI ones go through the ADC calibration without any problems, HSE one gets stuck. Exactly the same initialisation procedure. ADC definitely works as I can read and write from the registers, I am getting ADC readings, but I cant go through the calibration
static inline void ADCCalibration(ADC_TypeDef *ADC) {
uint32_t start = HAL_GetTick();
ADC -> CR |= ADC_CR_ADCAL;
while((ADC1 -> CR & ADC_CR_ADCAL)) {
if((HAL_GetTick() - start) > ADC_CAL_TIMEOUT) {
__BKPT();
}
}
}
__HAL_RCC_ADC12_CLK_ENABLE();
__HAL_RCC_ADC34_CLK_ENABLE();
ADC1 -> DIFSEL = 0;
ADC2 -> DIFSEL = 0;
ADC3 -> DIFSEL = 0;
ADC4 -> DIFSEL = 0;
while(ADC1 -> DIFSEL || ADC2 -> DIFSEL || ADC3 -> DIFSEL || ADC4 -> DIFSEL);
ADC1 -> CR &= ~(ADC_CR_ADVREGEN_Msk);
ADC1 -> CR |= ADC_CR_ADVREGEN_0;
ADC2 -> CR &= ~(ADC_CR_ADVREGEN_Msk);
ADC2 -> CR |= ADC_CR_ADVREGEN_0;
ADC3 -> CR &= ~(ADC_CR_ADVREGEN_Msk);
ADC3 -> CR |= ADC_CR_ADVREGEN_0;
ADC4 -> CR &= ~(ADC_CR_ADVREGEN_Msk);
ADC4 -> CR |= ADC_CR_ADVREGEN_0;
__ADC_Delay();
ADCCalibration(ADC1);
ADCCalibration(ADC2);
ADCCalibration(ADC3);
ADCCalibration(ADC4);
ADC1 -> CR |= ADC_CR_ADEN;
ADC2 -> CR |= ADC_CR_ADEN;
ADC3 -> CR |= ADC_CR_ADEN;
ADC4 -> CR |= ADC_CR_ADEN;
In you while loop, don't you mean:
while((ADC -> CR & ADC_CR_ADCAL))
rather than
while((ADC1 -> CR & ADC_CR_ADCAL))
maybe you could share what you did. Because I faced the same problem.
What I did was to check if ARDY bit was set and also if the ADC is enabled (after reset for some reason it was enabled) and clear/disable them. Due to the ref. manual it is not allowed to start a calibration if ADC is enabled.
/* if ADRDY is set */
if((ADC3->ISR & ADC_ISR_ADRDY) == (ADC_ISR_ADRDY))
{
ADC3->ISR |= (ADC_ISR_ADRDY); /* clear ADRDY */
}
/* if ADC3 is enabled */
if((ADC3->CR & ADC_CR_ADEN) == (ADC_CR_ADEN))
{
ADC3->CR |= (ADC_CR_ADDIS); /* disable ADC3 */
}
I also ran into this same issue on the STM32F303VC (Discovery board).
Your comment on Guillaume's answer was the answer:
The problem was in the PLL & ADC clock settings. This micro require some additional configuration as the ADC has dual clock domain architecture
The reference manual had the following:
Dual clock domain architecture
The dual clock-domain architecture means that each ADC clock is independent from the
AHB bus clock.
The input clock of the two ADCs (master and slave) can be selected between two different
clock sources (see Figure 53: ADC clock scheme):
a) The ADC clock can be a specific clock source, named “ADCxy_CK (xy=12 or 34)
which is independent and asynchronous with the AHB clock”.
It can be configured in the RCC to deliver up to 72 MHz (PLL output). Refer to
RCC Section for more information on generating ADC12_CK and ADC34_CK.
To select this scheme, bits CKMODE[1:0] of the ADCx_CCR register must be
reset.
b) The ADC clock can be derived from the AHB clock of the ADC bus interface,
divided by a programmable factor (1, 2 or 4). In this mode, a programmable divider
factor can be selected (/1, 2 or 4 according to bits CKMODE[1:0]).
To select this scheme, bits CKMODE[1:0] of the ADCx_CCR register must be
different from “00”.
When I originally tried to run the calibration the mode (value of the CKMODE bits) was 0.
This caused the calibration to never complete as I never configured RCC to generate an ADC12_CK clock.
To fix the issue I set the CKMODE bits to 2.
That means the ADC clock will be derived from the AHB clock divided by 2.