#include "SPI_Init.h"
#include "delay.h"
#include "usart.h"
//以下是SPI模块的初始化代码,配置成主机模式,访问SD Card/W25X16/24L01/JF24C
//SPI口初始化
//这里针是对SPI1的初始化
void SPIx_Init(void)
{
RCC->APB2ENR|=1<<2; //PORTA时钟使能
RCC->APB2ENR|=1<<12; //SPI1时钟使能
GPIOA->CRL&=0X0000FFFF;
GPIOA->CRL|=0XBBB30000;//PA5.6.7复用 PA4--CS
GPIOA->ODR|=0XF<<5; //PA4.5.6.7上拉
RCC->APB2ENR|=1<<4; //PORTC 时钟使能
GPIOC->CRL&=0XFF00FFFF;//PC4.5
GPIOC->CRL|=0X00380000;//PC4--INT .5--RST 推挽输出
GPIOC->ODR|=1<<5;//PC5输出为高
GPIOC->IDR|=1<<4;//PC4上拉输入
SPI1->CR1|=0<<10;//全双工模式
SPI1->CR1|=1<<9; //启用软件nss管理
SPI1->CR1|=1<<8; //SSM位,NSS电平为高,
SPI1->CR1|=1<<2; //SPI主机
SPI1->CR1|=0<<11;//8bit数据格式
SPI1->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1
SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始,CPHA=1
SPI1->CR1|=7<<3; //Fsck=Fcpu/256
SPI1->CR1|=0<<7; //MSBfirst
SPI1->CR1|=1<<6; //SPE=1 设备使能
}
u8 SPI1_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while((SPI1->SR&1<<1)==0)//等待发送区空
{
retry++;
//if(retry>200)return 0;
}
SPI1->DR=TxData; //发送一个byte
retry=0;
while((SPI1->SR&1<<0)==0) //等待接收完一个byte
{
retry++;
//if(retry>200)return 0;
}
return SPI1->DR; //返回收到的数据
}
/*******************************************************************************
* 描 述 : 延时指定微秒时间,根据单片机主频调整,不精确.
*******************************************************************************/
void mDelayuS( UINT8 us )
{
while ( us -- );
}
/*******************************************************************************
* 描 述 : 延时指定毫秒时间,根据单片机主频调整,不精确
*******************************************************************************/
void mDelaymS( UINT8 ms )
{
while ( ms -- )
{
mDelayuS( 250 );
mDelayuS( 250 );
mDelayuS( 250 );
mDelayuS( 250 );
}
}
/*******************************************************************************
* 函 数 名 : CH376_PORT_INIT
* 描 述 : 由于使用软件模拟SPI读写时序,所以进行初始化.
* 如果是硬件SPI接口,那么可使用mode3(CPOL=1&CPHA=1)或
* mode0(CPOL=0&CPHA=0),CH376在时钟上升沿采样输入,下降沿输出,数
* 据位是高位在前.
*******************************************************************************/
void CH376_PORT_INIT( void )
{
CH376_SPI_SCS = 1; /* 禁止SPI片选 */
CH376_INT_WIRE=1; /* 默认为高电平,SPI模式3,也可以用SPI模式0,但模拟程序可能需稍做修改 */
/* 对于双向I/O引脚模拟SPI接口,那么必须在此设置SPI_SCS,SPI_SCK,SPI_SDI为输出方向,
* SPI_SDO为输入方向 */
}
/*******************************************************************************
* 函 数 名 : xEndCH376Cmd 结束命令.
*******************************************************************************/
void xEndCH376Cmd( void )
{
CH376_SPI_SCS = 1; /* SPI片选无效,结束CH376命令,仅用于SPI接口方式 */
}
/*******************************************************************************
SPI输出8个位数据. * 发送: UINT8 d:要发送的数据.
*******************************************************************************/
void Spi376OutByte( UINT8 d )
{
SPI1_ReadWriteByte(d);
}
/*******************************************************************************
* 描 述 : SPI接收8个位数据. UINT8 d:接收到的数据.
*******************************************************************************/
UINT8 Spi376InByte( void )
{
/* 如果是硬件SPI接口,应该是先查询SPI状态寄存器以等待SPI字节传输完成,然后从SPI数据寄存器读出数据 */
while((SPI1->SR&1<<0)==0); //RXEN=0 接收缓冲区为空
return SPI1->DR; //返回收到的数据
}
/*******************************************************************************
* 描 述 : 向CH376写 命令.
* 输 入 : UINT8 mCmd:要发送的命令.
*******************************************************************************/
void xWriteCH376Cmd( UINT8 mCmd )
{
CH376_SPI_SCS = 1; /* 防止之前未通过xEndCH376Cmd禁止SPI片选 */
mDelayuS(10 );
mDelayuS(10 );
/* 对于双向I/O引脚模拟SPI接口,那么必须确保已经设置SPI_SCS,SPI_SCK,SPI_SDI为输出
* 方向,SPI_SDO为输入方向 */
CH376_SPI_SCS = 0; /* SPI片选有效 */
Spi376OutByte( mCmd ); /* 发出命令码 */
mDelayuS(10 );
mDelayuS(10 );
mDelayuS(10 ); /* 延时1.5uS确保读写周期大于1.5uS,或者用上面一行的状态查询代替 */
mDelayuS(10 );
}
/*******************************************************************************
* 描 述 : 向CH376写 数据.
* 输 入 : UINT8 mData:
* 要发送的数据.
*******************************************************************************/
void xWriteCH376Data( UINT8 mData )
{
Spi376OutByte( mData );
mDelayuS(10 ); /* 确保读写周期大于0.6uS */
}
/*******************************************************************************
* 函 数 名 : xReadCH376Data
* 描 述 : 从CH376读数据.
*******************************************************************************/
UINT8 xReadCH376Data( void )
{
UINT8 i;
mDelayuS(10);
i = SPI1_ReadWriteByte( 0xFF );
return( i );
}
/*******************************************************************************
* 描 述 : 查询CH376中断(INT#低电平).
* 返 回 : FALSE:无中断. TRUE:有中断.
*******************************************************************************/
UINT8 Query376Interrupt( void )
{
UINT8 i;
i = (CH376_INT_WIRE ? FALSE : TRUE ); /* 如果连接了CH376的中断引脚则直接查询中断引脚 */
return( i );
}
/*******************************************************************************
* 描 述 : 初始化CH376.
* 返 回 : FALSE:无中断. TRUE:有中断.
*******************************************************************************/
UINT8 mInitCH376Host( void )
{
UINT8 res;
mDelaymS(200);
mDelaymS(200);
mDelaymS(200);
CH376_PORT_INIT( ); /* 接口硬件初始化 */
xWriteCH376Cmd( CMD11_CHECK_EXIST ); /* 测试单片机与CH376之间的通讯接口 */
xWriteCH376Data( 0x55 );
res = xReadCH376Data( );
printf("res =%02x \n",(unsigned short)res);
xEndCH376Cmd( );
if ( res != 0xAA ) return( ERR_USB_UNKNOWN ); /* 通讯接口不正常,可能原因有:接口连接异常,其它设备影响(片选不唯一),串口波特率,一直在复位,晶振不工作 */
xWriteCH376Cmd( CMD11_SET_USB_MODE ); /* 设备USB工作模式 */
xWriteCH376Data( 0x06 ); //06H=已启用的主机方式并且自动产生SOF包 ???
mDelayuS( 20 );
res = xReadCH376Data( );
printf("res =%02x \n",(unsigned short)res);
xEndCH376Cmd( );
if ( res == CMD_RET_SUCCESS ) //RES=51 命令操作成功
{
return( USB_INT_SUCCESS ); //USB事务或者传输操作成功
}
else
{
return( ERR_USB_UNKNOWN );/* 设置模式错误 */
}
}
/************************************ End *************************************/
#include "delay.h"
#include "usart.h"
//以下是SPI模块的初始化代码,配置成主机模式,访问SD Card/W25X16/24L01/JF24C
//SPI口初始化
//这里针是对SPI1的初始化
void SPIx_Init(void)
{
RCC->APB2ENR|=1<<2; //PORTA时钟使能
RCC->APB2ENR|=1<<12; //SPI1时钟使能
GPIOA->CRL&=0X0000FFFF;
GPIOA->CRL|=0XBBB30000;//PA5.6.7复用 PA4--CS
GPIOA->ODR|=0XF<<5; //PA4.5.6.7上拉
RCC->APB2ENR|=1<<4; //PORTC 时钟使能
GPIOC->CRL&=0XFF00FFFF;//PC4.5
GPIOC->CRL|=0X00380000;//PC4--INT .5--RST 推挽输出
GPIOC->ODR|=1<<5;//PC5输出为高
GPIOC->IDR|=1<<4;//PC4上拉输入
SPI1->CR1|=0<<10;//全双工模式
SPI1->CR1|=1<<9; //启用软件nss管理
SPI1->CR1|=1<<8; //SSM位,NSS电平为高,
SPI1->CR1|=1<<2; //SPI主机
SPI1->CR1|=0<<11;//8bit数据格式
SPI1->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1
SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始,CPHA=1
SPI1->CR1|=7<<3; //Fsck=Fcpu/256
SPI1->CR1|=0<<7; //MSBfirst
SPI1->CR1|=1<<6; //SPE=1 设备使能
}
u8 SPI1_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while((SPI1->SR&1<<1)==0)//等待发送区空
{
retry++;
//if(retry>200)return 0;
}
SPI1->DR=TxData; //发送一个byte
retry=0;
while((SPI1->SR&1<<0)==0) //等待接收完一个byte
{
retry++;
//if(retry>200)return 0;
}
return SPI1->DR; //返回收到的数据
}
/*******************************************************************************
* 描 述 : 延时指定微秒时间,根据单片机主频调整,不精确.
*******************************************************************************/
void mDelayuS( UINT8 us )
{
while ( us -- );
}
/*******************************************************************************
* 描 述 : 延时指定毫秒时间,根据单片机主频调整,不精确
*******************************************************************************/
void mDelaymS( UINT8 ms )
{
while ( ms -- )
{
mDelayuS( 250 );
mDelayuS( 250 );
mDelayuS( 250 );
mDelayuS( 250 );
}
}
/*******************************************************************************
* 函 数 名 : CH376_PORT_INIT
* 描 述 : 由于使用软件模拟SPI读写时序,所以进行初始化.
* 如果是硬件SPI接口,那么可使用mode3(CPOL=1&CPHA=1)或
* mode0(CPOL=0&CPHA=0),CH376在时钟上升沿采样输入,下降沿输出,数
* 据位是高位在前.
*******************************************************************************/
void CH376_PORT_INIT( void )
{
CH376_SPI_SCS = 1; /* 禁止SPI片选 */
CH376_INT_WIRE=1; /* 默认为高电平,SPI模式3,也可以用SPI模式0,但模拟程序可能需稍做修改 */
/* 对于双向I/O引脚模拟SPI接口,那么必须在此设置SPI_SCS,SPI_SCK,SPI_SDI为输出方向,
* SPI_SDO为输入方向 */
}
/*******************************************************************************
* 函 数 名 : xEndCH376Cmd 结束命令.
*******************************************************************************/
void xEndCH376Cmd( void )
{
CH376_SPI_SCS = 1; /* SPI片选无效,结束CH376命令,仅用于SPI接口方式 */
}
/*******************************************************************************
SPI输出8个位数据. * 发送: UINT8 d:要发送的数据.
*******************************************************************************/
void Spi376OutByte( UINT8 d )
{
SPI1_ReadWriteByte(d);
}
/*******************************************************************************
* 描 述 : SPI接收8个位数据. UINT8 d:接收到的数据.
*******************************************************************************/
UINT8 Spi376InByte( void )
{
/* 如果是硬件SPI接口,应该是先查询SPI状态寄存器以等待SPI字节传输完成,然后从SPI数据寄存器读出数据 */
while((SPI1->SR&1<<0)==0); //RXEN=0 接收缓冲区为空
return SPI1->DR; //返回收到的数据
}
/*******************************************************************************
* 描 述 : 向CH376写 命令.
* 输 入 : UINT8 mCmd:要发送的命令.
*******************************************************************************/
void xWriteCH376Cmd( UINT8 mCmd )
{
CH376_SPI_SCS = 1; /* 防止之前未通过xEndCH376Cmd禁止SPI片选 */
mDelayuS(10 );
mDelayuS(10 );
/* 对于双向I/O引脚模拟SPI接口,那么必须确保已经设置SPI_SCS,SPI_SCK,SPI_SDI为输出
* 方向,SPI_SDO为输入方向 */
CH376_SPI_SCS = 0; /* SPI片选有效 */
Spi376OutByte( mCmd ); /* 发出命令码 */
mDelayuS(10 );
mDelayuS(10 );
mDelayuS(10 ); /* 延时1.5uS确保读写周期大于1.5uS,或者用上面一行的状态查询代替 */
mDelayuS(10 );
}
/*******************************************************************************
* 描 述 : 向CH376写 数据.
* 输 入 : UINT8 mData:
* 要发送的数据.
*******************************************************************************/
void xWriteCH376Data( UINT8 mData )
{
Spi376OutByte( mData );
mDelayuS(10 ); /* 确保读写周期大于0.6uS */
}
/*******************************************************************************
* 函 数 名 : xReadCH376Data
* 描 述 : 从CH376读数据.
*******************************************************************************/
UINT8 xReadCH376Data( void )
{
UINT8 i;
mDelayuS(10);
i = SPI1_ReadWriteByte( 0xFF );
return( i );
}
/*******************************************************************************
* 描 述 : 查询CH376中断(INT#低电平).
* 返 回 : FALSE:无中断. TRUE:有中断.
*******************************************************************************/
UINT8 Query376Interrupt( void )
{
UINT8 i;
i = (CH376_INT_WIRE ? FALSE : TRUE ); /* 如果连接了CH376的中断引脚则直接查询中断引脚 */
return( i );
}
/*******************************************************************************
* 描 述 : 初始化CH376.
* 返 回 : FALSE:无中断. TRUE:有中断.
*******************************************************************************/
UINT8 mInitCH376Host( void )
{
UINT8 res;
mDelaymS(200);
mDelaymS(200);
mDelaymS(200);
CH376_PORT_INIT( ); /* 接口硬件初始化 */
xWriteCH376Cmd( CMD11_CHECK_EXIST ); /* 测试单片机与CH376之间的通讯接口 */
xWriteCH376Data( 0x55 );
res = xReadCH376Data( );
printf("res =%02x \n",(unsigned short)res);
xEndCH376Cmd( );
if ( res != 0xAA ) return( ERR_USB_UNKNOWN ); /* 通讯接口不正常,可能原因有:接口连接异常,其它设备影响(片选不唯一),串口波特率,一直在复位,晶振不工作 */
xWriteCH376Cmd( CMD11_SET_USB_MODE ); /* 设备USB工作模式 */
xWriteCH376Data( 0x06 ); //06H=已启用的主机方式并且自动产生SOF包 ???
mDelayuS( 20 );
res = xReadCH376Data( );
printf("res =%02x \n",(unsigned short)res);
xEndCH376Cmd( );
if ( res == CMD_RET_SUCCESS ) //RES=51 命令操作成功
{
return( USB_INT_SUCCESS ); //USB事务或者传输操作成功
}
else
{
return( ERR_USB_UNKNOWN );/* 设置模式错误 */
}
}
/************************************ End *************************************/