I wrote code for generation square pulse. Everything works fine till I activate ADC Interrupt by NVIC commands. Is ADC IRQ Handler wrote correctly?(I am specious on Handler). ADC Sets on continuous mode by only one channel is using. End_of_Conversion and End_of_Sequence Interrupt flag have been set.
#include <stdint.h>
#include "stm32f0xx.h"
//static __IO uint32_t RPM;
//uint32_t RPM=1000;
#define DUTY_CYCLE 0.1
void SysTick_Handler(void);
void ADC1_IRQHandler(void);
void Delay_10us(__IO uint32_t nTime);
static __IO uint32_t TimingDelay;
static __IO uint32_t adcValue;
//int calibration_factor;
uint32_t rpm;
int global_counter=0;
int main(void)
{
// Set SysTick for create 10 microsecond delay
if(SysTick_Config(80))
{
while(1);
}
//CLOCK
RCC->CR|=RCC_CR_HSEON;
while(!(RCC->CR&RCC_CR_HSERDY));
RCC->CR|=RCC_CR2_HSI14ON;
while(!(RCC->CR2|=RCC_CR2_HSI14RDY));
RCC->CFGR|=RCC_CFGR_SW_HSE;
while(!(RCC->CFGR&=RCC_CFGR_SWS_HSE));
RCC->APB2ENR|=RCC_APB2ENR_ADCEN;
// ADC1->CFGR2|=ADC_CFGR2_CKMODE_1;
RCC->AHBENR|=RCC_AHBENR_GPIOAEN;
//GPIO
GPIOA->MODER|=GPIO_MODER_MODER4_0;
GPIOA->PUPDR|=GPIO_PUPDR_PUPDR4_0;
GPIOA->ODR|=GPIO_ODR_4;
GPIOA->MODER|=GPIO_MODER_MODER5;
//NVIC
NVIC_EnableIRQ(ADC1_COMP_IRQn);/*if I comment this two lines everything works*/
NVIC_SetPriority(ADC1_COMP_IRQn,0);/*if I comment this two lines everything works*/
//ADC
ADC1->CR&=~ADC_CR_ADEN;
ADC1->SMPR|=ADC_SMPR1_SMPR;
ADC1->CFGR1|=ADC_CFGR1_CONT;
ADC1->IER|=ADC_IER_EOSIE|ADC_IER_EOCIE;
ADC1->CHSELR|=ADC_CHSELR_CHSEL5;
// for(int i=0;i<10;i++);
// ADC1->CR|=ADC_CR_ADCAL;
// while(ADC1->CR&ADC_CR_ADCAL);
// calibration_factor=ADC1->DR;
for(int i=0;i<10;i++);
ADC1->ISR|=ADC_ISR_ADRDY;
ADC1->CR|=ADC_CR_ADEN;
while(!(ADC1->ISR&=ADC_ISR_ADRDY));
ADC1->CR|=ADC_CR_ADSTART;
/* Loop forever */
int counter=0;
while(1){
if(rpm<250){rpm=250;}
int T=10000/rpm;
int OnDelay=T*DUTY_CYCLE;
int OffDelay=T*(1-DUTY_CYCLE);
GPIOA->ODR &=~GPIO_ODR_4;
Delay_10us(OffDelay);
GPIOA->ODR|=GPIO_ODR_4;
Delay_10us(OnDelay);
counter++;
if(counter==59)
{
GPIOA->ODR &=~GPIO_ODR_4;
Delay_10us(2*T);
counter=0;
continue;
}
}
}
void Delay_10us(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
void SysTick_Handler(void){
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
void ADC1_IRQHandler()
{
//
}
Your program enters ADC1_IRQHandler and stays there forever, since the handler is empty.
If you've enabled an interrupt, you must handle it - at least clear the interrupt source. And don't look at the SysTick_Handler it is a special case with autoreset. For other interrupts the corresponding status bit must be reset, or a special sequence of operations performed. Details are usually found in the description of a status register, in your case it is ADC_ISR.
With End_Of_Conversion and End_Of_Sequence enabled, the interrupt could be triggered by the EOC and EOSEQ status bits. Both could be cleared by writing 1 to it. The handler should look something like this:
void ADC1_IRQHandler()
{
if(ADC1->ISR & ADC_ISR_EOC)
{
//Handle end of conversion
ADC1->ISR = ADC_ISR_EOC;
}
if(ADC1->ISR & ADC_ISR_EOSEQ)
{
//Handle end of sequence
ADC1->ISR = ADC_ISR_EOSEQ;
}
}
The name of a handler depends on the startup used, it must match name of a placeholder in the interrupt vector table of the startup file. In Cube-generated projects this is an assembler file (.s) in Startup folder (eg. /Core/Startup/startup_stm32f407zgtx.s)
Related
I'm currently working on a SAME70 board made by Atmel. I plugged on it an extention OLED1 board (Atmel too) on EXT1 port. My goal is to recover the information about interrupt type (falling or raising) when I'm pressing the button 3 on OLED1 board. I have a function which allows me to set the different registers related to an interrupts. But unfortunately the register which indicates the polarity (FLHSR) stay always at 0 whatever the state of the button.
Below my code.
#include <asf.h>
void set_Interupt_Pin()
{
uint32_t mask = 1 << 19; /*bitmasking*/
PIOA->PIO_IER = mask; /*enable interruptions*/
PIOA->PIO_AIMER = mask; /*add new interruption*/
PIOA->PIO_ESR = mask; /*set interrupt source on edge*/
PIOA->PIO_REHLSR = mask; /* set interrupt to rising edge*/
PIOA->PIO_FELLSR= mask; /* set interrupt to falling edge*/
NVIC_DisableIRQ(ID_PIOA);
NVIC_ClearPendingIRQ(ID_PIOA);
NVIC_EnableIRQ(ID_PIOA); /*set NVIC to get interruptions from PIOA*/
NVIC_SetPriority(ID_PIOA, 4); /*priority 4*/
}
void PIOA_Handler(void)
{
printf("FRLHSR: %x\n",PIOA->PIO_FRLHSR); /*state of polarity event */
printf("ISR: %x\n",PIOA->PIO_ISR);
printf("ELSR: %x\n",PIOA->PIO_ELSR);
printf("AIMMR: %x\n",PIOA->PIO_AIMMR);
}
int main(void)
{
const usart_serial_options_t usart_serial_options = {
.baudrate = CONF_UART_BAUDRATE,
.charlength = CONF_UART_CHAR_LENGTH,
.paritytype = CONF_UART_PARITY,
.stopbits = CONF_UART_STOP_BITS
};
sysclk_init();
board_init();
stdio_serial_init(CONF_UART, &usart_serial_options);
set_Interupt_Pin();
while(1){}
}
You can see here the result of print when I press button (first part) and I release button (second part).
Best regards
I am programming a STM8S103F3 to TX on UART via interrupt. I understand a write to DR after "Transmit data register empty interrupt" will start another TX, so I have this in my ISR. But it only works if my main loop spins on wait for interrupt. If it spins on nop only the first char is TXed - as though the write to DR within the ISR does not generate a subsequent interrupt.
Using SDCC compiler.
sdcc -mstm8 -o build\uart.hex uart.c
#include <stdint.h>
#include <stdlib.h>
#include "stm8.h"
#define DEBUG_BUF_SIZE 10
char debugBuf[DEBUG_BUF_SIZE];
volatile unsigned char *debugPtr;
// UART Tx interrupt
void TX_complete(void) __interrupt(UART_TX_COMPLETE) {
if(*debugPtr != 0) {
UART1_DR = *debugPtr++;
}
}
void log(char *msg)
{
unsigned char i = 0;
UART1_CR2 &= ~UART_CR2_TIEN;
for(; msg[i] != 0 && i<DEBUG_BUF_SIZE-1; i++) {
debugBuf[i] = msg[i];
}
debugBuf[i] = 0;
debugPtr = debugBuf;
UART1_CR2 |= UART_CR2_TIEN;
// Write to DR will start tx
UART1_DR = *debugPtr++;
}
int main(void)
{
// UART 115K2 baud, interrupt driven tx
// UART1_CR1, UART_CR3 reset values are 8N1
UART1_BRR2 = 0x0B;
UART1_BRR1 = 0x08;
UART1_CR2 |= UART_CR2_TEN | UART_CR2_TIEN;
/* Set clock to full speed (16 Mhz) */
CLK_CKDIVR = 0;
log("Run\r\n");
while(1) {
// Only the first char is txed if nop is used
nop();
// But all chars txed if wfi is used
// wfi();
}
}
See your reference manual for stm8 (I use CD00218714) at chapter 12.9.1 you will see default value (after reset) of CPU condition code register it's 0x28 - this mean that just after start your mcu will work at interrupt level 3 and all software interrupt are disabled, only RESET and TRAP will workable.
According to program manual (I use CD00161709) instruction WFI change interrupt level to level 0 and your software interrupt of USART become workable.
You need to insert asm("rim"); just after initialization code (after line CLK_CKDIVR = 0;) - this will make your code workable with asm("nop"); based main loop.
I am designing an IIR 2nd order Lowpass filter with sampling frequency = 100Hz and cutoff frequency = 10 Hz. The filter coefficients are of Chebyshev Type I using fdatool in Matlab.
But the code is not able to filter the signal (i.e. for all frequencies it gives the output with same amplitudes as the input signal) . Only minor decrease in amplitude is observed for an input signal of 10 KHz and above. I assure you that the ADC and DAC are working fine as i have tested the for FFT filter.
Here is the code:
/* Include core modules */
#include "stm32f4xx.h"
#include "stdint.h"
#include "stdlib.h"
#include "arm_math.h"
#include "my_files.h"
#define URS 2
#define numStages 1
#define NUM_TAPS 5*numStages
#define samples 3
////////ADC FUNCTION//////////////////
void ADC_configure(void)
{
RCC->APB2ENR|=1Ul<<8; // ADC1 clock enabled
ADC1->CR2|=0x00000001; // enable ADC
ADC1->CR1|=0; // single conversion ADC1 pin 0 has been selected
}
int32_t readADC(void)
{
ADC1->CR2|=(1UL<<30);
return(ADC1->DR);
}
////////DAC FUNCTION/////////////////
int32_t dv1,dv2,ds;
//---function declaration--//
// initilising DAC---------//
void DAC_init(void)
{
RCC->APB1ENR|=1UL<<29;
DAC->CR|=((1UL<<16)|(1UL<<0));
RCC->AHB1ENR|=0x00000001; // clock to gpio A
GPIOA->MODER|=0x00000F03; // pt0,4,5 in Analog mode
}
// Sending to DAC...........//
void Send_DAC(int32_t data_in1, int32_t data_in2)
{ dv1=data_in1;
dv2=data_in2<<16;
ds=dv2+dv1;
DAC->DHR12RD=ds;
}
/* IIR settings */
float32_t pState[2*numStages];
const float pCoeffs[NUM_TAPS] = {1,2,1,-1.1997,0.5157};//{b0,b1,b2,a1,a2}
/* Global variables */
float32_t Input[samples]; /* Data to be read from ADC */
float32_t InputData[samples]; /* Data to be processed */
float32_t Output[samples]; /* Output filtered Data */
arm_biquad_cascade_df2T_instance_f32 S; /* ARM IIR module */
uint16_t i;
void TIM3_Init (void) {
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; /* enable clock for TIM1 */
TIM3->PSC = 8600; /* set prescaler = 10KHz */
TIM3->ARR = 100; /* set auto-reload = 10ms */
TIM3->RCR = 0; /* set repetition counter */
TIM3->CR1 |= (1UL << URS);
TIM3->DIER = TIM_DIER_UIE; /* Update Interrupt enable */
NVIC_EnableIRQ(TIM3_IRQn); /* TIM1 Interrupt enable */
NVIC_SetPriority (TIM3_IRQn, 0);
TIM3->CR1 |= TIM_CR1_CEN; /* timer enable */
}
void TIM3_IRQHandler() {
/*Shift Operation*/
for(i=samples-1;i>0;i--){
Input[i]= Input[i-1];
InputData[i]= Input[i];
}
/* Input part from the ADC */
Input[0] = (float32_t)readADC();
InputData[0] = Input[0];
//////////IIR//////////////////////
/* Initialize the IIR module */
arm_biquad_cascade_df2T_init_f32(&S, numStages, pCoeffs, pState);
/* Process the data through the IIR module */
arm_biquad_cascade_df2T_f32(&S, InputData, Output, samples);
////////DAC Output/////////////////
Send_DAC(Input[0], Output[0]);
}
/////////main function///////////////
int main(void) {
/* Initialize system */
SystemInit();
DAC_init();
ADC_configure();
TIM3_Init();
while (1) {
}
}
Any suggestion or solution would be of great help.
Some possible problems:
Did you enable the FPU?
Check alignment for ADC (and DAC?).
Ensure the interrupt-handler does not run too long (overflow).
Good you do not use the stdlib for much more tha init, btw. But you really should use symbolic constants for the register initialization! This does not cost extra.
Not directly related, but will(!) give wrong results: If I get it right, you trigger each conversion in readADC. This leads to jitter (resulting in noise on the digitized signal); trigger the conversations by a timer (that's what the trigger system is for actually) and use the ADC-interrupt to read the data or use a DMA (the STM DMA provides a double-buffer mode which is perfect for this). In this simple example, if using DMA, you can even get along completely without interrupt and do the calculations in the main program.
For the DAC you should the same.
Not sure why use a timer anyway; the ADC can self-trigger. Is that not sufficient?
You do not need to init IIR filter every time. Do it only once in init code. Init procedure clears previous values in pState, but they are required for IIR to perform correctly. That's the reason why your filter doesn't work. Presence of FPU only influences the speed of computation.
I'm developing a C application using avr-libc on an AVR ATmega328P microcontroller. Since I don't have an ICE debugger for it, I followed these instructions and this tutorial for making the stdio.h functions such as printf able to use the hardware UART as stdout.
That works, and I can see the output on a PC terminal connected to my target board, but the strange thing is: When I have only one printf on main, but before the main loop something is causing the processor to reset, while if I have a printf only inside the main loop or before the main loop AND inside the loop it works fine. Something like this:
#include <stdio.h>
/* stream definitions for UART input/output */
FILE uart_output = FDEV_SETUP_STREAM(uart_drv_send_byte, NULL, _FDEV_SETUP_WRITE);
FILE uart_input = FDEV_SETUP_STREAM(NULL, uart_drv_read_byte, _FDEV_SETUP_READ);
int main() {
/* Definition of stdout and stdin */
stdout = &uart_output;
stdin = &uart_input;
/* Configures Timer1 for generating a compare interrupt each 1ms (1kHz) */
timer_init()
/* UART initialization */
uart_drv_start(UBRRH_VALUE, UBRRL_VALUE, USE_2X, &PORTB, 2);
/* Sets the sleep mode to idle */
set_sleep_mode(SLEEP_MODE_IDLE);
printf("START ");
/* main loop */
while(1) {
printf("LOOP ");
/* Sleeps so the main loop iterates only on interrupts (avoids busy loop) */
sleep_mode();
}
}
The code above produces the following output:
START LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP
which is expected. If we comment the printf("START ") line it produces this:
LOOP LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP
which is also fine. The problem is, if I don't have any printf inside the while loop, it goes like this:
START START START START START START ... START
That clearly shows the processor is being restarted, since the expected output would be just one START and nothing else while the infinite loop goes on being awaken only on the 1 kHz timer interrupts. Why is this happening? I should stress there's no watchdog timer configured (if there was, the cases where only LOOP is printed would be interrupted by a new START also).
Monitoring execution using GPIO pins
To try to get some insight into the situation, I turned GPIO pins ON and OFF around the problematic print("START ") and sleep_mode in the main loop:
int main() {
/* Irrelevant parts suppressed... */
GPIO1_ON;
printf("START ");
GPIO1_OFF;
/* Main loop */
while(1) {
/* Sleeps so the main loop iterates only on interrupts (avoids busy loop) */
GPIO2_ON;
sleep_mode();
GPIO2_OFF;
}
}
It turned out that GPIO1 stays ON for 132 µs (printf("START ") call time) and then OFF for 6.6 ms - roughly the time to transmit the six characters at 9600 bit/s - and GPIO2 toggles 12 times (six times two interrupts: the UART-ready-to-transmit interrupt and the UART-empty-data-register interrupt), showing sleep active for another 1.4 ms before GPIO1 goes ON again indicating a new printf("START ") - hence after reset. I'll probably have to check out the UART code, but I'm pretty sure the non-interrupt UART version also shows the same problem, and that doesn't explain either why having a printf inside the main loop works OK, without a reset happening (I would expect the reset would happen in any case should the UART code be faulty).
(SOLVED!): For completeness, The UART init and TX code is below**
This was my first attempt in writing an interrupt driven UART driver for the AVR, but one that could be used either on a RS-232 or a RS-485, which requires activating a TX_ENABLE pin while transmitting data. It turned out that, since I had to make the code useable either on ATmega328P or ATmega644, the interrupt vectors have different names, so I used a #define TX_VECTOR to assume the right name according to the processor used. In the process of making and testing the driver the choosing of "TX_VECTOR" for the UDRE data empty interrupt ended up masking the fact I hadn't defined the USART0_TX_vect yet (this was work in progress, I might not even need both anyway...)
Right now I just defined an empty interrupt service routine (ISR) for USART0_TX_vect and the thing doesn't reset anymore, showing #PeterGibson nailed it right on. Thanks a lot!
// Interrupt vectors for Atmega328P
#if defined(__AVR_ATmega328P__)
#define RX_VECTOR USART_RX_vect
#define TX_VECTOR USART_UDRE_vect
// Interrupt vectors for Atmega644
#elif defined(__AVR_ATmega644P__)
#define RX_VECTOR USART0_RX_vect
#define TX_VECTOR USART0_UDRE_vect
#endif
ISR(TX_VECTOR)
{
uint8_t byte;
if (!ringbuffer_read_byte(&txrb, &byte)) {
/* If RS-485 is enabled, sets TX_ENABLE high */
if (TX_ENABLE_PORT)
*TX_ENABLE_PORT |= _BV(TX_ENABLE_PIN);
UDR0 = byte;
}
else {
/* No more chars to be read from ringbuffer, disables empty
* data register interrupt */
UCSR0B &= ~_BV(UDRIE0);
}
/* If RS-485 mode is on and the interrupt was called with TXC0 set it
* means transmission is over. TX_ENABLED should be cleared. */
if ((TX_ENABLE_PORT) && (UCSR0A & _BV(TXC0) & _BV(UDR0))) {
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
UCSR0B &= ~_BV(UDRIE0);
}
}
void uart_drv_start(uint8_t ubrrh, uint8_t ubrrl, uint8_t use2x,
volatile uint8_t* rs485_tx_enable_io_port,
uint8_t rs485_tx_enable_io_pin)
{
/* Initializes TX and RX ring buffers */
ringbuffer_init(&txrb, &tx_buffer[0], UART_TX_BUFSIZE);
ringbuffer_init(&rxrb, &rx_buffer[0], UART_RX_BUFSIZE);
/* Disables UART */
UCSR0B = 0x00;
/* Initializes baud rate */
UBRR0H = ubrrh;
UBRR0L = ubrrl;
if (use2x)
UCSR0A |= _BV(U2X0);
else
UCSR0A &= ~_BV(U2X0);
/* Configures async 8N1 operation */
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
/* If a port was specified for a pin to be used as a RS-485 driver TX_ENABLE,
* configures the pin as output and enables the TX data register empty
* interrupt so it gets disabled in the end of transmission */
if (rs485_tx_enable_io_port) {
TX_ENABLE_PORT = rs485_tx_enable_io_port;
TX_ENABLE_PIN = rs485_tx_enable_io_pin;
/* Configures the RS-485 driver as an output (on the datasheet the data
* direction register is always on the byte preceding the I/O port addr) */
*(TX_ENABLE_PORT-1) |= _BV(TX_ENABLE_PIN);
/* Clears TX_ENABLE pin (active high) */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
/* Enables end of transmission interrupt */
UCSR0B = _BV(TXCIE0);
}
/* Enables receptor, transmitter and RX complete interrupts */
UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);
}
FIXED UART CODE (NOW WORKING 100%!)
In order to help anyone interested or developing a similar interrupt driven UART driver for the AVR ATmega, here it goes the code with the problems above fixed and tested. Thanks to everyone who helped me spot the problem with the missing ISR!
// Interrupt vectors for Atmega328P
#if defined(__AVR_ATmega328P__)
#define RX_BYTE_AVAILABLE USART_RX_vect
#define TX_FRAME_ENDED USART_TX_vect
#define TX_DATA_REGISTER_EMPTY USART_UDRE_vect
// Interrupt vectors for Atmega644
#elif defined(__AVR_ATmega644P__)
#define RX_BYTE_AVAILABLE USART0_RX_vect
#define TX_FRAME_ENDED USART0_TX_vect
#define TX_DATA_REGISTER_EMPTY USART0_UDRE_vect
#endif
/* I/O port containing the pin to be used as TX_ENABLE for the RS-485 driver */
static volatile uint8_t* TX_ENABLE_PORT = NULL;
/** Pin from the I/O port to be used as TX_ENABLE for the RS-485 driver */
static volatile uint8_t TX_ENABLE_PIN = 0;
ISR(RX_BYTE_AVAILABLE)
{
// Read the status and RX registers.
uint8_t status = UCSR0A;
// Framing error - treat as EOF.
if (status & _BV(FE0)) {
/* TODO: increment statistics */
}
// Overrun or parity error.
if (status & (_BV(DOR0) | _BV(UPE0))) {
/* TODO: increment statistics */
}
ringbuffer_write_byte(&rxrb, UDR0);
}
ISR(TX_FRAME_ENDED)
{
/* The end of frame interrupt will be enabled only when in RS-485 mode, so
* there is no need to test, just turn off the TX_ENABLE pin */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
}
ISR(TX_DATA_REGISTER_EMPTY)
{
uint8_t byte;
if (!ringbuffer_read_byte(&txrb, &byte)) {
/* If RS-485 is enabled, sets TX_ENABLE high */
if (TX_ENABLE_PORT)
*TX_ENABLE_PORT |= _BV(TX_ENABLE_PIN);
UDR0 = byte;
}
else {
/* No more chars to be read from ringbuffer, disables empty
* data register interrupt */
UCSR0B &= ~_BV(UDRIE0);
}
}
void uart_drv_start(uint8_t ubrrh, uint8_t ubrrl, uint8_t use2x,
volatile uint8_t* rs485_tx_enable_io_port,
uint8_t rs485_tx_enable_io_pin)
{
/* Initializes TX and RX ring buffers */
ringbuffer_init(&txrb, &tx_buffer[0], UART_TX_BUFSIZE);
ringbuffer_init(&rxrb, &rx_buffer[0], UART_RX_BUFSIZE);
cli();
/* Disables UART */
UCSR0B = 0x00;
/* Initializes baud rate */
UBRR0H = ubrrh;
UBRR0L = ubrrl;
if (use2x)
UCSR0A |= _BV(U2X0);
else
UCSR0A &= ~_BV(U2X0);
/* Configures async 8N1 operation */
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
/* If a port was specified for a pin to be used as a RS-485 driver TX_ENABLE,
* configures the pin as output and enables the TX data register empty
* interrupt so it gets disabled in the end of transmission */
if (rs485_tx_enable_io_port) {
TX_ENABLE_PORT = rs485_tx_enable_io_port;
TX_ENABLE_PIN = rs485_tx_enable_io_pin;
/* Configures the RS-485 driver as an output (on the datasheet the data
* direction register is always on the byte preceding the I/O port addr) */
*(TX_ENABLE_PORT-1) |= _BV(TX_ENABLE_PIN);
/* Clears TX_ENABLE pin (active high) */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
/* Enables end of transmission interrupt */
UCSR0B = _BV(TXCIE0);
}
/* Enables receptor, transmitter and RX complete interrupts */
UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);
sei();
}
void uart_drv_send_byte(uint8_t byte, FILE *stream)
{
if (byte == '\n') {
uart_drv_send_byte('\r', stream);
}
uint8_t sreg = SREG;
cli();
/* Write byte to the ring buffer, blocking while it is full */
while(ringbuffer_write_byte(&txrb, byte)) {
/* Enable interrupts to allow emptying a full buffer */
SREG = sreg;
_NOP();
sreg = SREG;
cli();
}
/* Enables empty data register interrupt */
UCSR0B |= _BV(UDRIE0);
SREG = sreg;
}
uint8_t uart_drv_read_byte(FILE *stream)
{
uint8_t byte;
uint8_t sreg = SREG;
cli();
ringbuffer_read_byte(&rxrb, &byte);
SREG = sreg;
return byte;
}
You've possibly enabled the UDRE (Uart Data Register Empty) interrupt and not set a vector for it, so when the interrupt triggers the processor resets (according to the defaults). When printf is called continuously in the main loop, this interrupt is never triggered.
From the docs
Catch-all interrupt vector
If an unexpected interrupt occurs (interrupt is enabled and no handler
is installed, which usually indicates a bug), then the default action
is to reset the device by jumping to the reset vector. You can
override this by supplying a function named BADISR_vect which should
be defined with ISR() as such. (The name BADISR_vect is actually an
alias for __vector_default. The latter must be used inside assembly
code in case is not included.)
I ran in the same situation right now, but since I don't have a high reputation on stackoverflow, I can not vote.
here is a snippet of my initialization procedure that caused this problem to me:
void USART_Init()
{
cli();
/* Set baud rate */
UBRR0H = (uint8_t)(BAUD_PRESCALE>>8);
UBRR0L = (uint8_t)BAUD_PRESCALE;
/* Enable receiver and transmitter */
UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
/* Set frame format: 8data, 1stop bit 8N1 => 86uS for a byte*/
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
/*enable Rx and Tx Interrupts*/
UCSR0B |= (1 << RXCIE0) | (1 << TXCIE0); //<- this was the problem
/*initialize the RingBuffer*/
RingBuffer_Init(&RxBuffer);
sei();
}
The problem was that I initially used interrupt based transmission, but later on I have changed the design and went for 10ms polling for Tx sequence, and forgotten to change this line as well in the init procedure.
Thanks very much for pointing this out Peter Gibson.
first code:
//------------------------------------------------------------------------------
/// Interrupt handlers for TC interrupts. Toggles the state of LEDs
//------------------------------------------------------------------------------
char token = 0;
void TC0_IrqHandler(void) {
volatile unsigned int dummy;
dummy = AT91C_BASE_TC0->TC_SR;
if(token == 1) {
PIO_Clear(&leds[0]);
PIO_Set(&leds[1]);
token = 0;
}
else {
PIO_Set(&leds[0]);
PIO_Clear(&leds[1]);
token = 1;
}
}
//------------------------------------------------------------------------------
/// Configure Timer Counter 0 to generate an interrupt every 250ms.
//------------------------------------------------------------------------------
void ConfigureTc(void) {
unsigned int div;
unsigned int tcclks;
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TC0; // Enable peripheral clock
TC_FindMckDivisor(1, BOARD_MCK, &div, &tcclks); // Configure TC for a 4Hz frequency and trigger on RC compare
TC_Configure(AT91C_BASE_TC0, tcclks | AT91C_TC_CPCTRG);
AT91C_BASE_TC0->TC_RC = (BOARD_MCK / div) / 1; // timerFreq / desiredFreq
IRQ_ConfigureIT(AT91C_ID_TC0, 0, TC0_IrqHandler); // Configure and enable interrupt on RC compare
AT91C_BASE_TC0->TC_IER = AT91C_TC_CPCS;
IRQ_EnableIT(AT91C_ID_TC0);
printf(" -- timer has started \n\r");
TC_Start(AT91C_BASE_TC0);
}
it's just interrupt timer and it's event (handler) but when I run some
while(1) {
// action
after ConfigureTc() it both cycle and interrupt timer are freezes... Why could that be? Should I add another timer and avoid while(1) ?
while(1) {
printf("hello");
}
-- this breaks (freeze) loops (yes, if I don't use timer it works as it must)
I'll venture an actual answer here. IME, 99% of the time my boards 'go out' with no response on any input and no 'heartbeat' LED-flash from the low-priority 'blinky' thread, the CPU has flown off to a prefetch or data abort handler. These handlers are entered by interrupt and most library-defined default handlers do not re-enable interrupts, so stuffing the entire system. Often, they're just endless loops and, with interrupts disabled, that's the end of the story:(
I have changed my default handlers to output suitable 'CRITICAL ERROR' messages to the UART, (by polling it - the OS/interrupts are stuft!).