《AVR单片机嵌入式系统原理与应用实践》P444页 SPI程序有问题
今天我用proteus仿真测试《AVR单片机嵌入式系统原理与应用实践》中P443-P444页SPI的程序,但在仿真的时候SPI串口一直没有发出数据,我测试的完整程序如下#include <mega16.h>
#define SIZE 100
unsigned char SPI_rx_buff;
unsigned char SPI_tx_buff;
unsigned char rx_wr_index,rx_rd_index,rx_counter,rx_buffer_overflow;
unsigned char tx_wr_index,tx_rd_index,tx_counter = 0;
interrupt void spi_isr(void)
{
SPI_rx_buff = SPDR;
if(tx_counter)
{
SPDR = SPI_tx_buff;
--tx_counter;
if(++ tx_rd_index == SIZE) tx_rd_index = 0;
}
if(++ rx_wr_index == SIZE) rx_wr_index = 0;
if(++ rx_counter == SIZE){
rx_counter = 0;
rx_buffer_overflow = 1;
}
}
unsigned char getSPIchar(void)
{
unsigned char data;
while(rx_counter == 0);
data = SPI_rx_buff;
if(++ rx_rd_index == SIZE) rx_rd_index = 0;
#asm("cli")
-- rx_counter;
#asm("sei")
return data;
}
void putSPIchar(unsigned char c)
{
while(tx_counter == SIZE);
#asm("cli")
if(tx_counter || (SPSR & 0x80) == 0){
SPI_tx_buff = c;
if(++ tx_wr_index == SIZE) tx_wr_index = 0;
++ tx_counter;
}
else
SPDR = c;
#asm("sei")
}
void spi_init(void)
{
char temp;
DDRB |= 0xb0;
PORTB |= 0x40;
SPCR = 0xf5;
SPSR = 0x00;
temp = SPSR;
temp = SPDR;//SPIF清零((SPSR & 0x80) == 0为真)
}
unsigned char buffer;
void main(void)
{
unsigned char i = 0;
#asm("cli")
spi_init();
#asm("sei")
for(i = 1;i < 8;i ++){
putSPIchar(i);
}
while(1);
}
后来我仔细琢磨了一下程序发现putSPIchar有问题,问题就在if(tx_counter || (SPSR & 0x80) == 0),在初始化spi_init()中, 已经把SPSR中中断标志SPIF清零了(即(SPSR & 0x80) == 0为真)。当使用putSPIchar函数发送数据时,if语句的条件始终成立,此时数据只会存入缓冲区,程序执行不到else语句,数据始终没有放入SPDR中,所以数据一直都不能发送出去。我把程序稍微改动了一下:
void putSPIchar(unsigned char c)
{
while(tx_counter == SIZE);
#asm("cli")
if(tx_counter || (SPSR & 0x80) == 0){
SPI_tx_buff = c;
if(++ tx_wr_index == SIZE) tx_wr_index = 0;
++ tx_counter;
if(tx_counter == 1) // 存入一个数据后立即跳到else,将数据C送入SPDR中启动SPI发数据。
goto flag;
}
else{
flag:
SPDR = c;
}
程序修改后程序进行Protues仿真的时候就没问题了,只是在连续发送多个字节或发送单个字节时,第一个数据重复发送了一次。但这个程序还不够完善希望大家能提出更好的解决办法。 在第1次印刷出版的书中,此段代码是有问题的。
在第2次印刷出版的书中已经做了修改。
感谢LZ购买本书,你手中可能是第1次印刷版的,在本栏的置顶帖中,有对第1次印刷版的修改表,共有151处各种错误。这些错误在第2次印刷出版的书中全部做了修改。
下面是这段代码的修改版,请LZ测试评估一下。
5。***P443-P444,原代码里面有几处笔误,以及一个BUG,请改成如下:
#define SIZE 100
unsigned char SPI_rx_buff;
unsigned char SPI_tx_buff;
unsigned char rx_wr_index,rx_rd_index,rx_counter,rx_buffer_overflow;
unsigned char tx_wr_index,tx_rd_index,tx_counter;
unsigned char SPI_free;
interrupt void spi_isr(void) // SPI 完成中断服务
{
SPI_rx_buff = SPDR; // 从SPI口读出收到的字节放入接收缓冲区
if (tx_counter) // 如果发送缓冲区中有待发的数据
{
SPDR = SPI_tx_buff; // 发送1字节数据,
--tx_counter; // 待发送数据个数减1
if (++tx_rd_index == SIZE) tx_rd_index = 0; // 调整发送缓冲区队列指针
}
else SPI_free = 1; // 无待发送数据,置SPI空闲
if (++rx_wr_index == SIZE) rx_wr_index = 0; // 调整接收缓冲区队列指针
if (++rx_counter == SIZE)
{
rx_counter = 0;
rx_buffer_overflow = 1; // 接收数据溢出
}
}
unsigned char getSPIchar(void)
{
unsigned char data;
while (rx_counter == 0); // 无接收数据,等待(死循环!)
data = SPI_rx_buff; // 从接收缓冲区取出一个SPI收到的数据
if (++rx_rd_index == SIZE) rx_rd_index = 0; // 调整指针
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
void putSPIchar(unsigned char c)
{
while (tx_counter == SIZE); // 发送缓冲区满,等待
#asm("cli")
if (SPI_free)
{
SPDR = c; // SPI口空闲,直接放入SPDR由SPI口发送
SPI_free = 0; // 置SPI忙
}
else
{
SPI_tx_buffer = c; // 将数据放入发送缓冲区排队
if (++tx_wr_index == SIZE) tx_wr_index = 0; // 调整指针
++tx_counter;
}
#asm("sei")
}
void spi_init(void)
{
unsigned char temp;
DDRB |= 0xB0; // MISO为输入方式,MOSI,SCK和~SS为输出方式
PORTB |= 0x40; // MISO上拉电阻有效
SPCR = 0xD5; // SPI允许,主机模式,MSB方式,允许SPI中断,极性方式01,1/16系统时钟频率
SPSR = 0x00;
temp = SPSR;
temp = SPDR; // 清除SPI中断标志位,使SPI空闲
SPI_free = 1; // 置SPI空闲
}
void main(void)
{
unsigned char i;
#asm("cli") // 关中断
spi_init(); // 初始化SPI接口
#asm("sei") // 使能中断
while()
{
putSPIchar(i); // 通过SPI发送1字节
i++;
getSPIchar(); // 读取SPI接收的字节
………
}
} 记号,谢谢马老师!! 增加了一个空闲标志,这样好多啦,我要认真琢磨琢磨。
谢谢马老师!!! 一头雾水 mark,我看了下,我买的书也是第一版第一次印刷的。 nsigned char getSPIchar(void)
{
unsigned char data;
while (rx_counter == 0); // 无接收数据,等待(死循环!)
data = SPI_rx_buff; // 从接收缓冲区取出一个SPI收到的数据
if (++rx_rd_index == SIZE) rx_rd_index = 0; // 调整指针
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#asm("cli") 各位看看 我认为将这句放在while 之后是否好一些期待讨论 to 6楼:
这个要看SPI的中断是如何写的。
用#asm("cli")关中断,是防止中断中也修改敏感变量造成的错误。
而变量rx_rd_index在SPI中断中并没有涉及(在其它中断中通常也不会涉及到的),只有rx_counter在SPI中断中涉及到,所以关闭中断放在调整rx_counter之前。
放在WHILE后也是可以的,但没有我书上的例子好。因为提前关闭全局中断,可能会影响其它中断的及时响应。在一个实际的系统中,可能不只是使用SPI中断,也会使用其它的中断,比如定时器中断,而且需要及时的响应(有更高的优先级)。如果过早的关闭全局中断,会影响其它中断的及时处理的。
在许多的细节问题上,我的教程中都考虑的比较周全。这本冠以“国家级规划教材”的教科书,我可以毫不客气的说,决不是虚有其名。
页:
[1]