刚刚写的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速度设置 *返回 : 无 *依赖 : 底层宏定义 *作者 : [email protected] *时间 : 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:初始化失败 *依赖 : 底层宏定义 *作者 : [email protected] *时间 : 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:要发送的数据 *返回 : 收到的数据 *依赖 : 底层宏定义 *作者 : [email protected] *时间 : 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_*/
测试
#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)