I am trying to run a simple blink program on a STM32 board based on STM32F413RG. The led lights up and only toggles when stepping through, not when continuing without breakpoint or running freely on Release mode.
I have setup the eclipse(4.11) to debug the program using a J-link hardware debugger. The code uploads and the LED programmed lights up, but I can see it toggle only when manually stepping through. It does not run without breakpoints.
Sharing my code below where I have setup the clock to be sourced from PLL running on 32 MHz and trying to blink the LED connected to Port B pin 1 every 0.5 second.
One more interesting thing is, Even if I am able to set a breakpoint to see inside the delay() method, the debugger never stops at it / seems to jump across that line of code when single-stepping. Why does it so?
void setupClocks()
{
// we want to use the 24000000 HSE clock (xtal) as the base
RCC->CR |= RCC_CR_HSEON;
// so wait for it to be ready
while ((RCC->CR & RCC_CR_HSERDY) == 0) {}
enter code here
// now configure the PLL (HSE / 12 * 96 /6) gives 32 MHz
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLM_Msk;
RCC->PLLCFGR |= (12)<< RCC_PLLCFGR_PLLM_Pos;
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_Msk;
RCC->PLLCFGR |= (96)<< RCC_PLLCFGR_PLLN_Pos; // 32 MHz
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLP_Msk;
RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_1; // 6
RCC->CR |= RCC_CR_PLLON;
// wait for PLL to be ready
while ((RCC->CR & RCC_CR_PLLRDY) == 0) {}
// now setup the AHB1
// 1/2 of system clock (48 MHz)
RCC->CFGR |= RCC_CFGR_PPRE1_2;
// select PLL (clocked from HSE)
RCC->CFGR |= RCC_CFGR_SW_1;
//reset the ones we use
RCC->AHB1RSTR = RCC_AHB1RSTR_GPIOARST;
RCC->AHB1RSTR = RCC_AHB1RSTR_GPIOBRST;
RCC->AHB1RSTR = RCC_AHB1RSTR_GPIOCRST;
RCC->AHB1RSTR = RCC_AHB1RSTR_GPIODRST;
RCC->AHB1RSTR = 0;
SystemCoreClockUpdate();
}
void initLED()
{
// enable port B clock
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
// set as output for Port B pin 1
GPIOB->MODER |= GPIO_MODER_MODER1_0;
// start with MP10 LED on
GPIOB->ODR = GPIO_ODR_ODR_1;
}
void delay(uint32_t microsec)
{
// wait a bit
uint32_t counter = (microsec * (SystemCoreClock / 1000000U));
while(counter != 0U)
{
counter--;
}
}
void blinkCount(int count)
{
for (int i = 0; i < count; ++i)
{
GPIOB->ODR = ~GPIO_ODR_ODR_1 ;
delay(500000);
GPIOB->ODR = GPIO_ODR_ODR_1;
delay(500000);
}
delay(1000000);
}
int main()
{
setupClocks();
initLED();
while(1)
{
blinkCount(1);
delay(1000000);
}
return 0;
}
Expecting to blink the led as per desired frequency when the program is run without breakpoints or release mode, but the led activity is only visible when stepping through during debug mode.
Be consistent. If you set the clock using directly registers, do not call HAL cube generated functions like SystemCoreClockUpdate(); it is very likely the SystemCoreClock will not have the value you think it has
When doing blocking delays I advice using volatile variables as they will not be removed by the compiler. There is no need of using the 64 bits variables unless you want many minutes delays. Try to do not block. use SysTick (or any other timer) interrupt to implement delays.
example
void delay(volatile uint32_t delay)
{
while(delay--);
}
or for more precise control inline assembly:
void delay1(uint32_t delay)
{
while(delay--)
{
asm volatile("" : : "r"(delay) : "memory");
}
}
which leads to the code:
delay:
sub sp, sp, #8
str r0, [sp, #4]
.L2:
ldr r3, [sp, #4]
sub r2, r3, #1
cmp r3, #0
str r2, [sp, #4]
bne .L2
add sp, sp, #8
bx lr
delay1:
.L6:
subs r0, r0, #1
bxcc lr
b .L6
You need a small delay after enabling the GPIO clock. Put a __DSB() call between enabling the clock and accessing the GPIO registers.
See the product errata document for details.
Related
I'm trying to get the STM32F446 running at full speed, following this tutorial: https://www.youtube.com/watch?v=GJ_LFAlOlSk&t=826s i did everything he does, but the clock speed of my timers is DEADLY slow, like literally, when blinking an LED with pre-scalar of 9 and ARR of 20, it is easily visible by eye.. Wt* am i doing wrong ?
void setup_clock(void)
{
// Enables HSE and waits until it is ready
*RCC_CR |= (1 << RCC_CR_HSEON);
while (!(*RCC_CR & (1 << RCC_CR_HSERDY)));
// Set the power enable clock and voltage regulator
*RCC_APB1ENR |= (1 << RCC_APB1ENR_PWREN);
*PWR_CR |= PWR_CR_VOS(PWR_CR_VOS_SCALEM1);
// Configure flash
*FLASH_ACR = (1 << FLASH_ACR_DCEN) | (1 << FLASH_ACR_ICEN) | (1 << FLASH_ACR_PRFTEN);
*FLASH_ACR |= FLASH_ACR_LATENCY(5);
// Configures HCLK, PCLK1, PCLK2
*RCC_CFGR &= ~RCC_CFGR_HPRE_MASK;
*RCC_CFGR |= RCC_CFGR_HPRE(RCC_CFGR_HPRE_NODIV); // HCLK 180Mhz
*RCC_CFGR &= ~RCC_CFGR_PPRE1_MASK;
*RCC_CFGR |= RCC_CFGR_PPRE1(RCC_CFGR_PPRE1_DIV4); // PCLK1 45Mhz
*RCC_CFGR &= ~RCC_CFGR_PPRE2_MASK;
*RCC_CFGR |= RCC_CFGR_PPRE2(RCC_CFGR_PPRE2_DIV2); // PCLK2 90Mhz
// Configures the main PLL
*RCC_PLLCFGR = RCC_PLLCFGR_PLLN(180) |
RCC_PLLCFGR_PLLP(RCC_PLLCFGR_PLLP_2) |
RCC_PLLCFGR_PLLR(2) |
RCC_PLLCFGR_PLLM(4) |
(1 << RCC_PLLCFGR_PLLSRC);
// Enable PLL
*RCC_CR |= (1 << RCC_CR_PLLON);
while (!(*RCC_CR & (1 << RCC_CR_PLLRDY)));
// Use PLL as clock source
*RCC_CFGR &= ~RCC_CFGR_SW_MASK;
*RCC_CFGR |= RCC_CFGR_SW(RCC_CFGR_SW_PLL_P);
while ((*RCC_CFGR & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS(RCC_CFGR_SWS_PLL));
// Sets the CLOCK Ready status LED
*GPIO_ODR(STATUS_BASE) |= (1 << STATUS_CLKREADY);
}
Below is a complete working project for the NUCLEO_F44RE using gnu tools, everything you need to build and run.
Differences.
I am starting off in the default power mode (well looks like so ar you yes?), so conservatively set the flash divisor to 8 (9 clocks). (can try this after, I would personally set for 8 first get it working then work back to 5).
I am neither using I nor D cache.
I set the system to HSE then set the PLL to use it as well. You skip that and that is probably fine as the HSE is up and ready (to be used by the PLL).
this line
*RCC_CFGR &= ~RCC_CFGR_SW_MASK;
switches the clock to HSI and then
*RCC_CFGR |= RCC_CFGR_SW(RCC_CFGR_SW_PLL_P);
switches the clock to PLL. Need to make up your mind, do not use/abuse the registers in this way as Lundin commented. You should do clean read-modify-writes, read, zero the bits that need to be zeroed (or all of them in the field) set the bits to be set, then write to the register. Use temporary variables for this. Or some flavor of
reg = (reg&this) | that;
but certainly not
reg &= this;
reg |= that;
In general. I doubt that is your problem though...Just a comment by a couple/few of us.
You have PLLQ at an invalid state. Might be a problem, just try it.
I am building for cortex-m0 out of habit/portability of code, can change that easily.
Before PJ brings this up
*RCC_APB1ENR |= (1 << RCC_APB1ENR_PWREN);
*PWR_CR |= PWR_CR_VOS(PWR_CR_VOS_SCALEM1);
Is risky you need to examine the compiled output and it can vary based on compiler, version, phase of the moon. If the str to RCC_APB1ENR is immediately followed by the LDR of PWR_CR, that may not work. What I did see doing experiments based on PJ's comment on another ticket was, that the gpio which was the case there, you can for some reason read the MODER register with the peripheral off so an str of the enable an ldr of the MODER works, then the instructions to do the modify and write are more than enough time for the write. But if you jam the moder register and specifically depending on your compiler and settings, it can optimize those as two back to back stores, I was able to cause this with one compiler and not another. (change settings though and they fix and fail, etc). The GET32/PUT32 thing I do insures there is no problem with touching the peripheral before the enable has had time to settle. YMMV.
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
}
notmain.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define RCCBASE 0x40023800
#define RCC_AHB1ENR (RCCBASE+0x30)
#define RCC_CR (RCCBASE+0x00)
#define RCC_PLLCFGR (RCCBASE+0x08)
#define RCC_CFGR (RCCBASE+0x08)
#define FLASH_ACR 0x40023C00
#define GPIOABASE 0x40020000
#define GPIOA_MODER (GPIOABASE+0x00)
#define GPIOA_BSRR (GPIOABASE+0x18)
//PA5
#define STK_CSR 0xE000E010
#define STK_RVR 0xE000E014
#define STK_CVR 0xE000E018
static void clock_init ( void )
{
unsigned int ra;
//switch to external clock.
ra=GET32(RCC_CR);
ra|=1<<16;
PUT32(RCC_CR,ra);
while(1) if(GET32(RCC_CR)&(1<<17)) break;
if(1)
{
ra=GET32(RCC_CFGR);
ra&=~3;
ra|=1;
PUT32(RCC_CFGR,ra);
while(1) if(((GET32(RCC_CFGR)>>2)&3)==1) break;
}
//HSE ready
}
static void pll_init ( void )
{
unsigned int ra;
//clock_init();
ra=GET32(FLASH_ACR);
ra&=(~(0xF<<0));
ra|=( 8<<0);
PUT32(FLASH_ACR,ra);
//poll this?
ra=GET32(RCC_CFGR);
ra&=(~(0x3<<13));
ra|=( 4<<13); //180/90 = 2
ra&=(~(0x3<<10));
ra|=( 5<<10); //180/45 = 4
PUT32(RCC_CFGR,ra);
//HSE 8Mhz
//PLLM It is recommended to select a frequency of 2 MHz to limit
// PLL jitter.
//PLLN input is 2, want >=50 and <=432 so between 25 and 216
//PLLM 4, PLLN 180, VCO 360, PLLP 2
//PLLM 8/4 = 2
//PLLN 2 * 180 = 360
//PLLP 360 / 2 = 180
//PLLR 2?
//PLLQ 180 / 48 = 3.75 so 4.
ra=0;
ra|=2<<28; //PLLR
ra|=4<<24; //PLLQ dont care
ra|=1<<22; //PLLSRC HSE
ra|=2<<16; //PLLP
ra|=180<<6; //PLLN
ra|=4<<0; //PLLM
PUT32(RCC_PLLCFGR,ra);
ra=GET32(RCC_CR);
ra|=1<<24;
PUT32(RCC_CR,ra);
while(1) if(GET32(RCC_CR)&(1<<25)) break;
ra=GET32(RCC_CFGR);
ra&=~3;
ra|=2;
PUT32(RCC_CFGR,ra);
while(1) if(((GET32(RCC_CFGR)>>2)&3)==2) break;
}
static void led_init ( void )
{
unsigned int ra;
ra=GET32(RCC_AHB1ENR);
ra|=1<<0; //enable GPIOA
PUT32(RCC_AHB1ENR,ra);
ra=GET32(GPIOA_MODER);
ra&=~(3<<(5<<1)); //PA5
ra|= (1<<(5<<1)); //PA5
PUT32(GPIOA_MODER,ra);
}
static void led_on ( void )
{
PUT32(GPIOA_BSRR,((1<<5)<< 0));
}
static void led_off ( void )
{
PUT32(GPIOA_BSRR,((1<<5)<<16));
}
void do_delay ( unsigned int sec )
{
unsigned int ra,rb,rc,rd;
rb=GET32(STK_CVR);
for(rd=0;rd<sec;)
{
ra=GET32(STK_CVR);
rc=(rb-ra)&0x00FFFFFF;
if(rc>=16000000)
{
rb=ra;
rd++;
}
}
}
int notmain ( void )
{
unsigned int rx;
led_init();
PUT32(STK_CSR,0x00000004);
PUT32(STK_RVR,0xFFFFFFFF);
PUT32(STK_CSR,0x00000005);
for(rx=0;rx<5;rx++)
{
led_on();
while(1) if((GET32(STK_CVR)&0x200000)!=0) break;
led_off();
while(1) if((GET32(STK_CVR)&0x200000)==0) break;
}
clock_init();
for(rx=0;rx<5;rx++)
{
led_on();
while(1) if((GET32(STK_CVR)&0x200000)!=0) break;
led_off();
while(1) if((GET32(STK_CVR)&0x200000)==0) break;
}
pll_init();
while(1)
{
led_on();
while(1) if((GET32(STK_CVR)&0x200000)!=0) break;
led_off();
while(1) if((GET32(STK_CVR)&0x200000)==0) break;
}
return(0);
}
build
arm-linux-gnueabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-linux-gnueabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-linux-gnueabi-ld -nostdlib -nostartfiles -T flash.ld flash.o notmain.o -o notmain.elf
arm-linux-gnueabi-objdump -D notmain.elf > notmain.list
arm-linux-gnueabi-objcopy -O binary notmain.elf notmain.bin
(you can naturally change the cortex-m0s to cortex-m4s).
copy notmain.bin to the nucleo card and watch the user led change speeds. faster, half slower, much much faster.
Hmm...
when VOS[1:0] = '0x11, the maximum value of f HCLK is 168 MHz. It can be extended to 180 MHz by activating the over-drive mode. The over-drive mode is not available when VDD ranges from 1.8 to 2.1 V (refer to Section 5.1.3: Voltage regulator for details on how to activate the over-drive mode).
and
11: Scale 1 mode (reset value)
(so no need to mess with that)
and
Entering Over-drive mode
It is recommended to enter Over-drive mode when the application is not running critical
tasks and when the system clock source is either HSI or HSE. To optimize the configuration
time, enable the Over-drive mode during the PLL lock phase.
To enter Over-drive mode, follow the sequence below:
Note:
1. Select HSI or HSE as system clock.
2. Configure RCC_PLLCFGR register and set PLLON bit of RCC_CR register.
3. Set ODEN bit of PWR_CR register to enable the Over-drive mode and wait for the
ODRDY flag to be set in the PWR_CSR register.
4. Set the ODSW bit in the PWR_CR register to switch the voltage regulator from Normal
mode to Over-drive mode. The System will be stalled during the switch but the PLL
clock system will be still running during locking phase.
5. Wait for the ODSWRDY flag in the PWR_CSR to be set.
6. Select the required Flash latency as well as AHB and APB prescalers.
7. Wait for PLL lock.
8. Switch the system clock to the PLL.
9. Enable the peripherals that are not generated by the System PLL (I2S clock, SAI1 and
SAI2 clocks, USB_48MHz clock....).
So I am running at room temperature the chip is nowhere near close to max temp so likely why it works fine being overclocked as I have done here. (technically it is not complete needs to either be 168 or set for overdrive).
If you want 180 vs 168 you should do these steps as documented.
I suspect you are not running your part near max temp either so you should be able to get away with 180 as well. Try removing your pwr register stuff see if that helps, make your flash delay longer, etc. Change to 168mhz, etc.
Did you try for 180 out of the gate or did you try some more reasonable speeds first that are not pushing any edges, like something less than 45mhz then something between 45 and 90 then 90 plus then work to 180?
EDIT
The Flash memory interface accelerates code execution with a system of instruction prefetch and cache lines.
Main features
• Flash memory read operations
• Flash memory program/erase operations
• Read / write protections
• Prefetch on I-Code
• 64 cache lines of 128 bits on I-Code
• 8 cache lines of 128 bits on D-Code
CubeMx has a very handy clock configuration tool. I do not use HAL but this tool saves a lot of time.
As I see you try to reinvent the wheel by using own registers definitions. Use standard CMSIS ones as creating own ones does not make any sense.
It is not possible to have 180MHz clock and use the USB at the same time as you cant get 48MHz required by the USB peripheral.
Here you have some possible settings:
25MHz external osc:
8Mhz external osc:
18MHz internal osc
OK so I have been attempting to create some code using a MSP430FR5994 TI launch pad that utilizes Timer0 and 3 separate compare registers to trigger 3 separate isr's. I have successfully got one to work however as soon as I add another compare register the CCIFE flag sets and never competes the execution of the second isr. I have watched the code in the debugger on both CCstudio and IAR same thing happens in both, the set up registers are correct and the TA0R registers is counting and will trigger the first isr based on the TA0CCR0 but all other compare regs R1 2 3 etc will not trigger and execute successfully. The code is below, idea's on what I am doing wrong would be much appreciated.
#include "msp430.h"
#include <stdbool.h>
#define COUNT_1 12000
#define COUNT_2 800
int main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
PM5CTL0 &= ~LOCKLPM5;
P1DIR |= BIT0 + BIT1;
P1OUT = BIT0 + BIT1;
//set up and enable timer A or TA0 for continous mode
TA0CCR0 = COUNT_1;
TA1CCR1 = COUNT_2;
TA0CTL = TASSEL__ACLK + MC_2; //set the max period for 16bit timer operation
TA1CTL = TASSEL__ACLK + MC_2;
TA0CCTL0 = CCIE; //enable compare reg 0
TA1CCTL1 = CCIE; //enable compare reg 1
//TA0CTL |= TAIE;
_BIS_SR( GIE); //ENABLE GLOBAL INTERRRUPTS
//set the max period for 16bit timer operation
while(true){}
}
#pragma vector= TIMER0_A0_VECTOR //compare interrupt 0 flahse red led
__interrupt void TIMER0_A0(void) {
P1OUT ^= BIT1 ;
}
#pragma vector = TIMER1_A1_VECTOR //compare interrupt 1 flashes green led
__interrupt void TIMER1_A1(void) {
P1OUT ^= BIT0;
}
The User's Guide says in section 25.2.6.1:
The TAxCCR0 CCIFG flag is automatically reset when the TAxCCR0 interrupt
request is serviced.
However, this does not happen for the other CCRx interrupts, because multiple ones use the same interrupt vector.
Section 25.2.5.2 says:
The highest-priority enabled interrupt generates a number in the TAxIV register (see register description). […]
Any access, read or write, of the TAxIV register automatically resets the highest-pending interrupt flag.
So you always have to read the TAxIV register (and with three or more CCRs, you need it to find out which CCR triggered the interrupt):
__interrupt void TIMER1_A1(void) {
switch (TA1IV) {
case TAIV__TACCR1:
P1OUT ^= BIT0;
break;
case TAIV__TACCR2:
...
break;
}
}
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.
Hi I am currently working on USART communication trying to transmit and receive data from any GPIO pin.
I am succeed to transmit data at any baud-rate when it comes to receiving i got stuck at a point.
I was able to receive a character at a time. Pin is set as external falling edge interrupt used a RX pin.
But when i transmit a string like "test" from terminal to controller only "t" is received rest 3 character is garbage value. I was thinking that after receiving first character and saving it, the Interrupt is not triggered as fast for next character.
Many things are hard coded in this sample code for test purpose.
Here the sample code for receiver
void EXTI0_IRQHandler(void){
r0 = GPIOA->IDR;
delay_us(delay_time);
r1 = GPIOA->IDR;
delay_us(delay_time);
r2 = GPIOA->IDR;
delay_us(delay_time);
r3 = GPIOA->IDR;
delay_us(delay_time);
r4 = GPIOA->IDR;
delay_us(delay_time);
r5 = GPIOA->IDR;
delay_us(delay_time);
r6 = GPIOA->IDR;
delay_us(delay_time);
r7 = GPIOA->IDR;
delay_us(delay_time);
r8 = GPIOA->IDR;
delay_us(delay_time);
r9 = GPIOA->IDR;
delay_us(delay_time);
r1 = r1 & 0x00000001;
r2 = r2 & 0x00000001;
r3 = r3 & 0x00000001;
r4 = r4 & 0x00000001;
r5 = r5 & 0x00000001;
r6 = r6 & 0x00000001;
r7 = r7 & 0x00000001;
r8 = r8 & 0x00000001;
x |= r8;
x = x << 1;
x |= r7;
x = x << 1;
x |= r6;
x = x << 1;
x |= r5;
x = x << 1;
x |= r4;
x = x << 1;
x |= r3;
x = x << 1;
x |= r2;
x = x << 1;
x |= r1;
buff1[z++] = x;
EXTI->PR |= 0X00000001;
x=0;
return ;}
Thanks for your help.
The fundamental problem with your solution is that you are sampling the bits at the transition point rather then the bit centre. On detection of the START transition, you delay one bit period only, so sample r1 at the bit transition rather then the bit centre - this will almost certainly result in errors, especially at high speed where the edges may not be very fast. The first delay should be 1.5 bit periods long. (delay_time * 2 / 3) as illustrated below:
A second problem is that you unnecessarily delay after the STOP bit, which will cause you to miss the next START transition because it may occur before you clear the interrupt flag. Your work is done as soon as you have r8.
Sampling r0 and r9 serves no purpose you discard them in any case, and the state r0 is implicit in any event form the EXTI transition, and r9 would only not be 1 if the sender was generating invalid frames. Moreover if you are not sampling r9 the delay before it also becomes unnecessary. These lines should be removed:
delay_us(delay_time);
r9 = GPIOA->IDR;
delay_us(delay_time);
That would at least give you two bit periods where your processor could do other work other then being stuck in the interrupt context, but delaying is an interrupt handler is not good practice - it blocks execution of normal code and all lower priority interrupts making the solution unsuited to real-time systems. In this case if the soft-UART Rx is all the system has to do, you are likely to get better results by simply polling the GPIO rather then using interrupts - at least then other interrupts could run normally, and it is much simpler to implement.
Your "unrolled-loop" implementation also serves no real purpose with the delays in place - even at very high bit rates a loop overhead is likely to be insignificant over the duration of the frame, and if it were you could tweak the delays a little to compensate:
void EXTI0_IRQHandler(void)
{
delay_us(delay_time * 2 / 3);
for( int i = 7; i >= 0; i-- )
{
x |= GPIOA->IDR << i ;
delay_us(delay_time);
}
EXTI->PR |= 0X00000001;
buff1[z++] = x;
x = 0 ;
return ;
}
A more robust solution for a soft receiver that will play well with other processes in your system, should use the EXTI interrupt only to detect the start bit; the handler should disable the EXTI, and start a timer at the baud rate plus half a bit period. The interrupt handler for the timer, samples the GPIO pin at the centre of the bit period, and on the first interrupt after the EXTI, changes the period to one bit period. For each timer interrupt it samples and counts the bits until a whole data word has been shifted in, when it disables the timer and re-enables the EXTI for the next start bit.
I have successfully used this technique on STM32 running at 120MHz at 4800 and pushed it to 38400, but at 26 microseconds per bit it gets quite busy in the interrupt context, and your application presumably has other things to do?
The following is a slightly genericised version of my implementation. It uses STM32 Standard Peripheral Library calls rather then direct register access or the later STM32Cube HAL, but you can easily port it one way or the other as you need. The framing is N,8,1.
#define SOFT_RX__BAUD = 4800u ;
#define SOFT_RX_TIMER_RELOAD = 100u ;
void softRxInit( void )
{
// Enable SYSCFG clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
// Connect the EXTI Line to GPIO Pin
SYSCFG_EXTILineConfig( EXTI_PortSourceGPIOB, EXTI_PinSource0 );
TIM_Cmd( TIM10, DISABLE);
// NVIC initialisation
NVIC_InitTypeDef NVIC_InitStructure = {0,0,0,DISABLE};
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable peripheral clock to timers
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10, ENABLE);
TIM_ARRPreloadConfig( TIM10, DISABLE );
// Generate soft Rx rate clock (4800 Baud)
TIM_TimeBaseInitTypeDef init = {0};
TIM_TimeBaseStructInit( &init ) ;
init.TIM_Period = static_cast<uint32_t>( SOFT_RX_TIMER_RELOAD );
init.TIM_Prescaler = static_cast<uint16_t>( (TIM10_ClockRate() / (SOFT_RX__BAUD * SOFT_RX_TIMER_RELOAD)) - 1 );
init.TIM_ClockDivision = 0;
init.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM10, &init ) ;
// Enable the EXTI Interrupt in the NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
// Dummy call to handler to force initialisation
// of UART frame state machine
softRxHandler() ;
}
// Soft UART Rx START-bit interrupt handler
void EXTI0_IRQHandler()
{
// Shared interrupt, so verify that it is the correct one
if( EXTI_GetFlagStatus( EXTI_Line0 ) == SET )
{
// Clear the EXTI line pending bit.
// Same as EXTI_ClearITPendingBit( EXTI_Line11 )
EXTI_ClearFlag( EXTI_Line0 ) ;
// Call Soft UART Rx handler
softRxHandler() ;
}
}
void TIM1_UP_TIM10_IRQHandler( void )
{
// Call Soft UART Rx handler
softRxHandler() ;
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
}
// Handler for software UART Rx
inline void softRxHandler()
{
static const int START_BIT = -1 ;
static const int STOP_BIT = 8 ;
static const int HALF_BIT = SOFT_RX_TIMER_RELOAD / 2;
static const int FULL_BIT = SOFT_RX_TIMER_RELOAD ;
static int rx_bit_n = STOP_BIT ;
static const uint8_t RXDATA_MSB = 0x80 ;
static uint8_t rx_data = 0 ;
static EXTI_InitTypeDef extiInit = { EXTI_Line0,
EXTI_Mode_Interrupt,
EXTI_Trigger_Falling,
DISABLE } ;
// Switch START-bit/DATA-bit
switch( rx_bit_n )
{
case START_BIT :
{
// Stop waiting for START_BIT
extiInit.EXTI_LineCmd = DISABLE;
EXTI_Init( &extiInit );
// Enable the Interrupt
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
TIM_ITConfig( TIM10, TIM_IT_Update, ENABLE );
// Enable the timer (TIM10)
// Set time to hit centre of data LSB
TIM_SetAutoreload( TIM10, FULL_BIT + HALF_BIT ) ;
TIM_Cmd( TIM10, ENABLE );
// Next = LSB data
rx_data = 0 ;
rx_bit_n++ ;
}
break ;
// STOP_BIT is only set on first-time initialisation as a state, othewise it is
// transient within this scase.
// Use fall through and conditional test to allow
// case to handle both initialisation and UART-frame (N,8,1) restart.
case STOP_BIT :
default : // Data bits
{
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
if( rx_bit_n < STOP_BIT )
{
if( rx_bit_n == 0 )
{
// On LSB reset time to hit centre of successive bits
TIM_SetAutoreload( TIM10, FULL_BIT ) ;
}
// Shift last bit toward LSB (emulate UART shift register)
rx_data >>= 1 ;
// Read Rx bit from GPIO
if( GPIO_ReadInputDataBit( GPIOB, GPIO_Pin_0 ) != 0 )
{
rx_data |= RXDATA_MSB ;
}
// Next bit
rx_bit_n++ ;
}
// If initial state or last DATA bit sampled...
if( rx_bit_n == STOP_BIT )
{
// Stop DATA-bit sample timer
TIM_Cmd( TIM10, DISABLE );
// Wait for new START-bit
rx_bit_n = START_BIT ;
extiInit.EXTI_LineCmd = ENABLE;
EXTI_Init( &extiInit );
// Place character in Rx buffer
serialReceive( rx_data ) ;
}
}
break ;
}
}
The code works in the same way as a real UART as illustrated in the timing diagrem above with the exception that in my implementation the STOP bit is not actually sampled - it is unnecessary; it only serves to ensure that the subsequent START bit is a 1 -> 0 transition and can generally be ignored. A real UART would probably generate a framing error if it were not 1, but if you were not going to handle such errors in any event, there is no purpose in checking.
I can't see in your code where you take account of the start bit that is normally part of a serial transmission. You seem to be only looking for 8 data bits and a stop bit.
With the convention of "start bit is the inverse of stop bit" there will be an additional edge your code detects between characters, thus apparently shifting the bit stream you detect by one bit.
You mentioned that character 't' is received when string "test" is sent.
Introduce sufficient inter character delay in the string.
Hopefully it works.
You can use docklite for sending string with inter character delay.
I am trying to develop a custom bootloader for the Atmel SAM4S, but am not meeting with much success.
I have seen a few other posts on forums around this issue, but having tried solutions from each post I have found, it's time for my own post.
So far my bootloader receives data over UART, and writes it into flash, starting at address 0x00410000, however it fails to launch my program as expected.
Written based on numerous forum posts and also the Atmel bootloader example, this is my jumpToApp function:
void jumpToApp(void)
{
uint32_t loop;
// // Disable IRQ
Disable_global_interrupt();
__disable_irq();
// Disable system timer
SysTick->CTRL = 0;
// //Disable IRQs
for (loop = 0; loop < 8; loop++)
{
NVIC->ICER[loop] = 0xFFFFFFFF;
}
// Clear pending IRQs
for (loop = 0; loop < 8; loop++)
{
NVIC->ICPR[loop] = 0xFFFFFFFF;
}
// -- Modify vector table location
// Barriers
__DSB();
__ISB();
// Change the vector table
SCB->VTOR = ((uint32_t)FLASH_APP_START_ADDR & SCB_VTOR_TBLOFF_Msk);
// Barriers
__DSB();
__ISB();
// -- Enable interrupts
__enable_irq();
// -- Execute application
//------------------------------------------
__asm volatile("movw r1, #0x4100 \n"
"mov.w r1, r1, lsl #8 \n"
"ldr r0, [r1, #4] \n"
"ldr sp, [r1] \n"
"blx r0"
);
}
I have also tried this C based approach:
uint32_t v=FLASH_APP_START_ADDR;
asm volatile ("ldr sp,[%0,#0]": "=r" (v) : "0" (v));
typedef int(*fn_prt_t)(void);
fn_prt_t main_prog;
main_prog = (fn_prt_t)(appStartAddress);
main_prog();
and this alternative:
__DSB();
__ISB();
__set_MSP(*(uint32_t *) FLASH_APP_START_ADDR);
/* Rebase the vector table base address */
SCB->VTOR = ((uint32_t) FLASH_APP_START_ADDR & SCB_VTOR_TBLOFF_Msk);
__DSB();
__ISB();
__enable_irq();
/* Jump to application Reset Handler in the application */
asm("bx %0"::"r"(appStartAddress));
All with the same results, which makes sense as they all do pretty much the same thing.
Debugging the flash sector, I can see that the initial stack pointer in the new vector table is 0x20003b50, and the reset vector address is 0x004104d5, which both seem reasonable.
Stepping through, when execution is meant to jump to my application, I can see that the Program Counter is sat at 0x004104D0, which is close to, but not the reset vector address it should be.
The Stack Pointer also appears to be slightly off, reading 0x20003B28 instead of 0x20003b50.
Dissassembly shows exectution sitting at :
004104D0 b #-4
and never moving away.
Given that I have tried so many variations on jumping to application in flash that seem to have worked for others, I am beginning to think its my flash data at fault.
It is taken directly from the Intel Hex file generated by Atmel Studio 7.0, compiled with the linker offset flag:
-Wl,--section-start=.text=0x00410000
I am new to bootloaders, and have reached the limits of my understanding of low level cpu execution to provide more observation than the above, so any help or observations would be greatly appreciated!