【Cubieboard2】配置编译内核支持SPI全双工通信驱动

2022-09-16 13:23:31 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

1,cubieboard2 A20系列,无论是官方还是社区的系统,默认都是不支持SPI总线驱动的。需要重新编译配置内核,修改文件才能支持SPI全双工通信。本文以Cuieboard2 Debain为例,进行讲解;

2,重新编译配置内核

(1)先去官网下载对应版本的linux内核源码,地址:https://github.com/linux-sunxi/linux-sunxi 我下载的是sun-xi3.4

或者直接git

代码语言:javascript复制
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

(2)找一台安装了Ubuntu系统的机器,将源代码解压并进入解压根目录;(也可以直接在Cubieboard2板子上进行编译,但是效率慢,依赖库问题比较多,不建议这样做;)

1、需要预先安装arm-linux-gnueabihf 交叉编译工具,可以先使用 apt-get cache search arm-linux ; 然后选择对应的文件 apt-get install XX;

2、之后先将sun-xi 7i 的默认编译配置复制到 .config中: 输入

代码语言:javascript复制
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sun7i_defconfig

ARCH=arm 非常关键,程序会进入到/arch/arm目录中去寻找 sun7i_defconfig 配置文件;

3、在linux-sunxi/drivers/spi/ 下创建文件spi-sun7i.c 文件内容见本文末尾;

4、修改 linux-sunxi/drivers/spi/Makefile 文件,在末尾加上下句:

代码语言:javascript复制
obj-$(CONFIG_SPI_SUN7I)          = spi-sun7i.o

5、修改linux-sunxi/drivers/spi/Kconfig 文件,加上下面内容:

代码语言:javascript复制
config SPI_SUN7I
   tristate "SUN7I SPI Controller"
   depends on ARCH_SUN7I
   help
      Allwinner Soc SPI controller,present on SUN7I chips.

config SUN7I_SPI_NDMA
        bool "SUN7I SPI Normal DMA mode select"
        depends on SPI_SUN7I
        help
          This selects SPI DMA mode with DMA transfer
          Y select NDMA mode and N select DDMA mode

6、在根目录下,输入 vim .config

加入或修改如下内容:

代码语言:javascript复制
CONFIG_SPI_SUN7I=y
CONFIG_SUN7I_SPI_NDMA=y

7、编译内核:

代码语言:javascript复制
make -j 4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage modules
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=output modules_install

8、如果编译成功,那么在 /arch/arm/boot/ 下面会有uImage文件生成,这就是新的内核了。 第二句会生成新的module文件,在output文件价下。

9、切换到Cubieboard2 Debian系统下, 首先 mkdir /media/nanda 新建挂载点; mount /dev/nanda /media/nanda 挂载系统;

将新编译的uImage内核替换 /media/nanda/ 下的uImage文件; 将新生成的 outpiut/lib/modules/ 下的文件拷贝覆盖到 debian的/lib/modules 下,替换原来的文件;

(3)修改文件

进入/media/nanda/ 目录, cp script.bin script.bin.bak 先备份要修改的文件;

bin2fex script.bin script.fex 将bin文件转换为fex文件;

vim script.fex 修改文件如下内容(没有的请自行添加):

代码语言:javascript复制
[spi0_para]
spi_used = 1
spi_cs_bitmap = 1
spi_cs0 = port:PI10<2><default><default><default>
spi_cs1 = port:PI14<2><default><default><default>
spi_sclk = port:PI11<2><default><default><default>
spi_mosi = port:PI12<2><default><default><default>
spi_miso = port:PI13<2><default><default><default>

[spi_devices]
spi_dev_num = 1

[spi_board0]
modalias = "spidev"
max_speed_hz = 100000
bus_num = 0
chip_select = 0
mode = 0
full_duplex = 1
manual_cs = 0

fex2bin script.fex script.bin 将fex文件转换为bin文件;并替换/media/nanda/script.bin 文件;

要想实现spi全双工通信,下面一步至关重要,这个文件必须修改:

修改Cubieboard2 Debian下的 /usr/include/linux/spi/spidev.h 为如下内容(其实只是增加了一句代码,但是必须改):

代码语言:javascript复制
struct spi_ioc_transfer {
 __u64 tx_buf;
 __u64 rx_buf;

 __u32 len;
 __u32 speed_hz;

 __u16 interbyte_usecs;
 __u16 delay_usecs;
 __u8 bits_per_word;
 __u8 cs_change;
 __u32 pad;
}

保存文件重启系统;

3. 验证是否SPI是否配置成功

(1)重启后,在/dev 目录下看是否生成了 spidev0.0 文件,如果有,那么已经成功了一多半了;

(2)将Cubieboard2上的spi MOSI MISO进行短接(形成回环)自己发自己收,然后编写测试代码如下:

代码语言:javascript复制
/*
* 说明:SPI通讯实现
* 方式一: 同时发送与接收实现函数: SPI_Transfer()
* 方式二:发送与接收分开来实现
* SPI_Write() 只发送
* SPI_Read() 只接收
* 两种方式不同之处:方式一,在发的过程中也在接收,第二种方式,收与发单独进行
* Created on: 2013-5-28
* Author: lzy
*/

#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>


#include "Debug.h"
#define SPI_DEBUG 0


static const char *device = "/dev/spidev0.0";
static uint8_t mode = 0; /* SPI通信使用全双工,设置CPOL=0,CPHA=0。 */
static uint8_t bits = 8; /* 8bits读写,MSB first。*/
static uint32_t speed = 100 * 1000;/* 设置100K传输速度 */
static uint16_t delay = 0;
static int g_SPI_Fd = 0;


static void pabort(const char *s)
{
perror(s);
abort();
}


/**
* 功 能:同步数据传输
* 入口参数 :
* TxBuf -> 发送数据首地址
* len -> 交换数据的长度
* 出口参数:
* RxBuf -> 接收数据缓冲区
* 返回值:0 成功
*/
int SPI_Transfer(const uint8_t *TxBuf, uint8_t *RxBuf, int len)
{
int ret;
int fd = g_SPI_Fd;


struct spi_ioc_transfer tr ={
.tx_buf = (unsigned long) TxBuf,
.rx_buf = (unsigned long) RxBuf,
.len =len,
.delay_usecs = delay,
};


ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pr_err("can't send spi message");
else
{
#if SPI_DEBUG
int i;
pr_debug("nsend spi message Succeed");
pr_debug("nSPI Send [Len:%d]: ", len);
for (i = 0; i < len; i  )
{
if (i % 8 == 0)
printf("nt");
printf("0xX ", TxBuf[i]);
}
printf("n");


pr_debug("SPI Receive [len:%d]:", len);
for (i = 0; i < len; i  )
{
if (i % 8 == 0)
printf("nt");
printf("0xX ", RxBuf[i]);
}
printf("n");
#endif
}
return ret;
}


/**
* 功 能:发送数据
* 入口参数 :
* TxBuf -> 发送数据首地址
*len -> 发送与长度
*返回值:0 成功
*/
int SPI_Write(uint8_t *TxBuf, int len)
{
int ret;
int fd = g_SPI_Fd;


ret = write(fd, TxBuf, len);
if (ret < 0)
pr_err("SPI Write errorn");
else
{
#if SPI_DEBUG
int i;
pr_debug("nSPI Write [Len:%d]: ", len);
for (i = 0; i < len; i  )
{
if (i % 8 == 0)
printf("nt");
printf("0xX ", TxBuf[i]);
}
printf("n");


#endif
}


return ret;
}


/**
* 功 能:接收数据
* 出口参数:
* RxBuf -> 接收数据缓冲区
* rtn -> 接收到的长度
* 返回值:>=0 成功
*/
int SPI_Read(uint8_t *RxBuf, int len)
{
int ret;
int fd = g_SPI_Fd;
ret = read(fd, RxBuf, len);
if (ret < 0)
pr_err("SPI Read errorn");
else
{
#if SPI_DEBUG
int i;
pr_debug("SPI Read [len:%d]:", len);
for (i = 0; i < len; i  )
{
if (i % 8 == 0)
printf("nt");
printf("0xX ", RxBuf[i]);
}
printf("n");
#endif
}


return ret;
}


/**
* 功 能:打开设备 并初始化设备
* 入口参数 :
* 出口参数:
* 返回值:0 表示已打开 0XF1 表示SPI已打开 其它出错
*/
int SPI_Open(void)
{
int fd;
int ret = 0;


if (g_SPI_Fd != 0) /* 设备已打开 */
return 0xF1;


fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");
else
pr_debug("SPI - Open Succeed. Start Init SPI...n");


g_SPI_Fd = fd;
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
pabort("can't set spi mode");


ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
pabort("can't get spi mode");


/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word");


ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word");


/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz");


ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");


pr_debug("spi mode: %dn", mode);
pr_debug("bits per word: %dn", bits);
pr_debug("max speed: %d KHz (%d MHz)n", speed / 1000, speed / 1000 / 1000);


return ret;
}


/**
* 功 能:关闭SPI模块
*/
int SPI_Close(void)
{
int fd = g_SPI_Fd;


if (fd == 0) /* SPI是否已经打开*/
return 0;
close(fd);
g_SPI_Fd = 0;


return 0;
}


/**
* 功 能:自发自收测试程序
* 接收到的数据与发送的数据如果不一样 ,则失败
* 说明:
* 在硬件上需要把输入与输出引脚短跑
*/
int SPI_LookBackTest(void)
{
int ret, i;
const int BufSize = 16;
uint8_t tx[BufSize], rx[BufSize];


bzero(rx, sizeof(rx));
for (i = 0; i < BufSize; i  )
tx[i] = i;


pr_debug("nSPI - LookBack Mode Test...n");
ret = SPI_Transfer(tx, rx, BufSize);
if (ret > 1)
{
ret = memcmp(tx, rx, BufSize);
if (ret != 0)
{
pr_err("LookBack Mode Test errorn");
//pabort("error");
}
else
pr_debug("SPI - LookBack Mode OKn");
}

return ret;
}

上面两项都测试通过了,那么你的SPI内核态驱动已经完成了。剩下的就是根据需求编写用户态驱动了;(很简单,就是跟操作文件一样)

4、注意事项:

(1)如果你的Cubieboard2 Debian是从NAND启动的,那么在重新编译内核的时候需要增加NAND的驱动支持,具体方法可以 make ARCH=arm menuconfig 或者直接修改.config 文件将NAND相关部分改为y 默认是不支持NAND驱动的;

(2)使用逻辑分析仪进行分析是非常可靠的。软件可能有问题,但是逻辑分析仪是直接测量的电平,很有说服力;注意spi的频率设置,一般100Khz-500khz就够了,太大了板子顶不住,而且逻辑分析仪的采样频率要比spi的频率高才能正确采样;

(3)关于Cubieboard2的针脚 只支持spi0 这个设备,具体定义见:

http://linux-sunxi.org/A20/PIO

http://docs.cubieboard.org/products/a10_cubieboard/expansion_ports

(4)关于spi-sun7i.c 文件下载连接:

http://download.csdn.net/detail/u010352603/9548040

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/163045.html原文链接:https://javaforall.cn

0 人点赞