I want to configure PLL in STM32F429 to its max frequency (180Mhz) without using STMCube-generated configurations. I am using my own register definitions like this
#define RCC_CFGR (*((volatile uint32 *)0x40023808))
and my own SET_BIT()/CLEAR_BIT() macros
My questions are:
1- Is this procedure correct?
2- How can I check if it is working?
3- May the MCU can not handle this speed (reading/writing in registers)?
#define PLL_M 16
#define PLL_N 360
#define PLL_P 2
#define PLL_Q 7
void PLL_init(void)
{
/* System Init */
/* HSI ON */
SET_BIT(RCC_CR, HSION);
/* Reset CFGR register */
RCC_CFGR = 0x00000000 ;
/* Reset PLLCFGR register */
RCC_PLLCFGR = 0x24003010;
/* Reset HSEON, CSSON and PLLON bits */
RCC_CR &= (uint32_t)0xFEF6FFFF;
/************* SetSysClock ************/
RCC_PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16)| (PLL_Q << 24);
/* PLL clock Source HSI or HSE */
CLEAR_BIT(RCC_PLLCFGR, PLLSRC);
/* APB1 PWR Enable*/
SET_BIT(RCC_APB1ENR, PWREN);
/* Select regulator voltage output Scale 1 mode, System frequency up to 180 MHz */
SET_BIT(PWR_CR, VOS0);
SET_BIT(PWR_CR, VOS1);
/* AHB div 1 */
CLEAR_BIT(RCC_CFGR,HPRE3);
CLEAR_BIT(RCC_CFGR,HPRE0);
/* APB2 Div = 2*/
CLEAR_BIT(RCC_CFGR,PPRE20);
CLEAR_BIT(RCC_CFGR,PPRE21);
SET_BIT(RCC_CFGR, PPRE23);
/* APB 1 Div = 8 */
CLEAR_BIT(RCC_CFGR, PPRE10);
SET_BIT(RCC_CFGR, PPRE11);
SET_BIT(RCC_CFGR, PPRE12);
/* SET PLL ON */
SET_BIT(RCC_CR, PLLON);
/* Check PLL is ready */
while(BIT_IS_CLEAR(RCC_CR,PLLRDY));
/* Enable the Over-drive to extend the clock frequency to 180 Mhz */
SET_BIT(PWR_CR, ODEN);
while(BIT_IS_CLEAR(PWR_CSR,ODRDY));
SET_BIT(PWR_CR, ODSWEN);
while(BIT_IS_CLEAR(PWR_CSR,ODSWRDY));
SET_BIT(FLASH_ACR, PRFTEN);
SET_BIT(FLASH_ACR, ICEN);
SET_BIT(FLASH_ACR, DCEN);
FLASH_ACR |= FLASH_ACR_LATENCY_5WS;
/* Select the main PLL as system clock source */
CLEAR_BIT(RCC_CFGR, SW0);
SET_BIT(RCC_CFGR, SW1);
/* Wait till the main PLL is used as system clock source */
while(!BIT_IS_CLEAR(RCC_CFGR,SWS0) && !BIT_IS_SET(RCC_CFGR,SWS1) ); /* Loop till is Set */
}
I did exactly this thing on my STM32F746 - set 216MHz with registers. It looks very similar to mine. Not observing any obvious things. You wait for all ready flags and all. Overdrive and overdrive switching are there, flash wait states are there.
How to check if it's working - timer is an easy idea. Timer that is clocked by a system clock with some prescaler and that outputs PWM to some pin. Given the configuration of the timer is set by you, you should know what output frequency to expect. You can set timer parameters in such a way that you expect timer-interrupt-toggle an LED every 1s.
The MCU should handle it OK if all prescalers for AHB/APB are within limits, as well as Flash latency is set correctly. Which seems to be the case.
If you want to compare the logic with what I do with STM32F746 (which looks almost identical in terms of how you do it), you can check it in this rcc.c file of mine. It definitely works, I tested it.
Related
I am working on distance sensor with PIC16F877A.
I am using MPLAB IDE AND XC8 compiler.
My goal is to gradually turn on the right leds at certain distance levels, but the leds are blinking unstable. What am I doing wrong? distance/5
When it measures 5 cm, it is 5/5 and the result is 1 and 1 led is on.
When it measures 10 cm, it is 10/5 and the result is 2 and 2 leds are on.
When you measure 15 cm, it is 15/5 and the result is 3 and 3 LEDs are on.
When you measure 20 cm, it is 20/5 and the result is 4 and 4 leds are on.
If the distance is less than 5 cm or above 20 cm, no led lights are on
https://i.stack.imgur.com/fFKQg.png
#include <xc.h>
#include <pic16f877a.h>
#pragma config FOSC = XT // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#define _XTAL_FREQ 4000000
#define trigger RC2
#define echo RC3
int calc_distance();
int calc_distance()
{
int distance=0;
trigger=1;
__delay_us(10);
trigger=0;
while(!echo); // normally the echo pin is logic 0. If we toggle the zero and throw it into the while, it returns idle until the echo arrives (until the signal becomes logic 1).
TMR1ON=1;//In the upper loop, while returning the command empty, echo comes and exits the upper loop and timer1 is activated.
while(echo);
TMR1ON=0;//
distance=TMR1/58;
return distance;
}
void main(void)
{
int dist=0; // Create Distance Variable
TRISB = 0x00; // Set PORTB To Be Output Port (All The 8 Pins)
PORTB = 0x00; // Set PORTB To Be LOW For initial State
TRISC2 = 0; // Set RC2 To Be Output Pin ( Trigger )
RC2 = 0;
TRISC3 = 1; // Set RC3 To Be Input Pin ( Echo )
//--[ Configure Timer Module To Operate in Timer Mode ]--
// Clear The Pre-Scaler Select Bits
T1CKPS0=0;
T1CKPS1=0;
TMR1CS=0;// Choose The Local Clock As Clock Source
while(1)
{
calc_distance();
dist = calc_distance()/5;
if(dist==1)
{PORTB = 0x01; __delay_ms(50);}
if(dist==2)
{PORTB = 0x03; __delay_ms(50);}
if(dist==3)
{PORTB = 0x07; __delay_ms(50);}
if(dist==4)
{PORTB = 0x0F; __delay_ms(50);}
else
{PORTB = 0x00; __delay_ms(50);}
}
return;
}
I am working on trying to get the EEPROM Emulator from stm32 working. I have followed the example given for a stm32 l47x board however I am still running into issues. When I call EE_init I end up running into a stack overflow. I am not too familiar with this emulator and am using the default configurations from the example.
This is how I am initializing everything.
EE_Status ee_status = EE_OK;
/* Enable and set FLASH Interrupt priority */
/* FLASH interrupt is used for the purpose of pages clean up under interrupt */
HAL_NVIC_SetPriority(FLASH_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(FLASH_IRQn);
HAL_FLASH_Unlock();
if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB) == RESET)
{
/* Blink LED_OK (Green) twice at startup */
LEDInterface_toggleColor(GREEN);
HAL_Delay(100);
LEDInterface_toggleColor(NONE);
HAL_Delay(100);
LEDInterface_toggleColor(GREEN);
HAL_Delay(100);
LEDInterface_toggleColor(NONE);
ee_status = EE_Init(EE_FORCED_ERASE);
if(ee_status != EE_OK)
{
while(1);
}
This is the eeprom_emul_conf.h settings which I also have not changed
/* Configuration of eeprom emulation in flash, can be custom */
#if defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
#define START_PAGE_ADDRESS 0x08100000U /*!< Start address of the 1st page in flash, for EEPROM emulation */
#else
#define START_PAGE_ADDRESS 0x08080000U /*!< Start address of the 1st page in flash, for EEPROM emulation */
#endif
#define CYCLES_NUMBER 1U /*!< Number of 10Kcycles requested, minimum 1 for 10Kcycles (default),
for instance 10 to reach 100Kcycles. This factor will increase
pages number */
#define GUARD_PAGES_NUMBER 2U /*!< Number of guard pages avoiding frequent transfers (must be multiple of 2): 0,2,4.. */
/* Configuration of crc calculation for eeprom emulation in flash */
#define CRC_POLYNOMIAL_LENGTH LL_CRC_POLYLENGTH_16B /* CRC polynomial lenght 16 bits */
#define CRC_POLYNOMIAL_VALUE 0x8005U /* Polynomial to use for CRC calculation *
/
I am running into the osal_hooks.c file where I am getting stuck in this while loop
#if defined(DOXYGEN)
void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
#else
OSAL_WEAK_FN(void, vApplicationStackOverflowHook)( TaskHandle_t xTask, char *pcTaskName )
#endif
{
volatile char * name = pcTaskName;
(void)name;
while (1)
{
;
}
}
Im sure I need to change where I allocate the memory but what is the best way to go about this. Thank you
I am working on a weird problem: As a part of my project, I migrated a firmware from CooCox to TrueStudio. Both, CooCox and TrueStudio automatically create some standard files while creating a project for a specific Microcontroller. The Microcontroller used here is the STM32F407VGT6. I am using ms - delay and s - delay which are derived from the µs - Delay function I will show you.
*edit2: I should mention, that the original project is a pure C project. I am trying to make the Project a C++/C project in TrueStudio.
What I will try now is to migrate the firmware into a TrueStudio pure C project and see if the problem still exists.
I will inform you about the results
**Results: The problem is actually gone now in the pure C Project, but I would really like to implement classes etc using C++. Any ideas how to solve this?
**
*
The initializing systick code is (HCLK Frequency = 168MHz).
*edit1: the HCLK Frequency equals the SYSCLK *
void systick_init(void){
RCC_ClocksTypeDef RCC_Clocks;
Systick_Delay=0;
RCC_GetClocksFreq(&RCC_Clocks);
SysTick_Config((RCC_Clocks.HCLK_Frequency / 1000000) - 1);
}
The function for the 1µs Delay looks like this:
void delay_us(volatile uint32_t delay)
{
Systick_Delay = delay;
while(Systick_Delay != 0);
}
The Systick Handler contains the following Code:
void SysTick_Handler(void)
{
// Tick für Delay
if(Systick_Delay != 0x00)
{
Systick_Delay--;
}
}
When I create a .hex file to flash the µC with using Coocox, the timing function works (with some minor accuracy mistakes that don't bother me).
When I create the .hex file with TrueStudio, the delays have massive inaccuracys. For example, a delay of 500ms becomes a delay of roughly 2s.
Since the Code is written dependant on the actual HCLK_Frequency, I can't understand the mistake and in my understanding, even if the HCLK should differ, the 1µs Delay should still take about 1µs.
My next step will be comparing the automatically created system files, but maybe anyone has a different approach / another idea?
*edit 3: I normally include my systick - header with the command ' extern "C" '. So my systick source file is a .c file. When I rename the file to systick.cpp, and I include the header without 'extern "C"', the delay function does not work at all. Maybe, that helps with the solution?
*
You are either running off a different clock or have different PLL settings. Looks like your clock speed is 1/4 of what you had before.
The basic startup code provided does not always set the maximum speed for the board. Have a look in some of the examples in the stm32cube.zip code. You will find some System Clock Configuration code for your board which will select the correct clock and pll settings. (this will be in your code somewhere as well).
Look in main.c under stm32cubef4/projects/STM32F4-Discovery\Demonstrations\src.
You will find the following code which sets up the clock:
/**
* #brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 168000000
* HCLK(Hz) = 168000000
* AHB Prescaler = 1
* APB1 Prescaler = 4
* APB2 Prescaler = 2
* HSE Frequency(Hz) = 8000000
* PLL_M = 8
* PLL_N = 336
* PLL_P = 2
* PLL_Q = 7
* VDD(V) = 3.3
* Main regulator output voltage = Scale1 mode
* Flash Latency(WS) = 5
* #param None
* #retval None
*/
static void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/* Enable Power Control clock */
__HAL_RCC_PWR_CLK_ENABLE();
/* The voltage scaling allows optimizing the power consumption when the device is
clocked below the maximum system frequency, to update the voltage scaling value
regarding system frequency refer to product datasheet. */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* Enable HSE Oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
/* STM32F405x/407x/415x/417x Revision Z devices: prefetch is supported */
if (HAL_GetREVID() == 0x1001)
{
/* Enable the Flash prefetch */
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
}
}
I found the solution now!
For anyone having similar problems:
You need to declare the SysTick_Handler function as
extern "C" void SysTick_Handler(void)
{
//Tick für Delay
if(Systick_Delay != 0x00)
{
Systick_Delay--;
}
}
Now it is working like it is supposed to do.
The following is the interrupt handler for LED0 to toggle when an interrupt occurs on the UART0 due to LIN transmission from master to slave, my device is the slave. But without connecting my device(slave) to the master that means without any LIN transmission over UART, the LED0 is toggling. I cannot understand, how can this happen automatically? How can an interrupt be generated and Toggle of LED0 can happen?
void FTM0_IRQHandler()
{
if (1==((FTM0_C0SC & FTM_CnSC_CHF_MASK)>>FTM_CnSC_CHF_SHIFT) ) /* If the CHF of the channel is equal to 0 */
{
(void)FTM0_C0SC; /* Read to clear flag */
FTM0_C0SC ^= FTM_CnSC_CHF_MASK; /* Clear flag */
FTM0_C0V = FTM0_C0V + 391 ; /* Refresh interrupt period */
if (LED_counter>=50){
/* Toggle LED for LIN transmission */
/* Reset counter */
LED0_TOGGLE;
LED_counter = 0;
}
LED_counter++;
}
}
In my main funtion I have called my timer initialization as follows:
lin_application_timer_FTM0();
And the above function is defined as follows:
void lin_application_timer_FTM0()
{
SIM_SCGC |= SIM_SCGC_FTM0_MASK; /* Enable Clock for FTM0 */
FTM0_SC |= FTM_SC_PS(7); /* Select Preescaler in this case 128. 20 Mhz /128 =156.25 Khz. */
/* Counter increase by one every 6.4 us */
/* Enable Channle 0*/
FTM0_C0SC |= FTM_CnSC_CHIE_MASK; /* Enable channel 0 interrupt */
FTM0_C0SC |= FTM_CnSC_MSA_MASK; /* Channel as Output compare mode */
/*Select interrupt frequency*/
FTM0_C0V = FTM_CnV_VAL(391) ; /* Interrupt every 2.5ms */
FTM0_SC |= FTM_SC_CLKS(1); /*FTM0 use system clock*/
/* Set the ICPR and ISER registers accordingly */
NVIC_ICPR |= 1 << ((INT_FTM0-16)%32);
NVIC_ISER |= 1 << ((INT_FTM0-16)%32);
}
Can anyone explain why is the LED0 toggling without any interrupt?
It’s possible that the interrupt is actually being triggered. Does your interrupt pin have a pull up or pull-down resistor attached? If it’s disconnected and just “floating”, electrical noise in the environment can cause spurious interrupts.
I am trying to read several PWM signals from an RC receiver into an ATMega 2560. I am having trouble understanding how the ICRn pin functions as it appears to be used for all three compare registers.
The RC PWM signal has a period of 20ms with a HIGH pulse of 2ms being a valid upper value and 1ms being a valid lower value. So the value will sweep from 1000us to 2000us. The period should begin at the rising edge of the pulse.
I have prescaled the 16MHz clock by 8 to have a 2MHz timer an thus should be able to measure the signal to 0.5us accuracy which is sufficient for my requirements.
Please note that I am having not problems with PWM output and this question is specifically about PWM input.
My code thus far is attached below. I know that I will have to use ICR3 and an ISR to measure the PWM values but I am unsure as to the best procedure for doing this. I also do not know how to check if the value measured is from PE3, PE4, or PE5. Is this code right and how do I get the value that I am looking for?
Any help would be greatly appreciated.
// Set pins as inputs
DDRE |= ( 0 << PE3 ) | ( 0 << PE4 ) | ( 0 << PE5 );
// Configure Timers for CTC mode
TCCR3A |= ( 1 << WGM31 ) | ( 1 << WGM30 ); // Set on compare match
TCCR3B |= ( 1 << WGM33 ) | ( 1 << WGM32 ) | ( 1 << CS31); // Set on compare match, prescale_clk/8
TCCR3B |= ( 1 << ICES5 ) // Use rising edge as trigger
// 16 bit register - set TOP value
OCR3A = 40000 - 1;
OCR3B = 40000 - 1;
OCR3C = 40000 - 1;
TIMSK3 |= ( 1 << ICIE3 );
I had forgotten to post my solution a few months ago so here it is...
I used a PPM receiver in the end so this code can easily edited to read a simple PWM.
In my header file I made a structure for a 6 channel receiver that I was using for my project. This can be changed as required for receivers with more or less channels.
#ifndef _PPM_H_
#define _PPM_H_
// Libraries included
#include <stdint.h>
#include <avr/interrupt.h>
struct orangeRX_ppm {
uint16_t ch[6];
};
volatile unsigned char ch_index;
struct orangeRX_ppm ppm;
/* Functions */
void ppm_input_init(void); // Initialise the PPM Input to CTC mode
ISR( TIMER5_CAPT_vect ); // Use ISR to handle CTC interrupt and decode PPM
#endif /* _PPM_H_ */
I then had the following in my .c file.
// Libraries included
#include <avr/io.h>
#include <stdint.h>
#include "ppm.h"
/* PPM INPUT
* ---
* ICP5 Pin48 on Arduino Mega
*/
void ppm_input_init(void)
{
DDRL |= ( 0 << PL1 ); // set ICP5 as an input
TCCR5A = 0x00; // none
TCCR5B = ( 1 << ICES5 ) | ( 1 << CS51); // use rising edge as trigger, prescale_clk/8
TIMSK5 = ( 1 << ICIE5 ); // allow input capture interrupts
// Clear timer 5
TCNT5H = 0x00;
TCNT5L = 0x00;
}
// Interrupt service routine for reading PPM values from the radio receiver.
ISR( TIMER5_CAPT_vect )
{
// Count duration of the high pulse
uint16_t high_cnt;
high_cnt = (unsigned int)ICR5L;
high_cnt += (unsigned int)ICR5H * 256;
/* If the duration is greater than 5000 counts then this is the end of the PPM signal
* and the next signal being addressed will be Ch0
*/
if ( high_cnt < 5000 )
{
// Added for security of the array
if ( ch_index > 5 )
{
ch_index = 5;
}
ppm.ch[ch_index] = high_cnt; // Write channel value to array
ch_index++; // increment channel index
}
else
{
ch_index = 0; // reset channel index
}
// Reset counter
TCNT5H = 0;
TCNT5L = 0;
TIFR5 = ( 1 << ICF5 ); // clear input capture flag
}
This code will use an trigger an ISR every time ICP5 goes from low to high. In this ISR the 16bit ICR5 register "ICR5H<<8|ICR5L" holds the number of pre-scaled clock pulses that have elapsed since the last change from low to high. This count is typically less than 2000 us. I have said that if the count is greater than 2500us (5000 counts) then the input is invalid and the next input should be ppm.ch[0].
I have attached an image of PPM as seen on my oscilloscope.
This method of reading PPM is quite efficient as we do not need to keep polling pins to check their logic level.
Don't forget to enable interrupts using the sei() command. Otherwise the ISR will never run.
Let's say you want to do the following (I'm not saying this will allow you to accurately measure the PWM signals but it might serve as example on how to set the registers)
Three timers running, which reset every 20 ms. This can be done by setting them in CTC mode for OCRnA: wgm3..0 = 0b0100.
//timer 1
TCCR4A = 0;
TCCR1B = (1<<CS11) | (1<<WGM12);
OCR1A = 40000 - 1;
//timer 3 (there's no ICP2)
TCCR3A = 0;
TCCR3B = (1<<CS31) | (1<<WGM32);
OCR3A = 40000 - 1;
//timer 4
TCCR4A = 0;
TCCR4B = (1<<CS41) | (1<<WGM42);
OCR4A = 40000 - 1;
Now connect each of the three pwm signals to their own ICPn pin (where n = timer). Check the datasheet for the locations of the different ICPn pins (i'm pretty sure it's not PE3, 4, 5)
Assuming the pwm signals start high at t=0 and go low after their high-time for the remainder of the period. You want to measure the high-time so we trigger an interrupt for each when a falling edge occurs on the ICPn pin.
bit ICESn in the TCCRnB register set to 0 will select the falling edge (this is already done in the previous code block).
To trigger the interrupts, set the corresponding interrupt enable bits:
TIMSK1 |= (1<<ICIE1);
TIMSK3 |= (1<<ICIE3);
TIMSK4 |= (1<<ICIE4);
sei();
Now each time an interrupt is triggered for ICn you can grab the ICRn register to see the time (in clockperiods/8) at which the falling edge occurred.