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

📄 spi.c

📁 AVR控制SD卡或MMC卡的程序 调试通过
💻 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 + -