📄 spi.c
字号:
/*********************************************************************************************************
** SPI 驱动程序
** (c) Copyright 2006-2008, limaokui
** All Rights Reserved
**
** V1.0.0
**
**
**--------------文件信息--------------------------------------------------------------------------------
**文 件 名:SPI.c
**创 建 人: 李茂奎
**最后修改日期: 2006年9月2日
**描 述: SPI驱动程序
**
**--------------历史版本信息----------------------------------------------------------------------------
** 创建人: 李茂奎
** 版 本: V1.00
** 日 期: 2006年9月2日
** 描 述: 原始版本
**
**------------------------------------------------------------------------------------------------------
** 修改人: 李茂奎
** 版 本:
** 日 期:
** 描 述:
**
**--------------当前版本修订------------------------------------------------------------------------------
** 修改人: 李茂奎
** 日 期: 2006年9月2日
** 描 述:
**
**------------------------------------------------------------------------------------------------------
*********************************************************************************************************/
/*
ATmega128的SPI采用硬件方式实现面向字节的全双工3线同步通信,
支持主机、从机和2种不同极性的SPI时序,通信速率有7种选择,
主机方式的最高速率为1/2系统时钟,从机方式最高速率为1/4系统时钟。
AVR的SPI由一个16位的循环移位寄存器构成,当数据从主机方移出时,从机的数据同时也被移入,
因此SPI的发送和接收在一个中断服务中完成。
在SPI中断服务程序中,先从SPDR中读一个接收的字节存入接收数据缓冲器中,再从发送数据缓冲
器取出一个字节写入SPDR中,由SPI发送到从机。
数据一旦写入SPDR,ISP硬件开始发送数据。
下一次ISP中断时,表示发送完成,并同时收到一个数据。
SPI为硬件接口和传输完成中断申请,所以使用SPI传输数据的有效方法是采用中断方式+数据缓存器的设计方法。
在对SPI初始化时,应注意以下几点:
.正确选择和设置主机或从机,以及工作模式(极性),数据传输率;
.注意传送字节的顺序,是低位优先(LSB First)还是高位优先(MSB Frist);
.正确设置MOSI和MISO接口的输入输出方向,输入引脚使用上拉电阻,可以节省总线上的吊高电阻。
*/
#include "config.h"
static uint8 Spi_Receive_Data[SPI_RECEIVE_DATA_BUFFER_SIZE];
static uint8 Spi_Send_Data[SPI_SEND_DATA_BUFFER_SIZE];
CirQueue Spi_Receive_Buffer={0,0,0,Spi_Receive_Data,SPI_RECEIVE_DATA_BUFFER_SIZE,0,0,0};
CirQueue Spi_Send_Buffer={0,0,0,Spi_Send_Data,SPI_SEND_DATA_BUFFER_SIZE,0,0,0};
#pragma interrupt_handler spi_stc_isr:iv_SPI_STC
void spi_stc_isr(void)
{
if(Spi_Receive_Buffer.status==SPI_DATA_RECEIVE)
EnQueue(&Spi_Receive_Buffer,SPDR); //从SPI口读出收到的字节
if (Spi_Send_Buffer.status==SPI_DATA_SEND)
{
if(!EmptyQueue(&Spi_Send_Buffer))//如果发送缓冲区中有待发的数据
{
SPDR = DeQueue(&Spi_Send_Buffer); //发送一个字节数据,并调整指针
}
else
{
Spi_Send_Buffer.status=SPI_DATA_FREE;
}
}
}
uint8 Spi_Get_Char(void)
{
uint8 data;
while (EmptyQueue(&Spi_Receive_Buffer)); //无接收数据,等待
data = DeQueue(&Spi_Receive_Buffer); //从接收缓冲区取出一个SPI收到的数据
return data;
}
void Spi_Put_Char(uint8 c)
{
while (FullQueue(&Spi_Send_Buffer));//发送缓冲区满,等待
if (Spi_Send_Buffer.count)//发送缓冲区已中有待发数据
{ //或SPI正在发送数据时
EnQueue(&Spi_Send_Buffer,c);//将数据放入发送缓冲区排队
}
else
SPDR = c; //发送缓冲区中空且SPI口空闲,直接放入SPDR由SIP口发送
}
uint8 Spi_Receive(uint8 *buffer,uint16 length)
{
uint16 i;
Spi_Receive_Buffer.status=SPI_DATA_RECEIVE;
for(i=0;i<length;i++)
{
*buffer++=Spi_Get_Char();
}
Spi_Receive_Buffer.status=SPI_DATA_FREE;
return i;
}
uint8 Spi_Send(uint8 *buffer,uint16 length)
{
uint16 i;
Spi_Send_Buffer.status=SPI_DATA_SEND;
for(i=0;i<length;i++)
{
Spi_Put_Char(*buffer++);
}
Spi_Send_Buffer.status=SPI_DATA_FREE;
return i;
}
uint8 Spi_SendReceive(uint8 *buffer,uint16 sendlength,uint16 receivelength)
{
uint16 i;
uint8 *ptr;
ptr=buffer;
if (receivelength!=0)
{
Spi_Receive_Buffer.status=SPI_DATA_RECEIVE;
}
Spi_Send_Buffer.status=SPI_DATA_SEND;
for(i=0;i<sendlength;i++)
{
Spi_Put_Char(*ptr++);
}
ptr=buffer;
for(i=0;(i<receivelength)&&(i<sendlength);i++)
{
*ptr++=Spi_Get_Char();
}
Spi_Send_Buffer.status=SPI_DATA_FREE;
Spi_Receive_Buffer.status=SPI_DATA_FREE;
return i;
}
void Spi_Init(void)
{
uint8 temp;
#if (CPU_TYPE == M32)|| (CPU_TYPE == M16) // MISO-PB6,MOSI-PB5,SCK-PB7,SS-PB4
#define DDR_SPI DDRB
#define PORT_SPI PORTB
#define DD_MOSI 5
#define DD_MISO 6
#define DD_SCK 7
#define DD_SS 4
#endif
#if CPU_TYPE == M8 // MISO-PB6,MOSI-PB5,SCK-PB7,SS-PB4
#define DDR_SPI DDRB
#define PORT_SPI PORTB
#define DD_MOSI 3
#define DD_MISO 4
#define DD_SCK 5
#define DD_SS 2
#endif
#if (CPU_TYPE == M128)||(CPU_TYPE == M64 ) // MISO-PB6,MOSI-PB5,SCK-PB7,SS-PB4
#define DDR_SPI DDRB
#define PORT_SPI PORTB
#define DD_MOSI 2
#define DD_MISO 3
#define DD_SCK 1
#define DD_SS 0
#endif
DDR_SPI = DDR_SPI|(1<<DD_SS)|(1<<DD_MOSI)|(1<<DD_SCK); //MISO=input and MOSI,SCK,SS = output
PORTB |=(1<<DD_MISO)|(1<<DD_SS); //MISO上拉电阻有效
SPCR = (1<<SPIE)|(1<<SPE)|(1<<MSTR); //SPI允许,4X 主机模式,MSB先发,SPI模式0 (CPOL CPHA 00(0) 01(1) 10(2) 11(3))
// SPSR = 0;
SPSR = (1<<SPI2X); //enable spi2x
temp = SPSR;
temp = SPDR; //清空SPI,和中断标志,使SPI空闲
}
/*
void main(void)
{
uint8 I;
CLI(); //关中断
spi_init(); //初始化SPI接口
SEI(); //开中断
while()
{
putSPIchat(i); //发送一个字节
i++;
Spi_Get_Char(); //接收一个字节(第一个字节为空字节)
}
}
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -