⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 9991.txt

📁 使用的同步串行三线SPI 接口
💻 TXT
字号:
串行接口SPI 接口应用设计
作者:马潮老师 / 整理:armok / 2005-01-17/ www.OurAVR.com
使用的同步串行三线SPI 接口,可以方便的连接采用SPI 通信协议的外围或另一片AVR 单片机,实现在
短距离内的高速同步通信。ATmega128 的SPI 采用硬件方式实现面向字节的全双工3 线同步通信,支持主
机、从机和2 种不同极性的SPI 时序,通信速率有7 种选择,主机方式的最高速率为1/2 系统时钟,从机方式
最高速率为1/4 系统时钟。
ATmega128 单片机内部的SPI 接口也被用于程序存储器和数据E2PROM 的编程下载和上传。但特别需要
注意的是,此时SPI 的MOSI 和MISO 接口不再对应PB2、PB3 引脚,而是转换到PE0、PE1 引脚上(PDI、
PDO),其详见第二章中关于程序存储器的串行编程和校验部分的内容。
ATmega128 的SPI 为硬件接口和传输完成中断申请,所以使用SPI 传输数据的有效方法是采用中断方式+
数据缓存器的设计方法。在对SPI 初始化时,应注意以下几点:
.正确选择和设置主机或从机,以及工作模式(极性),数据传输率;
.注意传送字节的顺序,是低位优先(LSB First)还是高位优先(MSB Frist);
.正确设置MOSI 和MISO 接口的输入输出方向,输入引脚使用上拉电阻,可以节省总线上的吊高电阻。
下面一段是SPI 主机方式连续发送(接收)字节的例程:
#define SIZE 100
unsigned char SPI_rx_buff[SIZE];
unsigned char SPI_tx_buff[SIZE];
unsigned char rx_wr_index,rx_rd_index,rx_counter,rx_buffer_overflow;
unsigned char tx_wr_index,tx_rd_index,tx_counter;
#pragma interrupt_handler spi_stc_isr:18
void spi_stc_isr(void)
{
SPI_rx_buff[rx_wr_index] = SPDR; //从ISP 口读出收到的字节
if (++rx_wr_index == SIZE) rx_wr_index = 0; //放入接收缓冲区,并调整队列指针
if (++rx_counter == SIZE)
{
rx_counter = 0;
rx_buffer_overflow = 1;
}
if (tx_counter) //如果发送缓冲区中有待发的数据
{
--tx_counter;
SPDR = SPI_tx_buff[tx_rd_index]; //发送一个字节数据,并调整指针
if (++tx_rd_index == SIZE) tx_rd_index = 0;
}
}
unsigned char getSPIchar(void)
{
unsigned char data;
while (rx_counter == 0); //无接收数据,等待
data = SPI_rx_buff[rx_rd_index]; //从接收缓冲区取出一个SPI 收到的数据
if (++rx_rd_index == SIZE) rx_rd_index = 0; //调整指针
CLI();
--rx_counter;
SEI();
return data;
}
void putSPIchar(char c)
{
while (tx_counter == SIZE);//发送缓冲区满,等待
CLI();
if (tx_counter || ((SPSR & 0x80) == 0))//发送缓冲区已中有待发数据
{ //或SPI 正在发送数据时
SPI_tx_buffer[tx_wr_index] = c; //将数据放入发送缓冲区排队
if (++tx_wr_index == SIZE) tx_wr_index = 0; //调整指针
++tx_counter;
}
else
SPDR = c; //发送缓冲区中空且SPI 口空闲,直接放入SPDR 由SIP 口发送
SEI();
}
void spi_init(void)
{
unsigned chat temp;
DDRB |= 0x080; //MISO=input and MOSI,SCK,SS = output
PORTB |= 0x80; //MISO 上拉电阻有效
SPCR = 0xD5; //SPI 允许,主机模式,MSB,允许SPI 中断,极性方式01,1/16 系统时钟速率
SPSR = 0x00;
temp = SPSR;
temp = SPDR; //清空SPI,和中断标志,使SPI 空闲
}
void main(void)
{
unsigned char I;
CLI(); //关中断
spi_init(); //初始化SPI 接口
SEI(); //开中断
while()
{
putSPIchat(i); //发送一个字节
i++;
getSPIchar(); //接收一个字节(第一个字节为空字节)
………
}
}
这个典型的SPI 例程比较简单,主程序中首先对ATmega128 的硬件SPI 进行初始化。在初始化过程中,
将PORTB 的MOSI、SCLK 和SS 引脚作为输出,同时将MISO 作为输入引脚,并打开上拉电阻。接着对SPI
的寄存器进行初始化设置,并空读一次SPSR、SPDR 寄存器(读SPSR 后再对SPDR 操作将自动清零SPI 中断
标志自动清零),使ISP 空闲等待发送数据。
AVR 的SPI 由一个16 位的循环移位寄存器构成,当数据从主机方移出时,从机的数据同时也被移入,因
此SPI 的发送和接收在一个中断服务中完成。在SPI 中断服务程序中,先从SPDR 中读一个接收的字节存入接
收数据缓冲器中,再从发送数据缓冲器取出一个字节写入SPDR 中,由ISP 发送到从机。数据一旦写入
SPDR,ISP 硬件开始发送数据。下一次ISP 中断时,表示发送完成,并同时收到一个数据。类似本章介绍的
USART 接口的使用,程序中putSPIchar()和getSPIchar()为应用程序的底层接口函数(SPI 驱动程序是SPI 中断
服务程序),同时也使用了两个数据缓冲器,分别构成循环队列。这种程序设计的思路,不但程序的结构性完
整,同时也适当的解决了高速MCU 和低速串口之间的矛盾,实现程序中任务的并行运行,提高了MCU 的运
行效率。
本例程是通过SPI 批量输出、输入数据的示例,用户可以使用一片ATmega128,将其MOSI 和MISO 两
个引脚连接起来,构成一个ISP 接口自发自收的系统,对程序进行演示验证。需要注意,实际接收到的字节为
上一次中断时发出的数据,即第一个收到的字节是空字节。
读懂和了解程序的处理思想,读者可以根据需要对程序进行改动,适合实际系统的使用。如在实际应用中
外接的从机是一片SPI 接口的温度芯片,协议规程为:主机先要连续发送3 个字节的命令,然后从机才返回一
个字节的数据。那么用户程序可以先循环调用putSPIchar()函数4 次,将3 个字节的命令和一个字节的空数据
发送到从机,然后等待一段时间,或处理一些其它的操作后,再循环调用getSPIchar()函数4 次,从接收数据
缓冲器中连续读取4 个字节,放弃前3 个空字节,第4 个字节即为从机的返回数据了。
原文出处: OurAVR.com 技术论坛,欢迎参加讨论

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -