STM32F103 blue pill interrupts from scratch - embedded

How could I create an interrupt for a blue pill from scratch?
I do not want to use any sort of special library. Also, I use Keil IDE, thus, by "building from scratch" I refer rather not to use any extra library than to assemble the project without the help of an IDE.
I tried to find resources, but no success. Could anybody help me and at least provide some information/bibliography for me? I would be grateful.
Moreover, by "strange library" I mean any other library than the stmf32f1xx.h header. I would like to fire an interrupt when one of the pins' input value toggles. In order to do this, on AVR MCUs it was very simple as long as only a few register values should be changed. Unfortunately, I don't know how an interrupt within an ARM MCU functions and in which registers should I write what values.
Also, a better understanding of the ARM MCU's interrupt mechanism would make me more prepared for tackling debouncing issues.

I am not going to take you entirely literally when you mandate "no libraries", because no one who wants to get work done and knows what they are doing on Cortex-M would do that - and I will assume at least that you will use the CMSIS - a common API provided for all ARM Cortex-M devices, and which makes your code more, not less portable.
All the CMSIS code is provided as source, rather than static library, so there is nothing hidden and if you chose not to use it, you can see how it works and replicate that functionality (needlessly) if you wish.
In the CMSIS default implementations are provided as "weak-links" that can be overridden by user code simply by defining a function of the pre-defined name to override the default. The default implementation is generally an infinite loop - so that unhandled interrupts are "trapped" so you can intervene with your debugger or wait for a watchdog reset for example.
The Cortex-M core interrupt handlers and exception handlers have common names across all Cortex-M parts:
Reset_Handler
NMI_Handler
HardFault_Handler
MemManage_Handler
BusFault_Handler
UsageFault_Handler
SVC_Handler
DebugMon_Handler
PendSV_Handler
SysTick_Handler
Peripheral interrupt handlers have names defined by the vendor, but the naming convention is <interrupt_source>_IRQHandler. For example on STM32F1xx EXTI0_IRQHandler is the shared external interrupt assigned to bit zero of GPIO ports.
To implement an CMSIS interrupt handler, all you need do is:
Implement the interrupt handler function using the CMSIS handler function name
Enable the interrupt in the NVIC (interrupt controller).
There other are things you might do such as assign the interrupt priority scheme (the split between preempt priorities and subpriorities), but lets keep it simple for the time being.
Because it is ubiquitous to all Cortex-M parts, and because it is useful in almost any non-trivial application an illustration using the SYSTICK interrupt is useful as a starting point.
#include "stm32f1xx.h"
volatile uint32_t msTicks = 0 ;
void SysTick_Handler(void)
{
msTicks++ ;
}
int main (void)
{
if( SysTick_Config( SystemCoreClock / 1000 ) != 0 ) // 1ms tick
{
// Error Handling
}
...
}
SysTick_Config() is another CMSIS function. In core_cm3.h it looks like this:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
So let's say you have a external interrupt source on the falling edge of GPIOA pin 0, then you would use the STM32 EXTI0 interrupt. The minimal handler would look like:
void EXTI0_IRQHandler(void)
{
EXTI->PR |= (1<<0); // clear pending interrupt
// Handle interrupt...
}
Setting up the EXTI requires enabling the GPIO and the EXTI itself as well as the NVIC:
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN ; // enable clock for GPIOA
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN ; // enable clock for Alternate Function
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0 ; // set pin to use
EXTI->IMR = EXTI_IMR_MR0 ; // unmask interrupt
EXTI->EMR = EXTI_EMR_MR0 ; // unmask event
EXTI->FTSR = EXTI_FTSR_TR0 ; // set falling edge
NVIC->ISER[0] |= (1 << (EXTI0_IRQChannel & 0x1F)); // enable interrupt EXTI 0
The peripheral registers and structures are defined in stm32f10weakx.h, and the "weak" default peripheral handlers to be overridden are in startup_stm32f10x_cl.s for your specific part. Any handlers you override must match these symbol names exactly.
All the peripheral interrupt sources and how to configure them is defined un the ST Reference Manual RM0008.
All the Cortex-M core specific stuff - systtick, NVIC, exception handlers etc. is provided by ARM at https://developer.arm.com/ip-products/processors/cortex-m/cortex-m3
CMSIS for CM3 is documented at https://developer.arm.com/documentation/dui0552/a/

Related

Can't get the analogue watchdog to trigger an interrupt on the DFSDM peripheral of a STM32L475

I have an AMC1306 current shunt modulator feeding 1-bit PDM data at 10 MHz into a STM32L475. Filter0 takes the bit stream from Channel0 and applies a sinc3 filter with Fosr=125 and Iosr=4. This provides 24-bit data at 20 kHz and is working fine. The DMA transfers the data into a 1-word circular buffer in main memory to maintain fresh data.
I want to be able to call an interrupt function if the 24-bit value leaves a certain window. This would be caused in an over-voltage situation and needs to disengage the MOSFET driver. It would seem this functionality is offered by the analogue watchdog within the peripheral.
I am using STM32CubeIDE and the graphical interface within the IDE to configure the peripherals. Filter0 global interrupts are enabled. I have added this code:
/* USER CODE BEGIN 2 */
HAL_DFSDM_FilterRegularStart_DMA(&hdfsdm1_filter0, Vbus_DMA, 1);
// Set up the watchdog
DFSDM_Filter_AwdParamTypeDef awdParamFilter0;
awdParamFilter0.DataSource = DFSDM_FILTER_AWD_FILTER_DATA;
awdParamFilter0.Channel = DFSDM_CHANNEL_0;
awdParamFilter0.HighBreakSignal = DFSDM_NO_BREAK_SIGNAL;
awdParamFilter0.HighThreshold = 250;
awdParamFilter0.LowBreakSignal = DFSDM_NO_BREAK_SIGNAL;
awdParamFilter0.LowThreshold = -250;
HAL_DFSDM_FilterAwdStart_IT(&hdfsdm1_filter0, &awdParamFilter0);
/* USER CODE END 2 */
I have also used the HAL callback function
/* USER CODE BEGIN 4 */
void HAL_DFSDM_FilterAwdCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter, uint32_t Channel, uint32_t Threshold)
{
HAL_GPIO_WritePin(GPIOA, LED_Pin, GPIO_PIN_SET);
}
/* USER CODE END 4 */
But the callback function never runs! I have experimented with the thresholds (I even made them zero).
In the debugger I can see the AWDIE=0x1 (So the AWD interrupt is enabled). The AWDF = 0x1 (So the threshold has been crossed and the peripheral should be requesting an interrupt...). The code doesn't even trigger a breakpoint in the stm32l4xx_it.c filter0 interrupt. So it'd seem no DFSDM1_FLT0 interrupts are happening
I'd be enormously appreciative of any help, any example code, any resources to read. Thanks in advance.
I know the DMA conversion complete callbacks work
I have played around with various thresholds and note that the AWDF gets set when the threshold is crossed.

Unexpected behaviour of an iexternal interrupt function, dspic33f

The project I'm desperatly trying to finish is rather simple in term of microcontroller programming. The dspic33fj128mc802 I use basically has to do 3 things:
receive data via UART and convert it into PWM signals for servomotors
regularly wake up its ADC to check a battery level
change its baudrate working values when fed an external interrupt.
It's the last point that causes issue. In my circuit I have a switch. One position corresponds to a baudrate value, the other one to a second value. I didn't find any documentation on how to trigger an interrupt on any voltage level change, so I use rising edge and falling edge trigger in combination with checking the current state of the pin I chose for my interrupt.
Furthemore, I have two other interrupt functions in my code, one for UART reception, and the second one is a timer interrupt to wake up the ADC periodically. Interrupt priorities are the following: UART -> 1, Timer -> 2, External Interrupt -> 6 (any number above 2 really).
Here is my interrupt code:
void __attribute__((interrupt, auto_psv)) _INT0Interrupt( void )
{
IEC0bits.INT0IE = 0; // disable INT0 interrupt
if(IFS0bits.INT0IF){
if (PORTBbits.RB7 == 1){ //if pin at high logic level
INTCON2bits.INT0EP = 1; //falling edge trigger
LATAbits.LATA0 = 1;
U1BRG = 23;
}
else{ //if pin at low logic level
INTCON2bits.INT0EP = 0; //rising edge trigger
LATAbits.LATA0 = 0;
U1BRG = 1;
}
}
IFS0bits.INT0IF = 0; //clear INT0 flag
IEC0bits.INT0IE = 1; // enable INT0 interrupt
}
The weird behaviour now -> When pulling the pin to low, the baudrate is set at the right value, the UART commmunication works perfectly. When pulled to high, the previous communication doesn't work anymore, proof that the baudrate has changed, but setting the new communication at that new baudrate doesn't work either. The LED status change works fine as well.
It is to be noted that all the different apsects of this project have been tested multiple times, each section works well, only adding this External Interrupt made the whole thing crash. The microcontroller works fine, my baudrate values are good, my circuit has been tested and has no issues.... I jst think I don't know how to properly use an external interrupt.

Can NVIC enable interupt after CPSID i is execute

__disable_irq();
// Setting timer 1
...
NVIC_SetPriority (TIM1_UP_IRQn, 1);
NVIC_EnableIRQ (TIM1_UP_IRQn);
// do something
...
__enable_irq();
Can Timer 1 interrupt occur after NVIC_EnableIRQ (TIM1_UP_IRQn) is execute.
You do not need to use ASM at all. CMSIS is has handy functions.
__disable_irq();
__enable_irq();
NVIC_EnableIRQ (TIM1_UP_IRQn);
Does not enable interrupts only enables particular interrupt source

STM32 SPI dropping data while using interrupt

I'm trying to send a variable size array of bytes over SPI using interrupts. The system is composed by two nucleo STM32L432 boards. The sender board works fine, but I'm having issue with the receiver board. Specifically, I noticed that very often some bytes are dropped. Beyond the default initialization provided by CubeMX, I have also the following settings in my init function:
// Trigger RXNE when the FIFO is 1/4 full
LL_SPI_SetRxFIFOThreshold(sw.spi_sw2pc,LL_SPI_RX_FIFO_TH_QUARTER);
// Enable RXNE interrupt
LL_SPI_EnableIT_RXNE(sw.spi_sw2pc);
// Enable SPI
if((SPI3->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
{
// If disabled, I enable it
SET_BIT(sw.spi_sw2pc->CR1, SPI_CR1_SPE);
}
The SPI is set to work at 10 Mbit/s. Can it be that the communication speed is too fast?
Following are the IRQ handler and the callback.
IRQ handler
void SPI3_IRQHandler(void)
{
/* USER CODE BEGIN SPI3_IRQn 0 */
/* Check RXNE flag value in ISR register */
if(LL_SPI_IsActiveFlag_RXNE(SPI3))
{
/* Call function Slave Reception Callback */
SW_rx_callback();
}
/* USER CODE END SPI3_IRQn 0 */
/* USER CODE BEGIN SPI3_IRQn 1 */
/* USER CODE END SPI3_IRQn 1 */
}
Callback
void SW_rx_callback(void)
{
// RXNE flag is cleared by reading data in DR register
while(LL_SPI_IsActiveFlag_RXNE(SPI3))
recv_buffer[recv_buffer_index++] = LL_SPI_ReceiveData8(SPI3);
if(LL_SPI_GetRxFIFOLevel(SPI3) == LL_SPI_RX_FIFO_EMPTY)
{
// If there are no more data
new_data_arrived = true;
memset(recv_buffer,'\0',recv_buffer_index);
recv_buffer_index = 0;
}
}
Thank you in advance for your help.
SPI on 10 Mbits mean that you will have 1.25 millions interrupts per second (in case of 8bit transfer) and this is quite enough to process by interrupts especially in combination with HAL.
STM32L4xx is quite fast (80MHz) but in this case it mean that every interrupt call can't take longer than 64 cycles. but calling interrupt take 12 cycles, exit interrupt 10 cycles (it is in ideal state with no wait states on bus) so if your interrupt code will take 42 or more cycles then you can be sure that you miss some bytes.
Here are my suggestions:
First try to enable some compiler optimizations, to speed-up the code.
Change interrupt routine and remove everything unnecessary from interrupt handler (use SW FIFO and process received data in main loop)
But best solution in your case can be to use DMA transfer.

Event handling in embedded code

I want to know how events are used in embedded system code.
Main intention is to know how exactly event flags are set/reset in code. and how to identify which task is using which event flag and which bits of the flag are getting set/reset by each task.
Please put your suggestion or comments about it.
Thanks in advance.
(edit 1: copied from clarification in answer below)
Sorry for not specifying the details required. Actually I am interested in the analysis of any application written in C language using vxworks/Itron/OSEK OS. For example there is eventLib library in vxworks to support event handling. I want to know that how one can make use of such system routines to handle events in task. What is event flag(is it global/local...or what ?), how to set bits of any event flag and which can be the possible relationship between task and event flags ??
How task can wait for multiple events in AND and OR mode ??
I came across one example in which the scenario given below looks dangerous, but why ??
Scenarios is ==> *[Task1 : Set(e1), Task2 : Wait(e1) and Set(e2), Task3 : Wait(e2) ]*
I know that multiple event flags waited by one task or circular dependency between multiple tasks(deadlock) are dangerous cases in task-event relationship, but how above scenario is dangerous, I am not getting it....Kindly explain.
(Are there any more such scenarios possible in task-event handling which should be reviewed in code ?? )
I hope above information is sufficient ....
Many embedded systems use Interrupt Service Routines (ISR) to handle events. You would define an ISR for a given "flag" and reset that flag after you handle the event.
For instance say you have a device performing Analog to Digital Conversions (ADC). On the device you could have an ISR that fires each time the ADC completes a conversion and then handle it within the ISR or notify some other task that the data is available (if you want to send it across some communications protocol). After you complete that you would reset the ADC flag so that it can fire again at it's next conversion.
Usually there are a set of ISRs defined in the devices manual. Sometimes they provide general purpose flags that you could also handle as you wish. Each time resetting the flag that caused the routine to fire.
The eventLib in VxWorks is similar to signal() in unix -- it can indicate to a different thread that something occurred. If you need to pass data with the event, you may want to use Message Queues instead.
The events are "global" between the sender and receiver. Since each sender indicates which task the event is intended for, there can be multiple event masks in the system with each sender/receiver pair having their own interpretation.
A basic example:
#define EVENT1 0x00000001
#define EVENT2 0x00000002
#define EVENT3 0x00000004
...
#define EVENT_EXIT 0x80000000
/* Spawn the event handler task (event receiver) */
rcvTaskId = taskSpawn("tRcv",priority,0,stackSize,handleEvents,0,0,0,0,0,0,0,0,0,0);
...
/* Receive thread: Loop to receive events */
STATUS handleEvents(void)
{
UINT32 rcvEventMask = 0xFFFFFFFF;
while(1)
{
UINT32 events = 0;
if (eventReceive(rcvEventMask. EVENTS_WAIT_ANY, WAIT_FOREVER, &events) == OK)
{
/* Process events */
if (events & EVENT1)
handleEvent1();
if (events & EVENT2)
handleEvent2();
...
if (events & EVENT_EXIT)
break;
}
}
return OK;
}
The event sender is typically a hardware driver (BSP) or another thread. When a desired action occurs, the driver builds a mask of all pertinent events and sends them to the receiver task.
The sender needs to obtain the taskID of the receiver. The taskID can be a global,
int RcvTaskID = ERROR;
...
eventSend(RcvTaskID, eventMask);
it can be registered with the driver/sender task by the receiver,
static int RcvTaskID = ERROR;
void DRIVER_setRcvTaskID(int rcvTaskID)
{
RcvTaskID = rcvTaskID;
}
...
eventSend(RcvTaskID, eventMask);
or the driver/sender task can call a receiver API method to send the event (wrapper).
static int RcvTaskID;
void RECV_sendEvents(UINT32 eventMask)
{
eventSend(RcvTaskID, eventMask);
}
This question needs to provide more context. Embedded systems can be created using a wide range of languages, operating systems (including no operating system), frameworks etc. There is nothing universal about how events are created and handled in an embedded system, just as there is nothing universal about how events are created and handled in computing in general.
If you're asking how to set, clear, and check the various bits that represent events, this example may help. The basic strategy is to declare a (usually global) variable and use one bit to represent each condition.
unsigned char bit_flags = 0;
Now we can assign events to the bits:
#define TIMER_EXPIRED 0x01 // 0000 0001
#define DATA_READY 0x02 // 0000 0010
#define BUFFER_OVERFLOW 0x04 // 0000 0100
And we can set, clear, and check bits with bitwise operators:
// Bitwise OR: bit_flags | 00000001 sets the first bit.
bit_flags |= TIMER_EXPIRED; // Set TIMER_EXPIRED bit.
// Bitwise AND w/complement clears bits: flags & 11111101 clears the 2nd bit.
bit_flags &= ~DATA_READY; // Clear DATA_READY bit.
// Bitwise AND tests a bit. The result is BUFFER_OVERFLOW
// if the bit is set, 0 if the bit is clear.
had_ovflow = bit_flags & BUFFER_OVERFLOW;
We can also set or clear combinations of bits:
// Set DATA_READY and BUFFER_OVERFLOW bits.
bit_flags |= (DATA_READY | BUFFER_OVERFLOW);
You'll often see these operations implemented as macros:
#define SET_BITS(bits, data) data |= (bits)
#define CLEAR_BITS(bits, data) data &= ~(bits)
#define CHECK_BITS(bits, data) (data & (bits))
Also, a note about interrupts and interrupt service routines: they need to run fast, so a typical ISR will simply set a flag, increment a counter, or copy some data and exit immediately. Then you can check the flag and attend to the event at your leisure. You probably do not want to undertake lengthy or error-prone activities in your ISR.
Hope that's helpful!
Sorry for not specifying the details required. Actually I am interested in the analysis of any application written in C language using vxworks/Itron/OSEK OS.
For example there is eventLib library in vxworks to support event handling.
I want to know that how one can make use of such system routines to handle events in task. What is event flag(is it global/local...or what ?), how to set bits of any event flag and which can be the possible relationship between task and event flags ??
I hope above information is sufficient ....
If you're interested in using event-driven programming at the embedded level you should really look into QP. It's an excellent lightweight framework and if you get the book "Practical UML Statecharts in C/C++" by Miro Samek you find everything from how to handle system events in an embedded linux kernel (ISR's etc) to handling and creating them in a build with QP as your environment. (Here is a link to an example event).
In one family of embedded systems I designed (for a PIC18Fxx micro with ~128KB flash and 3.5KB RAM), I wrote a library to handle up to 16 timers with 1/16-second resolution (measured by a 16Hz pulse input to the CPU). The code is set up to determine whether any timer is in the Expired state or any dedicated wakeup pin is signaling, and if not, sleep until the next timer would expire or a wakeup input changes state. Quite a handy bit of code, though I should in retrospect probably have designed it to work with multiple groups of eight timers rather than one set of 16.
A key aspect of my timing routines which I have found to be useful is that they mostly aren't driven by interrupts; instead I have a 'poll when convenient' routine which updates the timers off a 16Hz counter. While it sometimes feels odd to have timers which aren't run via interrupt, doing things that way avoids the need to worry about interrupts happening at odd times. If the action controlled by a timer wouldn't be able to happen within an interrupt (due to stack nesting and other limitations), there's no need to worry about the timer in an interrupt--just keep track of how much time has passed.