Linux下CH376S的SPI接口驱动实现细节

本文详细描述了在Linux环境下使用CH376S芯片的SPI接口进行驱动实现,涉及模拟SPI时序、51单片机操作以及Linux内核SPI框架的应用。作者分享了从硬件模拟到内核框架的两种方法,并给出了相应的代码示例和注意事项。
摘要由CSDN通过智能技术生成

SourceURL:file:///home/ui/Linux下CH376S的SPI接口驱动实现细节.docx

CH376厂商提供的时序图

从时序图可知,当CS信号由高电平变低电平后,CH376S被选定。在SPI模式3下,单片机向CH376发送数据,在SCK的上升沿,CH376S读取输入的命令和数据;于此同时CH376S向单片机输出数据。

以下是使用51单片机IO口模拟SPI时序的数据写入: 

void Spi376OutByte( UINT8 d ) /* SPI输出8个位数据 */

{

/* 如果是硬件SPI接口,应该是先将数据写入SPI数据寄存器,然后查询SPI状态寄存器以等待SPI字节传输完成 */

UINT8 i 

for ( i = 0 i < 8 i ++ )

{

CH376_SPI_SCK = 0 

if ( d & 0x80 ) CH376_SPI_SDI = 1 

else CH376_SPI_SDI = 0 

d <<= 1 /* 数据位是高位在前 */

CH376_SPI_SCK = 1 /* CH376在时钟上升沿采样输入 */

}

}

先是准备好单片机上MOSI引脚要输出的数据位的电平,然后再将SCK(时钟)电平由低拉高,形成一个时钟上升沿,这时CH376读取MOSI引脚上的电平并转换成0或者1。

下面是以51单片机IO口模拟SPI时序的数据读取:

UINT8 Spi376InByte( void ) /* SPI输入8个位数据 */

{

/* 如果是硬件SPI接口,应该是先查询SPI状态寄存器以等待SPI字节传输完成,然后从SPI数据寄存器读出数据 */

UINT8 i d 

d = 0 

for ( i = 0 i < 8 i ++ )

{

CH376_SPI_SCK = 0  

d <<= 1 /* 数据位是高位在前 ,可以理解为这是一个向前的移位动作*/

if ( CH376_SPI_SDO ) d ++ /*d++是“个位”“置1”操作*/

CH376_SPI_SCK = 1 

}

return( d ) //最终根据SDO的电平判断返回1字节数据

}

下面是对输入输出操作还有进一步封装:

#define xEndCH376Cmd( ) { CH376_SPI_SCS = 1}

/* SPI片选无效,结束CH376命令,仅用于SPI接口方式 */

void xWriteCH376Cmd( UINT8 mCmd ) /* CH376写命令 */

{

CH376_SPI_SCS = 1 /* 防止之前未通过xEndCH376Cmd禁止SPI片选 */

mDelay0_5uS( ) 

/* 对于双向I/O引脚模拟SPI接口,那么必须确保已经设置SPI_SCSSPI_SCKSPI_SDI为输出方向,SPI_SDO为输入方向 */

CH376_SPI_SCS = 0 /* SPI片选有效 */

Spi376OutByte( mCmd ) /* 发出命令码 */

mDelay0_5uS( ) mDelay0_5uS( ) mDelay0_5uS( ) 

/* 延时1.5uS确保读写周期大于1.5uS,或者用上面一行的状态查询代替*/

}

void xWriteCH376Data( UINT8 mData ) /* CH376写数据 */

{

Spi376OutByte( mData ) 

//mDelay0_5uS( ) /* 确保读写周期大于0.6uS */

}

UINT8 xReadCH376Data( void ) /* CH376读数据 */

{

//mDelay0_5uS( ) /* 确保读写周期大于0.6uS */

return( Spi376InByte( ) ) 

}

从以上代码可以看出每次单片机与CH376S通信,都要先拉低CS电平,然后传送1字节命令,其后再传送若干字节数据,最后再拉高CS电平,完成一次完整的通信。

那么如果我们想写一个Linux下CH376S的驱动模块,针对这些SPI的底层通信,我们该如何实现?

有两种方法:

方法一、对照上面的51单片机代码,写一个以GPIO引脚模拟的SPI底层通信程序,包括控制CS引脚做出高、低电平变化的程序段、模拟MOSI输出和MISO输入的程序段;

方法二、利用linux内核提供的SPI框架(包括SPI Core、SPI Master、SPI Device、SPI transfer、SPI message等等)完成CH376S的SPI接口程序设计。

两方法各有优缺点,方法一实现简单,控制过程完全由编程实现,并不需要单片机提供专用的SPI端口,但没有利用到单片机集成的SPI硬件接口、也没有发挥出linux已有的SPI架构优势。方法二所涉及的理论知识多而繁杂,阅读源代码的工作量较大,实现上基本看不到硬件操控的痕迹,但通信效率方面也相当出色。

那么如何以方法二实现CH376S的SPI接口通信呢?笔者经多日摸索,终于找到了一点规律,现在分享给大家,代码可能不是很完美,希望能给有需要的读者以帮助,同时如果大家有什么好的建议也请不吝赐教。

话不多说,直接上代码:

//多字节写入操作

void Spi376OutBytes(unsigned char *d,unsigned char cnt){

int ret;

ret = spi_write(spi_device,d,cnt);

if (ret != 0)

printk(KERN_NOTICE"outbyte,spi_write failed.\n");

return;

}

//一字节写入ch376

void Spi376OutByte(unsigned char d){

int ret;

ret = spi_write(spi_device,&d,1);

if (ret !=0)

printk(KERN_NOTICE"in outbyte,spi_write failed.\n");

return;

}

对输入输出操作进一步封装:

void xWriteCH376Cmd(unsigned char mCmd)

{

Spi376OutByte(mCmd);

//延时1.5uS确保读写周期大于1.5uS

udelay(3);

}

//向CH376写入命令+数据的多字节指令

void xWriteCH376Cmds(unsigned char *d,unsigned char cnt){

Spi376OutBytes(d,cnt);

}

//向CH376写入单字节数据

void xWriteCH376Data(unsigned char mData)

{

Spi376OutByte(mData);

/* 确保读写周期大于0.6uS */

udelay(1); //应该是等待CH376处理数据并在SDO口准备输出所用的时间.

}

//从CH376读取反馈结果

int xReadCH376Data(void)

{

char ret;

if(spi_read(spich376_dev.spi_device,&ret,1)==0)

return ret;

else

return -1; //返回-1表示读取失败

}

对于以上代码,看起来很简单吧?但有一些事还是需要说一下:

  1. xWriteCH376Cmds()能单独使用,在该函数调用期间,Linux内核会自动完成CS、CLK、MOSI引脚的联合动作,以完成从单片机到CH376S方向(MOSI)的多字节写入操作;再配合xReadCH376Data()从CH376读取反馈(MISO),就能完成整套的通信过程;
  2. 拉低CS电平->xWriteCH376Cmd()->xWriteCH376Data()->xReadCH376Data()->

拉高CS电平,是一套完整的通信过程。相当于上面xWriteCH376Cmds()和xReadCH376Data()的调用过程。

3、xWriteCH376Cmd()通常不单独调用,它完成的是单片机到CH376S方向(MOSI)的单字节命令写入操作,如果xWriteCH376Cmd()被单独调用说明后续没有需要写入CH376S的数据,只是单字节指令;

4、xWriteCH376Data()不能单独调用,它完成的是单片机到CH376S方向(MOSI)的单字节数据写入操作,与xWriteCH376Cmd() 共用一个CS拉低过程,而且必须xWriteCH376Cmd() 在前,xWriteCH376Data() 在后。

5、关于CS线的控制问题。查看上述代码你会发现,Linux内核使用spi_write() 和 spi_read()封装了对SPI接口的通信操作,里面包含了对CH376S的CS线的电平的自动控制,通常一个spi_write()或者一个spi_read()过程中Linux内核就会使CS线的电平“高->低->高”变换一次,所以对CH376S的通信操作推荐使用xWriteCH376Cmds()来完成,如果非要使用xWriteCH376Cmd()和xWriteCH376Data()组合来完成,那么CS引脚就必须自己编程控制了。另外多说一句,不要在Linux里控制单片机内置spi接口的CS引脚,自己另寻一个空闲的GPIO引脚作为CH376S的CS,实现起来要方便的多,因为什么,留给你自己思考吧!另外再补充一句,不要再多路复用的SPI接口上自己编程控制CS引脚,因为什么,也留给你自己思考吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值