1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
硬件平台 :友善之臂Ting4412
: NRF24L01 2.4G无线模块SPI接口 内核版本:Linux3.5 硬件连接方式 以前在STM32F103C8T6上使用STM32的SPI控制器驱动过NRF24L01 2.4G无线模块,最近学习Linux设备驱动,刚好同学手边有一个Tiny4412开发板就当是练练手,顺便学习学习Linux下SPI设备驱动的编写。 NRF24L01 2.4G无线模块的SPI驱动暂时只是调试通过,能够实现Tiny4412通过NRF24L01 2.4G无线模块发送数据,暂时没写成字符设备驱动,在内核定时器中调用spi_write函数不断发送数据。 通信现象: STM32没接到数据就会报错,如下: 一旦加载驱动模块,就立马接收正常,如下: ......................................................................................只说流程,不说原理............................................................................ 1、构造struct spi_board_info对象,这里我们暂时构造两个,方便以后在SPI0控制器上挂载两个NFR24L01设备,实现相互收发数据。 struct s3c64xx_spi_csinfo cs0={ .fb_delay = 0, .line = EXYNOS4_GPX3(2),//片选引脚 }; struct s3c64xx_spi_csinfo cs1={ .fb_delay = 0, .line = EXYNOS4_GPX3(4),//片选引脚 }; static struct spi_board_info nrf24l01_spi_info[]={ { .modalias = DEV_NAME_01, .max_speed_hz = 10*1000*1000,//最大时钟,需比设备要求的时钟大,暂选10Mhz .bus_num = 0,/*exynos4412 的spi0控制器*/ .chip_select = EXYNOS4_GPX3(2),/*由spi master确定其最大值*/ .mode = SPI_MODE_0, .platform_data = (void*)EXYNOS4_GPX3(3), /*平台数据,作为NRF24LXX的CE引脚*/ .controller_data= (void*)&cs0, }, { .modalias = DEV_NAME_02, .max_speed_hz = 10*1000*1000,//最大时钟,需比设备要求的时钟大,暂选10Mhz .bus_num = 0,/*exynos4412 的spi0控制器*/ .chip_select = EXYNOS4_GPX3(4),/*由spi master确定其最大值*/ .mode = SPI_MODE_0, .platform_data = (void*)EXYNOS4_GPX3(5), /*平台数据,作为NRF24LXX的CE引脚*/ .controller_data= (void*)&cs1, }, }; 2、注册它,内核会依据构造的struct spi_board_info 对象创建对应的struct spi_device对象。 在init函数中注册构造好的truct spi_board_info对象static struct spi_board_info nrf24l01_spi_info[] static int __init demo_init(void) { printk(KERN_INFO"current func is-------------------------------------------%sn",__FUNCTION__); /*spi_register_board_info *依照spi_board_info 和spi_master的bus_num比较,匹配成功会生成一个对应的spi_device对象 */ return spi_register_board_info(nrf24l01_spi_info,ARRAY_SIZE(nrf24l01_spi_info)); } 看看spi_register_board_info干了什么? int spi_register_board_info(struct spi_board_info const *info, unsigned n) //master和bi->board_info的bus_nus比较,相等则调用spi_new_device创建spi_device对象 spi_match_master_to_boardinfo(master, &bi->board_info);调用spi_new_device spi_new_device(struct spi_master *master,struct spi_board_info *chip) int spi_add_device(struct spi_device *spi)//注册struct spi_device /*spi_add_device注册struct spi_device时候会做一些判断,一会进去分析 */ 分析spi_new_device int spi_register_board_info(struct spi_board_info const *info, unsigned n) //master和bi->board_info的bus_nus比较,相等则调用spi_new_device创建spi_device对象 spi_match_master_to_boardinfo(master, &bi->board_info);调用spi_new_device spi_new_device(struct spi_master *master,struct spi_board_info *chip) int spi_add_device(struct spi_device *spi)//注册struct spi_device /*spi_add_device注册struct spi_device时候会做一些判断,比如我们第一步构造 *static struct spi_board_info中的chip_select会和spi_master的num_chipselect做比较 *要求设置我们的片选信号不能大于spi_master的num_chipselect * if (spi->chip_select >= spi->master->num_chipselect) { * dev_err(dev, "cs%d >= max %dn",spi->chip_select,spi->master->num_chipselect); * return -EINVAL;//直接报错返回,导致我们不能注册spi_device对象...... * 这个问题怎么解决?我们直接修改spi主控器驱动就好了。 *} * 修改 vi arch/arm/mach-exynos/mach-tiny4412.c中设置spi控制器函数 * s3c64xx_spi0_set_platdata(NULL,0,1)这个函数就好了,内核默认设置 * 最大片选值为1,也就是说默认最多支持1个spi设备,那好了我们把它修改 * 大一些就好了,直接修改到0xffff,保证它一定能注册成功。 */ 至于程序中 struct s3c64xx_spi_csinfo cs0 和struct s3c64xx_spi_csinfo cs1怎么来的?为什么需要这两个?这个是安装驱动后看内核看内核打印错误信息找出来的。出错的地方为 driversspispi-s3c64xx.c的static int s3c64xx_spi_setup(struct spi_device *spi)函数中报if (IS_ERR_OR_NULL(cs)) { dev_err(&spi->dev, "No CS for SPI(%d)n", spi->chip_select); return -ENODEV; } 意思是cs为空,那么追溯cs,发现 spi_device spi->controller_data = cs;那好,我们就在 static struct spi_board_info nrf24l01_spi_info[]添加.controller_data= (void*)&cs0即可。 struct s3c64xx_spi_csinfo cs0={ .fb_delay = 0, .line = EXYNOS4_GPX3(2), }; struct s3c64xx_spi_csinfo cs1={ .fb_delay = 0, .line = EXYNOS4_GPX3(4), }; 好了下面编译成模块,但是编译的时候报WARNING: "spi_register_board_info"/nrf24xx_spi_device.ko] undefined!第一感觉是 spi_register_board_info相关的头文件没有,但是相关spi的头文件都加了,查资料发现原来是linux3.5源码中没有将spi_register_board_info函数导出符号表。那好,就编译进内核得了,将nrf24xx_spi_device.c复制drivers/spi目录下,修改Makefile,将nrf24xx_spi_device.c编译进内核。下面先给出nrf24xx_spi_device.c源码。 #include make zImage,启动内核。在sys/bus/spi/devices目录下出现下面spi0.220 spi0.222两个节点 其中0表示bus_num,220和222表示我们的static struct spi_board_info nrf24l01_spi_info[0]的.chip_select = EXYNOS4_GPX3(2),//这个宏的值就是220同理222就是EXYNOS4_GPX3(4),默认内核只创建spi0.0、spi2.0。注意现在只是在内核中创建了spi_device,还没nrf24xx的驱动。nrf24xx_spi_driver就是在STM32程序基础上简单的修修改改就好了,对比Tiny4412和STM32的程序你会发现关于nrf24l01的代码名字是一样的。 ..............................................................下面就直接给出nrf24xx_spi_driver.c的源码......................................................................... #include #include #include #include #include #include #include #include #include #include #include /******************硬件相关的信息****************/ #define DEV_NAME_01 "nrf24xx_spi_01" #define DEV_NAME_02 "nrf24xx_spi_02" #define DEV1_CS_PIN EXYNOS4_GPX3(2) #define DEV2_CS_PIN EXYNOS4_GPX3(4) #define DEV1_CE_PIN EXYNOS4_GPX3(3) #define DEV2_CE_PIN EXYNOS4_GPX3(5) /* 片选引脚接在GPX3_2 */ struct s3c64xx_spi_csinfo cs0={ .fb_delay = 0, .line = EXYNOS4_GPX3(2),//片选引脚 }; struct s3c64xx_spi_csinfo cs1={ .fb_delay = 0, .line = EXYNOS4_GPX3(4),//片选引脚 }; static struct spi_board_info nrf24l01_spi_info[]={ { .modalias = DEV_NAME_01, .max_speed_hz = 2*1000*1000,//最大时钟,需比设备要求的时钟大,暂选2Mhz .bus_num = 0,/*exynos4412 的spi0控制器*/ .chip_select = EXYNOS4_GPX3(2),/*由spi master确定其最大值*/ .mode = SPI_MODE_0, .platform_data = (void*)EXYNOS4_GPX3(3), /*平台数据,作为NRF24LXX的CE引脚*/ .controller_data= (void*)&cs0, }, { .modalias = DEV_NAME_02, .max_speed_hz = 2*1000*1000,//最大时钟,需比设备要求的时钟大,暂选2Mhz .bus_num = 0,/*exynos4412 的spi0控制器*/ .chip_select = EXYNOS4_GPX3(4),/*由spi master确定其最大值*/ .mode = SPI_MODE_0, .platform_data = (void*)EXYNOS4_GPX3(5), /*平台数据,作为NRF24LXX的CE引脚*/ .controller_data= (void*)&cs1, }, }; static int __init demo_init(void) { printk(KERN_INFO"current func is-------------------------------------------%sn",__FUNCTION__); /*spi_register_board_info *依照spi_board_info 和spi_master的bus_num比较,匹配成功会生成一个对应的spi_device对象 */ return spi_register_board_info(nrf24l01_spi_info,ARRAY_SIZE(nrf24l01_spi_info)); } static void __exit demo_exit(void) { printk(KERN_INFO"current func is-----%sn",__FUNCTION__); } module_init(demo_init); module_exit(demo_exit); MODULE_LICENSE("GPL"); 就当是记笔记。 |
|
|
|
编译驱动,并加载模块,在sys/bus/spi/drivers/目录下出现我们的nrf24xxspi驱动,如下图: ..................................................................................下面直接给出STM32的源码......................................................................... #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nrf24xx.h" #define DEVICE_NUM 1 #define GPX3CON 0x11000C60 #define GPX3DAT 0x11000C64 #define DEV_NAME_01 "nrf24xx_spi_01" #define DEV_NAME_02 "nrf24xx_spi_02" #define DEV_NAME DEV_NAME_01 #define DEV1_CS_PIN EXYNOS4_GPX3(2) #define DEV2_CS_PIN EXYNOS4_GPX3(4) #define DEV1_CE_PIN EXYNOS4_GPX3(3) #define DEV2_CE_PIN EXYNOS4_GPX3(5) unsigned char TX_ADDRESS[TX_ADR_WIDTH_5]={0x34,0x43,0x10,0x10,0x01}; //发送地址 unsigned char RX_ADDRESS[TX_ADR_WIDTH_5]={0x34,0x43,0x10,0x10,0x01}; static struct work_struct work; struct timer_list timer; struct spi_device *nrf24xx_spi; static int nrf24l01_spi_probe(struct spi_device *spi); static int nrf24l01_spi_remove(struct spi_device *spi); void Nrf24L10_RX_Mode(void); void Nrf24L10_TX_Mode(void); unsigned char SPI1_ReadBuf(unsigned char reg,unsigned char *buf,unsigned char length); unsigned char SPI1_SendBuf(unsigned char reg,unsigned char *buf,unsigned char length); unsigned char SPI1_Write_Register(unsigned char reg,unsigned char data); void Active_Nrf24l10(unsigned char cmd); unsigned int SPI1_Read_Reg(unsigned char reg,unsigned char *status); unsigned char Nrf24l10_SendBuf(unsigned char* data_buffer, unsigned char length); unsigned char Nrf24l0_ReceiveBuf(unsigned char *buf); unsigned char Nrf24l10_Irq(void); char tmp=0,counter =0; static struct spi_driver nrf24l01_spi_driver={ .driver={ .name = DEV_NAME, .bus =&spi_bus_type, .owner = THIS_MODULE, }, .probe = nrf24l01_spi_probe, .remove =__devexit_p(nrf24l01_spi_remove), }; static void timer_function(unsigned long counter) { //printk(KERN_INFO"current func is-----%sn",__FUNCTION__); tmp = tmp?0:1; gpio_set_value(DEV2_CE_PIN,tmp); mod_timer(&timer,jiffies+HZ); schedule_work(&work); } /*中断底半部中发送数据*/ static void irqBottom(struct work_struct *work) { char wbuf[50]={0}; int ret =0; // char reg = EN_AA; counter = counter>30? 0:counter; sprintf(wbuf,"Message Comes From Linux -%d %c",counter,' |