Atmega328p SPI在未指定的期间后冻结
我正在研究一个项目,在该项目中,我读了一个MCP3008 ADC和MCP4901 DAC。两者都使用SPI与Arduino通信。
几秒钟内,一切都很好,我得到了从Arduino到DAC的所有值,从DAC到ADC,然后再返回到Arduino。
但是,在未指定的时间之后,该程序在段循环中冻结(可能会陷入无限循环时)。该程序中的计时器仍在中断,但主要循环被冻结。
调试时,我发现SPI收发器函数是问题所在。一段时间后,它停在那里。这是功能:
/* spi data buffer send */
uint8_t spi_tranceiver (uint8_t data)
{
// Load data into the buffer
SPDR = data;
// Wait until transmission complete
while(!(SPSR & (1<<SPIF)));
// Return received data
return(SPDR);
}
我的猜测是它被卡在循环中,等待传输完成。但是我无法确定。 输出应该是这样的,但是在一段时间后冻结:
这是整个程序:
/*
* Created: 6-4-2022 13:15:10
* Author : meule
*/
#define F_CPU 16000000
#define BAUD_RATE 9600
#define UBBRN (F_CPU/16/BAUD_RATE)-1
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ARDUINO definitions
#define MISO PORTB4
#define MOSI PORTB3 //SDI
#define SCK PORTB5
#define SS PORTB2
#define SS_2 PORTB1
#define NUMSTEPS 200
//Global variables :/
char sine[NUMSTEPS];
int index = 0;
void init_SPI()
{
// Set SS, SS_2, MOSI and SCK output, all others input
DDRB = (1<<SS)|(1<<SS_2)|(1<<MOSI)|(1<<SCK);
//Set the slave select pin (Active low)
PORTB |= (1 << SS);
PORTB |= (1 << SS_2);
// Enable SPI, Master, set clock rate fosc/16
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}
// Initialize the USART
void init_usart(){
// Set baud registers
UBRR0H = (unsigned char)(UBBRN>>8);
UBRR0L = (unsigned char)UBBRN;
// Enable transmitter
UCSR0B = (1<<TXEN0) | (1<<RXEN0);
// Data format of 8-bits
UCSR0C = (1 <<UCSZ01) | (1 <<UCSZ00);
}
void init_timer(){
TIMSK0 = 0b00000010; //Set compare mode A
TCCR0A = 0b00000010; //Set CTC mode
TCCR0B = 0b00000101; //Set prescaler to 1024
OCR0A = 255;
}
void init_Sin(int amplitude, int dc_offset, int num_steps, char *sine){
//Calculate Sine wave for the DAC to output
for(int i = 0; i < num_steps; i++){
sine[i] = dc_offset + amplitude * sin((i*360)/num_steps);
}
}
/* spi data buffer send */
uint8_t spi_tranceiver (uint8_t data)
{
// Load data into the buffer
SPDR = data;
// Wait until transmission complete
while(!(SPSR & (1<<SPIF)));
// Return received data
return(SPDR);
}
void writeDAC(uint8_t data){
PORTB &= ~(1 << SS_2);
//8 bits for initializing the DAC
char init = 0b00110000;
//Get the 4MSB from the data
char data1 = (data >> 4);
//Get the 4LSB from the data
char data2 = (data << 4);
//Combine init with data1
init |= data1;
//Send data to the DAC
spi_tranceiver(init);
spi_tranceiver(data2);
PORTB |= (1 << SS_2);
}
float ReadADC(char opcode, int vref, int resolution)
{
// Activate the ADC for reading
PORTB &= ~(1 << SS);
spi_tranceiver(0b00000001);
//Get the first 8 bits from the ADC
uint8_t analogH = spi_tranceiver(opcode);
//Mask the bits since I only need the 2LSB
analogH = (analogH & 0b00000011);
//Get the second 8 bits from the ADC
uint8_t analogL = spi_tranceiver(0);
//Convert the ADC values into a 16 bit value
uint16_t total = (analogH << 8) + analogL;
//Convert the value with the vref and resolution to a float
float result = ((total*vref)/(float)resolution);
PORTB |= (1 << SS);
return result;
}
int main(void)
{
init_SPI();
init_usart();
init_timer();
init_Sin(100, 127, NUMSTEPS, sine);
// Enable the global interrupt
sei();
/* Replace with your application code */
while (1)
{
char data[6];
float val = ReadADC(0b10010000,5,1024);
dtostrf(val, 5, 3, data);
data[4] = 10;
data[5] = 0;
int i = 0;
while(data[i] != 0){
while (!(UCSR0A & (1<<UDRE0)));
UDR0 = data[i];
i++;
_delay_ms(5);
}
}
_delay_ms(5);
}
ISR(TIMER0_COMPA_vect){
if(index < NUMSTEPS){
writeDAC(sine[index]);
}
else{
index = 0;
writeDAC(sine[index]);
}
index++;
}
I am working on a project where I read out an MCP3008 ADC and an MCP4901 DAC. Both use SPI to communicate with the arduino.
For a few seconds everything is fine, I get all the values I need from the Arduino to the DAC, from the DAC to the ADC and then back again to the Arduino.
However, after an unspecified amount of time, the program freezes in the while loop (probably gets stuck in an infinite while loop). The timer in the program still continues with the interrupt, but the main loop is frozen.
When debugging I found out that the SPI transceiver function is the problem. After some time it stops there. This is the function:
/* spi data buffer send */
uint8_t spi_tranceiver (uint8_t data)
{
// Load data into the buffer
SPDR = data;
// Wait until transmission complete
while(!(SPSR & (1<<SPIF)));
// Return received data
return(SPDR);
}
My guess is it gets stuck inside the while loop waiting for the transmission to complete. But I cannot tell for certain.
The output should be like this, but it freezes after a while:
Anyone got a clue?
This is the whole program:
/*
* Created: 6-4-2022 13:15:10
* Author : meule
*/
#define F_CPU 16000000
#define BAUD_RATE 9600
#define UBBRN (F_CPU/16/BAUD_RATE)-1
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ARDUINO definitions
#define MISO PORTB4
#define MOSI PORTB3 //SDI
#define SCK PORTB5
#define SS PORTB2
#define SS_2 PORTB1
#define NUMSTEPS 200
//Global variables :/
char sine[NUMSTEPS];
int index = 0;
void init_SPI()
{
// Set SS, SS_2, MOSI and SCK output, all others input
DDRB = (1<<SS)|(1<<SS_2)|(1<<MOSI)|(1<<SCK);
//Set the slave select pin (Active low)
PORTB |= (1 << SS);
PORTB |= (1 << SS_2);
// Enable SPI, Master, set clock rate fosc/16
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}
// Initialize the USART
void init_usart(){
// Set baud registers
UBRR0H = (unsigned char)(UBBRN>>8);
UBRR0L = (unsigned char)UBBRN;
// Enable transmitter
UCSR0B = (1<<TXEN0) | (1<<RXEN0);
// Data format of 8-bits
UCSR0C = (1 <<UCSZ01) | (1 <<UCSZ00);
}
void init_timer(){
TIMSK0 = 0b00000010; //Set compare mode A
TCCR0A = 0b00000010; //Set CTC mode
TCCR0B = 0b00000101; //Set prescaler to 1024
OCR0A = 255;
}
void init_Sin(int amplitude, int dc_offset, int num_steps, char *sine){
//Calculate Sine wave for the DAC to output
for(int i = 0; i < num_steps; i++){
sine[i] = dc_offset + amplitude * sin((i*360)/num_steps);
}
}
/* spi data buffer send */
uint8_t spi_tranceiver (uint8_t data)
{
// Load data into the buffer
SPDR = data;
// Wait until transmission complete
while(!(SPSR & (1<<SPIF)));
// Return received data
return(SPDR);
}
void writeDAC(uint8_t data){
PORTB &= ~(1 << SS_2);
//8 bits for initializing the DAC
char init = 0b00110000;
//Get the 4MSB from the data
char data1 = (data >> 4);
//Get the 4LSB from the data
char data2 = (data << 4);
//Combine init with data1
init |= data1;
//Send data to the DAC
spi_tranceiver(init);
spi_tranceiver(data2);
PORTB |= (1 << SS_2);
}
float ReadADC(char opcode, int vref, int resolution)
{
// Activate the ADC for reading
PORTB &= ~(1 << SS);
spi_tranceiver(0b00000001);
//Get the first 8 bits from the ADC
uint8_t analogH = spi_tranceiver(opcode);
//Mask the bits since I only need the 2LSB
analogH = (analogH & 0b00000011);
//Get the second 8 bits from the ADC
uint8_t analogL = spi_tranceiver(0);
//Convert the ADC values into a 16 bit value
uint16_t total = (analogH << 8) + analogL;
//Convert the value with the vref and resolution to a float
float result = ((total*vref)/(float)resolution);
PORTB |= (1 << SS);
return result;
}
int main(void)
{
init_SPI();
init_usart();
init_timer();
init_Sin(100, 127, NUMSTEPS, sine);
// Enable the global interrupt
sei();
/* Replace with your application code */
while (1)
{
char data[6];
float val = ReadADC(0b10010000,5,1024);
dtostrf(val, 5, 3, data);
data[4] = 10;
data[5] = 0;
int i = 0;
while(data[i] != 0){
while (!(UCSR0A & (1<<UDRE0)));
UDR0 = data[i];
i++;
_delay_ms(5);
}
}
_delay_ms(5);
}
ISR(TIMER0_COMPA_vect){
if(index < NUMSTEPS){
writeDAC(sine[index]);
}
else{
index = 0;
writeDAC(sine[index]);
}
index++;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
函数
writedac
是从中断处理程序调用的。然后,它调用
spi_tranceiver
触发SPI传输(从而影响SPIF标志)的也从
main
调用此功能。想象一下这种情况:发生了一个字节传输
spdr = data;
-此时,计时器中断 ,它也称为spi_tranceiver,该spi_tranceiver覆盖传输缓冲区,等待传输到结束,清除SPIF(通过读取spdr)while(!(SPSR&amp;(1&lt;&lt; spif))));
- 此周期更新的结局,因为现在没有正在进行的传输和SPIF清除。您应该避免在并发线程(主和中断)中使用相同的外围设备。
有许多方法可以解决该问题:
spi_tranceiver
内的中断,并在退出时恢复我标记。main
中。例如,设置为timer0_compa_vect
中断(不要忘记也禁用Timsk中的中断标志)添加类似的内容:The function
writeDAC
is called from the interrupt handler.Then it calls
spi_tranceiver
which triggers SPI transmission (thus affecting SPIF flag)Also this function is called from the
main
. Imagine this situation:SPDR = data;
- this starts a byte transmissionwhile(!(SPSR & (1<<SPIF)));
- This cycle newer ends because now there is no ongoing transmission and SPIF is cleared.You should avoid to use the same peripherals in concurrent threads (main and the interrupt).
There are many approaches to fix that issue:
spi_tranceiver
and restore I flag on exit.main
. E.g. insted ofTIMER0_COMPA_vect
interrupt (don't forget also to disable the interrupt flag in TIMSK) add something like: