刚刚写的SPI驱动,想移植到LINUX上面用来读写SD卡
只测试了发送,没有测试接收.
spi.c
/*************************************************************************************************************
* 文件名: spi.c
* 功能: S3C6410 SPI底层驱动函数
* 作者: 陈鹏
* 创建时间: 2012年9月8日20:35
* 最后修改时间:2012年9月8日
* 详细: SPI始化,发送,接收,配置等
* 使用的是手动控制片选,因为在实际使用过程中手动控制片选较为灵活,但是也有个问题,就是何时取消片选,应为数据写入到发送FIFO并
* 不代表数据已经发送完成了,如果在数据没有发送完成之前取消了片选会导致数据传输错误,因此简单的方法就是在发送数据后加入一定
* 的延时,让数据发送完成后取消片选
*************************************************************************************************************/
#include "system.h"
#include "spi.h"
#include "delay.h"
//SPI通道数量
#define SPI_CH_N 2
//SPI外设结构
const SPI_TypeDef *SPI_CH[SPI_CH_N] = {SPI0,SPI1};
//默认模式1
//主设备模式,空闲时钟低电平,第一个时钟边沿有效(上升沿有效),使能发送接收,数据宽度8bit,关闭DMA,使能手动控制片选
const SPI_Config_TypeDef SPI_DEFAULT_01 = {0,0,0,1,1,8,0,0,0,0};
/*************************************************************************************************************************
*函数 : void SPI_SetSpeed(u8 ch,u8 Speed)
*功能 : 设置SPI速度
*参数 : CH:SPI通道选择;Speed:SPI速度设置
*返回 : 无
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 20121005
*最后修改时间: 20121005
*说明 : 修改的时候注意SPI应该处于空闲或者无效状态,使用的时钟是PCLK
* SPI 时钟输出 = 时钟源 / ( 2 ×(预分频值 +1))
*************************************************************************************************************************/
void SPI_SetSpeed(u8 ch,u8 Speed)
{
SPI_TypeDef *SPI;
if(ch >= SPI_CH_N)
return; //通道号超出范围
SPI = (SPI_TypeDef *)SPI_CH[ch]; //获取对应通道寄存器结构指针
SPI->CLKCFG = 0; //清空设置并关闭时钟
SPI->CLKCFG = Speed;//设置预分频值
SPI->CLKCFG |= BIT8;//使能时钟
}
/*************************************************************************************************************************
*函数 : u8 SPI_Init(u8 ch,SPI_Config_TypeDef *config,u8 Speed)
*功能 : SPI初始化
*参数 : CH:SPI通道选择;config:配置结构指针;Speed:SPI速度设置
*返回 : 0:初始化成功;1:初始化失败
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 20121005
*最后修改时间: 20121005
*说明 : 无
*************************************************************************************************************************/
u8 SPI_Init(u8 ch,const SPI_Config_TypeDef *config,u8 Speed)
{
u32 chcfg = 0;
u32 modcfg = 0;
u32 slavecfg = 0;
SPI_TypeDef *SPI;
if(ch >= SPI_CH_N)
return 1; //通道号超出范围
SPI = (SPI_TypeDef *)SPI_CH[ch]; //获取对应通道寄存器结构指针
SPI->CHCFG |= BIT5; //SPI软复位
Delay_US(10); //适当延时
switch(ch)
{
case 0: //通道0
{
Set_GateClk(PCLK_SPI0,ENABLE); //使能SPI0门控时钟
rGPCCON &= ~0xfff;
rGPCCON |= 0x222; //初始化SPI0 MISO CLK MOSI 相关IO
}break;
case 1: //通道1
{
Set_GateClk(PCLK_SPI1,ENABLE); //使能SPI1门控时钟
rGPCCON &= ~(0xfff << 16);
rGPCCON |= (0x222 << 16); //初始化SPI1 MISO CLK MOSI 相关IO
}break;
default:break;
}
SPI->CLKCFG &= ~BIT8; //关闭SPI时钟
SPI->CHCFG = 0; //清除设置并关闭SPI发送接收通道
if(config->EnSlave) //使能从设备模式
{
chcfg |= BIT4;
}
if(config->EnCPOH) //使能空闲时钟高电平
{
chcfg |= BIT3;
}
if(config->EnCPHB) //使能第二个时钟边沿有效
{
chcfg |= BIT2;
}
if(config->EnRx) //使能接收
{
chcfg |= BIT1;
}
if(config->EnTx) //使能发送
{
chcfg |= BIT0;
}
switch(config->SetTranSize)//设置传输数据位宽
{
case 16: modcfg |= (1 << 29);modcfg |= (1 << 17);break; //半字
case 32: modcfg |= (2 << 29);modcfg |= (2 << 17);break; //字
default : break; //字节
}
if(config->EnRxDMA) //使能接收DMA
{
modcfg |= BIT2;
}
if(config->EnTxDMA) //使能发送DMA
{
modcfg |= BIT1;
}
if(config->EnDMA4Burst) //设置DMA传输类型为4个脉冲
{
modcfg |= BIT0;
}
if(config->EnAutoCS) //使能自动片选
{
slavecfg |= BIT1;
switch(ch)
{
case 0: //通道0
{
rGPCCON &= ~0xf000;
rGPCCON |= 0x2000; //初始化CS 相关IO
}break;
case 1: //通道1
{
rGPCCON &= ~(0xf0000000);
rGPCCON |= (0x2000 << 16); //初始化CS 相关IO
}break;
default:break;
}
}
//写入配置数据
SPI->CHCFG = chcfg;
SPI->MODECFG = modcfg;
SPI->SLAVE = slavecfg;
SPI->CLKCFG |= BIT8; //使能SPI时钟
SPI_SetSpeed(ch,Speed); //设置SPI速度
SPIx_ReadWriteData(ch,0xaa); //启动第一次传输
return 0;
}
/*************************************************************************************************************************
*函数 : u32 SPIx_ReadWriteData(u8 ch,u32 TxData)
*功能 : SPI发送接收数据
*参数 : CH:SPI通道选择;TxData:要发送的数据
*返回 : 收到的数据
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 20121005
*最后修改时间: 20121005
*说明 : 发送和接收的数据宽度要看配置,可以是8bit,16bit,32bit
* 发送完成后要加延时,因为数据写入到FIFO后并没有马上发送完,当发送完成之前片选可能就已经取消了,因此需要适当的添加延时
* 因为我们无法判断数据是否已经从移位寄存器中发送完毕,只能检测FIFO
*************************************************************************************************************************/
u32 SPIx_ReadWriteData(u8 ch,u32 TxData)
{
u16 retry = 0;
SPI_TypeDef *SPI;
u8 temp;
if(ch >= SPI_CH_N)
return 1; //通道号超出范围
SPI = (SPI_TypeDef *)SPI_CH[ch]; //获取对应通道寄存器结构指针
do
{
temp = (SPI->STATUS >> 6) & 0x7f; //获取发送FIFO数据数量
retry ++;
if(retry > 8000)
return 0;
}
while(temp > 63); //发送FIFO满了,等待
SPI->TXDATA = TxData; //发送数据
retry = 0;
do
{
temp = (SPI->STATUS >> 13) & 0x7f; //获取接收FIFO数据数量
retry ++;
if(retry > 8000)
return 0;
}
while(temp == 0); //接收FIFO为空,等待
return SPI->RXDATA; //返回受到的数据
}
spi.h
/*************************************************************************************************************
* 文件名: spi.h
* 功能: S3C6410 SPI底层驱动函数
* 作者: 陈鹏
* 创建时间: 2012年9月8日20:35
* 最后修改时间:2012年9月8日
* 详细: SPI始化,发送,接收,配置等
*************************************************************************************************************/
#ifndef SPI_H_
#define SPI_H_
#include "system.h"
//SPI配置结构定义
typedef struct
{
u8 EnSlave; //使能SPI从设备模式
u8 EnCPOH; //使能空闲时钟高电平
u8 EnCPHB; //使能时钟第二个边沿有效,否则为第一个边沿有效
u8 EnRx; //使能接收
u8 EnTx; //使能发送
u8 SetTranSize; //设置传输数据宽度,8bit,16bit,32bit;
u8 EnRxDMA; //使能接收DMA
u8 EnTxDMA; //使能发送DMA
u8 EnDMA4Burst; //设置DMA传输类型为4个脉冲
u8 EnAutoCS; //使能自动片选
}SPI_Config_TypeDef;
//默认模式1
//主设备模式,空闲时钟低电平,第一个时钟边沿有效(上升沿有效),使能发送接收,数据宽度8bit,关闭DMA,使能手动控制片选
extern const SPI_Config_TypeDef SPI_DEFAULT_01;
void SPI_SetSpeed(u8 ch,u8 Speed);
u8 SPI_Init(u8 ch,const SPI_Config_TypeDef *config,u8 Speed);
u32 SPIx_ReadWriteData(u8 ch,u32 TxData);
#endif /*SPI_H_*/
main.c
测试
#include "system.h"
#include "uart.h"
#include "tft_lcd.h"
#include "other.h"
#include "delay.h"
#include "timer.h"
#include "spi.h"
//LED1闪烁程序,在定时器0中断服务程序中闪烁,周期400MS
void LED1_flash(void)
{
LED1_FLASH();
}
int main(void)
{
u8 i = 0;
LCD_Init(); //初始化LCD
UART0_Init(DISABLE,115200); //初始化串口,失能中断接收,波特率115200
LED_Init(); //初始化LED
rGPCCON |= 1 << 12;
rGPCDAT |= BIT3;
Timer1_Init(400000-1,ENABLE,LED1_flash); //初始化定时器0,周期400ms
lcd_printf("Get_FCLK : %d Hz\n",Get_FCLK());
lcd_printf("Get_PCLK : %d Hz\n",Get_PCLK());
SPI_Init(0,&SPI_DEFAULT_01,100);
while(1)
{
LED2_FLASH(); //LED2闪烁
//Delay_US(600000);
rGPCDAT &= ~BIT3;
SPIx_ReadWriteData(0,i++);
Delay_US(1); //适当添加延时
rGPCDAT |= BIT3;
Delay_US(6);
}
}
测试了一下硬件控制片选,发现片选信号非常完美
上图
补充SPI寄存器结构
//SPI
typedef struct
{
vu32 CHCFG; //配置寄存器
vu32 CLKCFG; //时钟配置寄存器
vu32 MODECFG; //FIFO控制寄存器
vu32 SLAVE; //从属器选择寄存器
vu32 INTEN; //中断启动寄存器
vu32 STATUS; //状态寄存器
vu32 TXDATA; //发送数据寄存器
vu32 RXDATA; //接收数据寄存器
vu32 CNT; //计数,主控器收到多少数据
vu32 CLR; //状态清除
vu32 SWAPCFG ; //交换配置寄存器
vu32 FBCLK; //反馈时钟选择寄存器
}SPI_TypeDef;
//SPI
#define SPI0_BASE 0x7f00b000
#define SPI1_BASE 0x7f00c000
//SPI0
#define SPI0 ((SPI_TypeDef*)SPI0_BASE)
//SPI1
#define SPI1 ((SPI_TypeDef*)SPI1_BASE)
上一篇:S3C6410裸机AC97驱动
下一篇:S3C6410 裸机硬件JPEG解码
推荐阅读最新更新时间:2024-03-16 15:34