I have an ATtiny1626 code that I'm in the early stages of just getting the modules going on my custom board for my client.
I had some issues with my interrupts being called indefinitely over and over. It did get it resolved for the ADC interrupts by manually clearing the flag inside the ISR. But for my SPI it just doesn't seem to work.
I have searched for ny solution or remotely similar cases i found did not help :/
As you can se in my code, I've only sent one byte of data thru the SPI in the SPI_init function. I'll include all my code just in case anyone asks for it, You can ignore ADC & USART sections:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BAUDR (F_CPU * 64)/(16UL * 9600)
int i = 0;
int j = 0;
void __delay_ms(int n) // for variable as a delay amount...
{
while (n--)
{
_delay_ms(1);
}
}
void ADC_init()
{
PORTA.DIR |= 0x04; // make AIN2(POT) Input for ADC
ADC0.MUXPOS = 0x02; // ADC is connected to AIN2
_delay_us(250);
ADC0.CTRLA = 0x01; // ADC enabled
ADC0.CTRLB = 0x0F; // Prescalar = /64
// VREF = VDD (CTRLC = 0x00)
//ADC0.INTCTRL = 0x01; // Result Ready Interrupt enabled
ADC0.CTRLE = 10;
//ADC0.CTRLF = 0x20;
ADC0.COMMAND = 0x01; // Immediate Sample Start 8-bit
}
void SPI_init()
{
PORTA.DIRSET = 0x02;
PORTC.DIRSET = 0x0D;
_delay_us(10);
SPI0.CTRLA = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_PRESC_DIV128_gc; // SPI ON MAster Prescalar/128
//SPI0.CTRLB = 0x04;
SPI0.INTCTRL = SPI_IE_bm; // Interrupt enabled;
_delay_us(10);
SPI0.DATA = 0xCC;
}
void UART_init()
{
// Set the baud rate in the BAUD register
USART0.BAUD = BAUDR;
USART0.CTRLB |= USART_TXEN_bm | USART_RXEN_bm;
USART0.CTRLC = USART_CHSIZE_8BIT_gc | USART_PMODE_DISABLED_gc | USART_SBMODE_1BIT_gc;
}
void Putc(char c)
{
while(!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = c;
//_delay_ms(1);
}
void Puts(char *s)
{
for(int a = 0; s[a] != '\0'; a++)
{
Putc(s[a]);
//_delay_ms(1);
}
}
void shift_out(uint8_t bit) {
if (bit) {
PORTC.OUTSET = 0x04; // Set SER high
} else {
PORTC.OUTCLR = 0x04; // Set SER low
}
PORTC.OUTSET = 0x01; // Set SRCLK high
_delay_us(1); // Short delay to ensure timing
PORTC.OUTCLR = 0x01; // Set SRCLK low
_delay_us(1);
//_delay_ms(125);
}
int main()
{
sei();
// Output enable DISP_EN and DISP_DP
PORTB.DIR = 0x26;
PORTB.OUT = 0x22;
//_delay_ms(2000);
ADC_init();
_delay_ms(10);
SPI_init();
UART_init();
//_delay_ms(500);
/*
shift_out(1);
shift_out(0);
shift_out(0);
shift_out(0);
shift_out(0);
shift_out(0);
shift_out(1);
shift_out(0);
PORTA.OUTCLR = 0x02;
_delay_us(1);
PORTA.OUTSET = 0x02;
_delay_us(1);
*/
while (1)
{
_delay_ms(500);
//PORTB.OUTTGL = 0x20;
}
return 0;
}
// Interrupts had an infinte loop isisue so flags had to be cleared manually...
ISR(ADC0_RESRDY_vect)
{
//ADC0.INTFLAGS |= ADC_RESRDY_bm;
}
ISR(SPI0_INT_vect)
{
// this bit and the variable i are only for the temporary purpose of checking wether the interrupt is called on repeat or not
i++;
if(i >= 10)
{
PORTB.OUTCLR = 0x20;
}
// 74HC595 Latch Pulse
PORTA.OUTSET = 0x02;
_delay_us(3);
PORTA.OUTCLR = 0x02;
//uint8_t d = SPI0.DATA;
SPI0.INTFLAGS = SPI_IF_bm;
}
I can tell the interrupts is being called over and over as in the SPI ISR, the counter variable i exceeds 10000!!!! I am very confused and im not sure what to do. On top of this, The SPI is connected to shift register and 7seg, And no data seem to be sent either as latching the shift register in the ISR does nothing, and FYI, the hardware seems fine i used bit banging and it worked perfectly.
Furthermore the commented blink code in while loop in main also runs extremely slow when the interrupt of SPI is enabled, suggesting its being called over and over slowing down the delay_ms
Clearing the flags manually didn't really solve anything searching for solution just made me more certain that there is a "reason" that makes the interrupt persistently being called over and over
but the "reason" is where i don't know what is happening :/
*** EDIT: Ive updated the code a bit, but still no success, And on top of this, SPI sent data(if even anything is sent) doesn't seem to change anything on the Shift Reigster i have connected to my 7segment Any thoughts on tht?(and again, the hardware is fine as bit banging works just fine)
There are a whole lot of problems with this code... here are some that may be relevant for the specific problem.
The normal way to write any ISR on most MCUs including AVR, is to always clear the interrupt source flags that caused the interrupt from within the ISR. You seem surprised by this, but it's how every ISR is properly written.
On any MCU where writing a 1 clears the interrupt flag, then |=
is the wrong operator to use because it results in a read-modify-write. That is, all status flags will get read and any status flag which happened to have a 1 set will get cleared, in addition to the SPI_IF_bm
OR:ed in. Correct code for preserving other flags would (counter-intuitively) be SPI0.INTFLAGS = SPI_IF_bm;
. This works without destroying any other flag because writing a zero has no effect.
On many MCUs, SPI flags specifically are often cleared in a peculiar way, namely by first reading the status/flag register when the flag is set, followed by an immediate read of the data register. Comments suggest that this may be the case here. If so you should do something like:
uint8_t dummy = SPI0.INTFLAGS;
uint8_t ch = SPI0.DATA;
Note that on MCUs having this peculiar way of clearing SPI flags, there's a notoriously obscure problem when you have a debugger open and view either the specific register or a raw memory map dump. The debugger would then send read commands to the MCU and by doing so destroying the flags. You can tell if you are having this problem in case single-stepping in the debugger doesn't work, but leaving the code free-running without debugger works.
Using busy-wait delays in your program is a strong indication of poor program design - in professional code bases such things are never used. Instead hardware peripheral timers are used, even if they only set one flag which is then polled.
As for AVR skunkware libs specifically, they seem deeply problematic for a lot of other reasons. This particular function takes a floating point(!!!) parameter, it will not work unless optimizations are enabled and it does not take the effects of interrupts in account. There is just no way you should be using this function anywhere in a 8 bit MCU, let alone in an ISR! If you look at the implementation here this was clearly written for some PC with a FPU, by PC people. On a tiny AVR with 2k RAM and no FPU, this will kill your available memory, execution time, everything. For something as mundane as a delay, which can be trivially implemented with an on-hardware timer peripheral using some 10 bytes of RAM and flash.
Variables shared between an ISR and the background program must be declared volatile
to prevent incorrect optimizations. Additionally, you must also protect such a variable from race condition bugs. Otherwise you can easily end up in a situation like 1) background program reads half of i
2) Boom! Interrupt. i
gets overwritten 3) background program reads the other half of i
. 4) i
is now corrupt in a very subtle way.
Protection against race condition bugs usually involves temporarily disabling the relevant interrupt(s) like SPI in this case, read the variable, then re-enable the interrupt. But in this case you could get rid of a lot of problems by instead using a local variable i
where required.
i
etc were declared int
but you have no protection against integer overflow. This should at the very least be declared unsigned
, because unsigned
variables safely wrap-around to zero. In embedded systems, the general rule is to never use the default types of C but instead only use the types from stdint.h
. And to never use signed types unless you actually need negative values, which is somewhat rare.