I'm a beginner to embedded software. I try to build my simple real time operating system kernel using C code with the ARM Cortex-M4F Based MCU Tiva C LaunchPad and run in the IAR Embedded Workbench IDE.
The system can support 3 tasks, task A blinks the red LED, task B blinks the blue LED and task C blinks the green LED. The tasks are scheduled in a round-robin way. It uses SysTick to trigger PendSV once per second for context switching.
The following code works fine to blink the LEDs as expected.
#include "include/tm4c_cmsis.h"
#include <intrinsics.h>
#define SYS_CLOCK_HZ 16000000U
#define LED_RED (1U << 1)
#define LED_BLUE (1U << 2)
#define LED_GREEN (1U << 3)
#define MAX_TASK_NUM 3
#define MAX_TASK_SIZE 0x40
int OSStack[MAX_TASK_NUM][MAX_TASK_SIZE] __attribute__ ((aligned (4)));
void task_A();
void task_B();
void task_C();
/* Task Control Block (TCB) */
typedef struct {
int *sp; /* stack pointer */
int status; // 0: does not exists, 1: created, 2: running
} OSTask;
OSTask OSTask_List[MAX_TASK_NUM];
int OS_curr; /* index of the current task */
int OS_next;
int OS_tn; // total task number
int *sp_curr;
int *sp_next;
void OSInit(){
// configure GPIOF for LED blinking
SYSCTL->RCGC2 |= (1U << 5);
GPIOF->DIR |= (1<<3)|(1<<2)|(1<<1);
GPIOF->DEN |= (1<<3)|(1<<2)|(1<<1);
SysTick->LOAD = SYS_CLOCK_HZ - 1;
SysTick->VAL = 0;
SysTick->CTRL = (1U << 2) | (1U << 1) | 1;
OS_tn = 0;
}
void OSCreateTask(void* taskH){
int n=OS_tn;
OS_tn++;
int* p = (int *) OSStack;
OSTask_List[n].sp = p + ((n+1) * MAX_TASK_SIZE);
// init the stack for each task
*(--OSTask_List[n].sp) = (1U << 24); /* xPSR */
*(--OSTask_List[n].sp) = (uint32_t)taskH; /* PC */
*(--OSTask_List[n].sp) = 0x0000000EU + n*16; /* LR */
*(--OSTask_List[n].sp) = 0x0000000CU + n*16; /* R12 */
*(--OSTask_List[n].sp) = 0x00000003U + n*16; /* R3 */
*(--OSTask_List[n].sp) = 0x00000002U + n*16; /* R2 */
*(--OSTask_List[n].sp) = 0x00000001U + n*16; /* R1 */
*(--OSTask_List[n].sp) = 0x00000000U + n*16; /* R0 */
*(--OSTask_List[n].sp) = 0x0000000BU + n*16; /* R11 */
*(--OSTask_List[n].sp) = 0x0000000AU + n*16; /* R10 */
*(--OSTask_List[n].sp) = 0x00000009U + n*16; /* R9 */
*(--OSTask_List[n].sp) = 0x00000008U + n*16; /* R8 */
*(--OSTask_List[n].sp) = 0x00000007U + n*16; /* R7 */
*(--OSTask_List[n].sp) = 0x00000006U + n*16; /* R6 */
*(--OSTask_List[n].sp) = 0x00000005U + n*16; /* R5 */
*(--OSTask_List[n].sp) = 0x00000004U + n*16; /* R4 */
}
// cannot create tasks out of order
void OSSchd(){
if (OS_curr == OS_tn-1)
OS_next = 0;
else
OS_next = OS_curr + 1;
sp_curr = OSTask_List[OS_curr].sp;
sp_next = OSTask_List[OS_next].sp;
*(volatile uint32_t *)0xE000ED04 = (1U << 28);
}
void PendSV_Handler(void) {
asm("PUSH {r4-r11}");
asm("LDR r3, =sp_curr");
asm("STR sp, [r3,#0x00]");
asm("LDR r3, =sp_next");
asm("LDR sp, [r3,#0x00]");
OS_curr = OS_next;
asm("POP {r4-r11}");
}
void SysTick_Handler(void) {
GPIOF->DATA = 0;
OSSchd();
}
void lightRed(void){
GPIOF->DATA_Bits[LED_RED] ^= LED_RED;
}
int main() {
OSInit();
OSCreateTask((void *)task_A);
OSCreateTask((void *)task_B);
OSCreateTask((void *)task_C);
__enable_interrupt();
while (1) {
}
}
void task_A() {
while (1) {
//lightRed();
GPIOF->DATA_Bits[LED_RED] ^= LED_RED;
}
}
void task_B() {
while (1) {
GPIOF->DATA_Bits[LED_BLUE] ^= LED_BLUE;
}
}
void task_C() {
while (1) {
GPIOF->DATA_Bits[LED_GREEN] ^= LED_GREEN;
}
}
However, when I try to change the code inside task_A by calling the function lightRed() as follow:
void lightRed(void){
GPIOF->DATA_Bits[LED_RED] ^= LED_RED;
}
...
void task_A() {
while (1) {
lightRed();
}
}
The three LEDs only blink for 2 cycles and no further response. I stop executing the code and the debugger shows the following problems:
: HardFault exception.
: The processor has escalated a configurable-priority exception to HardFault.
: An integrity check error has occurred on EXC_RETURN (CFSR.INVPC).
: Exception occured at PC = 0x7, LR = 0x1000000
: See the call stack for more information.
: The stack pointer for stack 'CSTACK' (currently 0x200000E0) is outside the stack range (0x20000330 to 0x20000B30)
Also, the call stack is as follow:
-> [__iar_zero_init3 + 0x39]
<Exception frame>
[__vector_table + 0x7]
How can I solve this problem?
This is, fittingly enough, definitely a case of stack overflow. The debug message you're getting is somewhat bogus; the valid stack range given (0x20000330 to 0x20000B30) is probably the configured range for the main stack, which you're not using (all of your tasks are using stacks from your OSStack array).
You've allocated 64 bytes (0x40) for each task's stack. The stack frame you've defined for a task that's suspended is already 64 bytes in size, and that's before it's done anything. If a task is running, and has used any stack space for anything whatsoever, then when it is suspended it will use an additional 64 bytes on top of whatever it is using at the time. This pretty much guarantees you a stack overflow on any nontrivial task, and in this case as soon as you introduce the function call into task_A() you'll be forcing it to use the stack.
The immediate fix is simple: just increase the value of the MAX_TASK_SIZE constant.
Incidentally, the Cortex-M4 has dual stack capability, so you can configure thread-mode code to use a different stack from handler-mode code. This can simplify analysis of stack usage and reduce the required size of task stacks, because interrupt service routines will not use the task stacks for their local storage. Your context switch can remain almost the same, but must read and write PSP to obtain and modify the task stack pointers rather than just using sp.
Related
I'm learning how to program STM32 Nucleo F446RE board using registers.
To know the position of a register, I take from datasheets the boundary address and the offset.
However, I cannot calculate the sum of them. I show an exmaple:
volatile uint32_t *GPIOA = 0x0; // Initialization of the boundary adress
GPIOA = (uint32_t*)0x40020000; // Boundary adress from datasheet
volatile uint32_t *GPIOA_ODR = 0x0; // Initialization of GPIOA_ODR register
GPIOA_ODR = GPIOA + (uint32_t*)0x14; // Calculation of GPIOA_ODR as the sum of the boundary adress and the offset (i.e. 0x14.
Line 5 gives me an error, do you know how to calculate it correctly?
Thank you very much in advance.
It is wrong. If you want to use this extremely inconvenient way:
#define GPIOA 0x4002000
#define ODR_OFFSET 0x14
#define GPIO_ODR (*(volatile uint32_t *)(GPIOA + ODR_OFFSET))
why #define not the pointer? It is just more compiler friendly and saves one memory read.
https://godbolt.org/z/LdLLVN
#define GPIOA 0x4002000
#define ODR_OFFSET 0x14
#define GPIO_ODR (*(volatile uint32_t *)(GPIOA + ODR_OFFSET))
volatile uint32_t *pGPIO_ODR = (volatile uint32_t *)(GPIOA + ODR_OFFSET);
void foo(uint32_t x)
{
GPIO_ODR = x;
}
void bar(uint32_t x)
{
*pGPIO_ODR = x;
}
and resulting code
foo:
ldr r3, .L3
str r0, [r3, #20]
bx lr
.L3:
.word 67117056
bar:
ldr r3, .L6
ldr r3, [r3]
str r0, [r3]
bx lr
.L6:
.word .LANCHOR0
pGPIO_ODR:
.word 67117076
The cast should be outside the constant value, in other words, you are adding GPIOA address + 14 to generate a new address. So the cast must be outside them:
GPIOA_ODR = (uint32_t*)(GPIOA + 0x14);
I tried but nothing. If I insert the GPIOA_ODR = (uint32_t*)(0x40020000 + 0x14); it works, instead if I insert GPIOA_ODR = (uint32_t*)(GPIOA + 0x14); it doesn't work.
Some other ideas?
Thank you very much for the answer. The complete code I'm using is the following:
int main(int argc, char* argv[])
{
/** RCC **/
/* RCC */
volatile uint32_t *RCC = 0x0;
RCC = (uint32_t*)0x40023800;
/* RCC_AHB1ENR */
volatile uint32_t *RCC_AHB1ENR = 0x0;
RCC_AHB1ENR = (uint32_t*)(0x40023800 + 0x30);
*RCC_AHB1ENR |= 0x1;
/** GPIOA **/
/* GPIOA */
volatile uint32_t *GPIOA = 0x0;
GPIOA = (uint32_t*)0x40020000;
/* GPIOA_MODER */
volatile uint32_t *GPIOA_MODER = 0x0;
GPIOA_MODER = (uint32_t*)(0x40020000 + 0x00);
*GPIOA_MODER |= 1 << 16;
*GPIOA_MODER &= ~(0 << 17);
/* GPIOA_ODR */
volatile uint32_t *GPIOA_ODR = 0x0;
GPIOA_ODR = (uint32_t*)(GPIOA + 0x14);
*GPIOA_ODR |= 1 << 8;
}
This code doens't work correctly because of the line GPIOA_ODR = (uint32_t*)(GPIOA + 0x14);. If I insert GPIOA_ODR = (uint32_t*)(0x40020000 + 0x14) it works correctly.
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 have been measuring clock cycle count on the cortex m4 and would now like to do it on the cortex m7. The board I use is STM32F746ZG.
For the m4 everything worked with:
volatile unsigned int *DWT_CYCCNT;
volatile unsigned int *DWT_CONTROL;
volatile unsigned int *SCB_DEMCR;
void reset_cnt(){
DWT_CYCCNT = (volatile unsigned int *)0xE0001004; //address of the register
DWT_CONTROL = (volatile unsigned int *)0xE0001000; //address of the register
SCB_DEMCR = (volatile unsigned int *)0xE000EDFC; //address of the register
*SCB_DEMCR = *SCB_DEMCR | 0x01000000;
*DWT_CYCCNT = 0; // reset the counter
*DWT_CONTROL = 0;
}
void start_cnt(){
*DWT_CONTROL = *DWT_CONTROL | 0x00000001 ; // enable the counter
}
void stop_cnt(){
*DWT_CONTROL = *DWT_CONTROL & 0xFFFFFFFE ; // disable the counter
}
unsigned int getCycles(){
return *DWT_CYCCNT;
}
The problem is that the DWT_CTRL register isn't changed when I run on the m7 and remains 0x40000000 instead of changing to 0x40000001 so the cycle count is always zero. From what I have read in other posts it seems like you need to set the FP_LAR register to 0xC5ACCE55 to be able to change DWT_CTRL.
I added these defines (have tried both FP_LAR_PTR addresses below):
#define FP_LAR_PTR ((volatile unsigned int *) 0xe0000fb0) //according to reference
//#define FP_LAR_PTR ((volatile unsigned int *) 0xe0002fb0) //according to guy on the internet
// Lock Status Register lock status bit
#define DWT_LSR_SLK_Pos 1
#define DWT_LSR_SLK_Msk (1UL << DWT_LSR_SLK_Pos)
// Lock Status Register lock availability bit
#define DWT_LSR_SLI_Pos 0
#define DWT_LSR_SLI_Msk (1UL << DWT_LSR_SLI_Pos)
// Lock Access key, common for all
#define DWT_LAR_KEY 0xC5ACCE55
and this function:
void dwt_access_enable(unsigned int ena){
volatile unsigned int *LSR;
LSR = (volatile unsigned int *) 0xe0000fb4;
uint32_t lsr = *LSR;;
//printf("LSR: %.8X - SLI MASK: %.8X\n", lsr, DWT_LSR_SLI_Msk);
if ((lsr & DWT_LSR_SLI_Msk) != 0) {
if (ena) {
//printf("LSR: %.8X - SLKMASK: %.8X\n", lsr, DWT_LSR_SLK_Msk);
if ((lsr & DWT_LSR_SLK_Msk) != 0) { //locked: access need unlock
*FP_LAR_PTR = DWT_LAR_KEY;
printf("FP_LAR directly after change: 0x%.8X\n", *FP_LAR_PTR);
}
} else {
if ((lsr & DWT_LSR_SLK_Msk) == 0) { //unlocked
*FP_LAR_PTR = 0;
//printf("FP_LAR directly after change: 0x%.8X\n", *FP_LAR_PTR);
}
}
}
}
When I call the uncommented print I get 0xC5ACCE55 but when I printed it after the return of the function I get 0x00000000 and I have no idea why. Am I on the right track or is this completely wrong?
Edit: I think it also would be good to mention that I have tried without all the extra code in the function and only tried to change the LAR register.
BR
Gustav
Looking at the docs again, I'm now incredibly suspicious of a typo or copy-paste error in the ARM TRM. 0xe0000fb0 is given as the address of ITM_LAR, DWT_LAR and FP_LSR (and equivalently for *_LSR). Since all the other ITM registers are in page 0xe0000000, it looks an awful lot like whoever was responsible for that part of the Cortex-M7 documentation took the Cortex-M4 register definitions, added the new LAR and LSR to the ITM page, then copied them to the DWT and FPB pages updating the names but overlooking to update the addresses.
I'd bet my dinner that you're unwittingly unlocking ITM_LAR (or the real FP_LAR), and DWT_LAR is actually at 0xe0001fb0.
EDIT by dwelch
Somebody owes somebody a dinner.
hexstring(GET32(0xE0001FB4));
hexstring(GET32(0xE0001000));
hexstring(GET32(0xE0001004));
hexstring(GET32(0xE0001004));
PUT32(0xE000EDFC,0x01000000);
hexstring(GET32(0xE0001FB4));
hexstring(GET32(0xE0001000));
hexstring(GET32(0xE0001004));
hexstring(GET32(0xE0001004));
PUT32(0xE0001000,0x40000001);
hexstring(GET32(0xE0001FB4));
hexstring(GET32(0xE0001000));
hexstring(GET32(0xE0001004));
hexstring(GET32(0xE0001004));
PUT32(0xE0001FB0,0xC5ACCE55);
PUT32(0xE0001000,0x40000001);
hexstring(GET32(0xE0001FB4));
hexstring(GET32(0xE0001000));
hexstring(GET32(0xE0001004));
hexstring(GET32(0xE0001004));
output
00000000
00000000
00000000
00000000
00000003
40000000
00000000
00000000
00000003
40000000
00000000
00000000
00000001
40000001
0000774F
0000B311
The table in the TRM is funny looking and as the other documentation shows you add the 0xFB0 and 0xFB4 to the base, the rest of the DWT for the Cortex-M7 is 0xE0001xxx and indeed it appears that the LAR and LSR are ate 0xE0001FB0 and 0xE0001FB4.
I would advise against creating your own register definitions when they are defined as part of the CMSIS - to do so requires that both the documentation and your interpretation of it are correct. In this case it appears that the documentation is indeed incorrect, but that the CMSIS headers are correct. It is a lot easier to validate the CMSIS headers automatically than it is to verify the documentation is correct, so I would trust the CMSIS every time.
I am not sure what register FP_LAR might refer to, but your address assignment refers to ITM_LAR, but it seems more likely that you intended DWT_LAR which Cortex-M4 lacks.
Despite my advice to trust it, CMSIS 4.00 omits to define masks for DWT_LSR/SWT_LAR, but I believe they are identical to the corresponding ITM masks.
Note also that the LAR is a write-only register - any attempt to read it is meaningless.
Your code using CMSIS would be:
#include "core_cm7.h" // Applies to all Cortex-M7
void reset_cnt()
{
CoreDebug->DEMCR |= 0x01000000;
DWT->CYCCNT = 0; // reset the counter
DWT->CTRL = 0;
}
void start_cnt()
{
DWT->CTRL |= 0x00000001 ; // enable the counter
}
void stop_cnt()
{
DWT->CTRL &= 0xFFFFFFFE ; // disable the counter
}
unsigned int getCycles()
{
return DWT->CYCCNT ;
}
// Not defined in CMSIS 4.00 headers - check if defined
// to allow for possible correction in later versions
#if !defined DWT_LSR_Present_Msk
#define DWT_LSR_Present_Msk ITM_LSR_Present_Msk
#endif
#if !defined DWT_LSR_Access_Msk
#define DWT_LSR_Access_Msk ITM_LSR_Access_Msk
#endif
#define DWT_LAR_KEY 0xC5ACCE55
void dwt_access_enable( unsigned ena )
{
uint32_t lsr = DWT->LSR;;
if( (lsr & DWT_LSR_Present_Msk) != 0 )
{
if( ena )
{
if ((lsr & DWT_LSR_Access_Msk) != 0) //locked: access need unlock
{
DWT->LAR = DWT_LAR_KEY;
}
}
else
{
if ((lsr & DWT_LSR_Access_Msk) == 0) //unlocked
{
DWT->LAR = 0;
}
}
}
}
I am new to programming on micro controllers and I am trying to write a timer program for the PICLF1571. Every time it wakes from sleep, it's supposed to write to the flash memory. When I debug it with the simulation, it's able to write once, but once it loops the program gets stuck in the interrupt routine. If I comment out the Interrupt routine, the simulation jumps to another place or goes to 0x00.
The only time I see the program get stuck is when the function flash_write is used.
What could be some causes for interrupts to occur if no flags are triggered?
pin setup
void init(void){
ANSELA = 0x0; //|-> Pin setup
TRISA = 0x0;
TRISAbits.TRISA5 = 1;
PORTA = 0x0;
LATA = 0x0;
INTCONbits.GIE = 1; //|-> Interrupt setup
INTCONbits.IOCIE = 1;
IOCAP = 0x0;
IOCAPbits.IOCAP5 = 0;
INTCONbits.IOCIF = 0;
IOCAF = 0x0;
IOCAFbits.IOCAF5 = 0;
WDTCONbits.SWDTEN = 1; //|-> Watchdog Timer setup
WDTCONbits.WDTPS = 0b00001;// reconfigure for correct speed
//currently 0b10001
}
main
//#include <stdio.h>
#include <xc.h>
#include "init.h"
#include "Interrupt.h"
#include "flash.h"
int main(void){
init();
unsigned short ad = 1;
unsigned short f = 0x3FFF;
unsigned short a = 0x0000;
unsigned short ret = 0x0000;
flash_erase(0x0000);
while(1) {
asm("sleep");
flash_write(a,f);
//flash_erase(a);
//flash_read1(a,&ret);
}
return 1;
}
flash_write function. Instructions based on flowchart in datasheet
void flash_write(unsigned short addr, unsigned short data){
INTCONbits.GIE = 0; //||]->start write
PMCON1bits.CFGS = 0;//||]
PMADRH = (unsigned char)((addr >> 8) & 0xFF);
PMADRL = (unsigned char)(addr & 0xFF);
PMCON1bits.FREE = 0;//||]->enable write
PMCON1bits.LWLO = 1;//||]
PMCON1bits.WREN = 1;//||]
PMDATH = (unsigned char)((data >> 8) & 0xFF);
PMDATL = (unsigned char)(data & 0xFF);
PMCON1bits.LWLO = 0;
PMCON2 = 0x55; //||]->unlock sequence
PMCON2 = 0xAA; //||]
PMCON1bits.WR = 1; //||]
NOP(); //||]
NOP(); //||]
PMCON1bits.WREN = 0;//]->end write
INTCONbits.GIE = 1;
asm("RETURN");
}
interrupt routine
#include <xc.h>
void interrupt button(void){
if (INTCONbits.IOCIE == 1 && IOCAFbits.IOCAF5 == 1 ){
int time = 0;
while (PORTAbits.RA5 == 1){//RA5 in sim never changes. always 0
time++;
if (time >= 20000 && PORTAbits.RA5 == 1){
LATA = 0x0;
asm("NOP");
break;
}
if ( time < 20000 && PORTAbits.RA5 == 0){
asm("NOP");
break;
}
}
IOCAF = 0x0;
INTCONbits.IOCIF = 0;
asm("RETFIE");
}
}
0000h is the reset vector address; once written, subsequent resets will jump to 0x3fff, which is not a valid address on a device with only 0x3ff words of flash. Note that since 0x3fff is the erased state of flash memory, the write in this case actually has no effect that is not already caused by the erase alone.
Also understand that, the PIC12 flash memory is word-write/row-erase, and a row is 16 words. So the erase operation will wipe out the entire vector table and the start of the program area too. You are essentially modifying the code, but not in a manner than makes any sense (if it makes sense at all).
You should reserve an area of flash using appropriate linker directives and well away from the vector table (probably the entire last row at 0x3f0, to prevent the linker locating code in the space you want to write at run time.
Another issue is that the flash cell endurance on PIC12F1571 is only 10000 erase/write cycles - are you sure you want to write to the same address every time the device boots? If you "stripe" the reserved row and write to each of the 16 words in turn before erasing the row and restarting, you will increase endurance to 160000 cycles. Add more rows to get greater endurance. Since erased flash has a value 0x3fff (14 bit words), to find the "current value" you need to scan the reserved row(s) for the last value that is not 0x3fff (0x3fff is implicitly therefore not a valid actual value).
I have bootloader, when good jobed in lpc2xxx. But, when I copy this to lpc4078, bootloader not jump to main programm.
I tried:
1) Use #define USER_FLASH_START 0x8000
__asm void boot_jump(uint32_t address)
{
LDR SP, [R0]
LDR PC, [R0, #4]
}
main()
{
bool t = true;
while(t)
{
...WORKING CODE...
uart << "Hello, World!!!";
}
uart << "END";
__disable_irq();
__set_CONTROL(0);
__set_MSP(stack_adr);
SCB->VTOR = (USER_FLASH_START & 0x1FFFFF80);
boot_jump(USER_FLASH_START);
}
2) Use #define USER_FLASH_START 0x8000
void JumpToAppAt(unsigned int * vtbp)
{
__disable_irq();
__set_MSP(vtbp[0]); // load SP
((void (*)(void)) vtbp[1])(); // go...
}
main()
{
bool t = true;
while(t)
{
...WORKING CODE...
uart << "Hello, World!!!";
}
uart << "END";
__disable_irq();
__set_CONTROL(0);
__set_MSP(stack_adr);
SCB->VTOR = (USER_FLASH_START & 0x1FFFFF80);
JumpToAppAt((unsigned int *) USER_FLASH_START);
}
Main programm not start(.
I thought what not work MAIN_PROGRAMM and tried:
3) Use #define USER_FLASH_START 0x0, but nothing has changed.
LPC2xxx are based on ARM7 while LPC4078 is based on Cortex-M4. Regarding the reset vector, I see that you have adjusted to using the 2nd entry of the vector table. Please make sure that the addresses stored in the vector table have their LSB set to 1, i.e. bit[0] = 1, because this bit indicates Thumb instructions, and the Cortex-M4 processor only supports Thumb instructions. (reference)