本文主要是介绍spi驱动之can总线mcp2515驱动测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
问1:linux内核.config Makefile Kbuild的关系?
答1:在word里可以找到答案问2:因为mcp2515是spi转can芯片,所以首先移植spi驱动,分析spi驱动过程
答2:----------------------------spi驱动整体框架--------------------------------------------- spi驱动分三个层次:spi核心层,spi控制器驱动层,spi设备驱动层spi核心层 : 与平台无关,向上提供统一接口,位置SPI核心层的代码位于driver/spi/spi.cspi控制器驱动层 : 平台移植相关层,每条spi总线提供相应的读写方法,物理上连接若干个从设备,一个控制器驱动可以用数据结构struct spi_master来描述spi设备驱动层 : 用户接口层,通过struct spi_driver和struct spi_device描述。 //-------------------------------------------------------
spi设备驱动层
*********************************************************
1. spi_driver和spi_device结构struct spi_driver {const struct spi_device_id *id_table;int (*probe)(struct spi_device *spi);int (*remove)(struct spi_device *spi);void (*shutdown)(struct spi_device *spi);int (*suspend)(struct spi_device *spi, pm_message_t mesg);int (*resume)(struct spi_device *spi);struct device_driver driver;
};struct spi_device {struct device dev;struct spi_master *master;u32 max_speed_hz;u8 chip_select;u8 mode;u8 bits_per_word;int irq;void *controller_state;void *controller_data;char modalias[SPI_NAME_SIZE];}.modalias = "m25p10",.mode =SPI_MODE_0, //CPOL=0, CPHA=0 此处选择具体数据传输模式.max_speed_hz = 10000000, //最大的spi时钟频率/* Connected to SPI-0 as 1st Slave */.bus_num = 0, //设备连接在spi控制器0上.chip_select = 0, //片选线号,在S5PC100的控制器驱动中没有使用它作为片选的依据,而是选择了下文controller_data里的方法。.controller_data = &smdk_spi0_csi[0], 通常来说spi_device对应着SPI总线上某个特定的slave。并且spi_device封装了一个spi_master结构体。
spi_device结构体包含了私有的特定的slave设备特性,包括它最大的频率,片选那个,输入输出模式等等总结: Driver是为device服务的,spi_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定,probe函数用于驱动和设备匹配时被调用。从上面的结构体注释中我们可以知道,SPI的通信是通过消息队列机制,而不是像I2C那样通过与从设备进行对话的方式。***********************************************
2. spi_device在board中如何注册,通过spi_board_info结构体spi_device以下一系列的操作是在platform板文件中完成!spi_device的板信息用spi_board_info结构体来描述:struct spi_board_info {charmodalias[SPI_NAME_SIZE];const void*platform_data;void*controller_data;intirq;u32max_speed_hz;u16bus_num;u16chip_select;u8mode;};这个结构体记录了SPI外设使用的主机控制器序号、片选信号、数据比特率、SPI传输方式等构建的操作是以下的两个步骤:1.
static struct spi_board_info s3c_spi_devs[] __initdata = {{.modalias = "m25p10a",.mode = SPI_MODE_0,.max_speed_hz = 1000000,.bus_num = 0,.chip_select = 0,.controller_data = &smdk_spi0_csi[SMDK_MMCSPI_CS],},};2.而这个info在init函数调用的时候会初始化:spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));//注册spi_board_info。
这个代码会把spi_board_info注册到链表board_list上。spi_device封装了一个spi_master结构体,
事实上spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,
找到挂接在它上面的spi设备,然后创建并注册spi_device。至此spi_device就构建并注册完成了!!!!!!!!!!!!!*******************************************************************
3. spi_driver的构建与注册driver有几个重要的结构体:spi_driver、spi_transfer、spi_messagedriver有几个重要的函数 :spi_message_init、spi_message_add_tail、spi_sync//spi_driver的构建static struct spi_driver m25p80_driver = { .driver = {.name ="m25p80",.bus =&spi_bus_type,.owner = THIS_MODULE,},.probe = m25p_probe,.remove =__devexit_p(m25p_remove),};//spidriver的注册spi_register_driver(&m25p80_driver);在有匹配的spi_device时,会调用m25p_probeprobe里完成了spi_transfer、spi_message的构建;spi_message_init、spi_message_add_tail、spi_sync、spi_write_then_read函数的调用----------------------------spi驱动分析流程---------------------------------------------
在spi核心层 driver/spi/spi.c static int __init spi_init(void)注册spi总线bus_register(&spi_bus_type) 在sys/class下产生spi_master这个节点,用于自动产生设备节点,下面挂接的是控制器的节点 class_register(&spi_master_class); 其中注册bus结构体
struct bus_type spi_bus_type = {.name = "spi",.dev_attrs = spi_dev_attrs,.match = spi_match_device, //匹配函数.uevent = spi_uevent,.suspend = spi_suspend,.resume = spi_resume,
}; 类结构体
static struct class spi_master_class = {.name = "spi_master",.owner = THIS_MODULE,.dev_release = spi_master_release,
}; 顺便分析下匹配函数
static int spi_match_device(struct device *dev, struct device_driver *drv)const struct spi_device *spi = to_spi_device(dev); 利用id表格匹配spi_match_id(sdrv->id_table, spi);利用名字匹配strcmp(spi->modalias, drv->name) 再来看看spi注册函数
int spi_register_driver(struct spi_driver *sdrv)注册标准的driver,匹配bus设备链表上的device,如果找到执行相应的函数 driver_register(&sdrv->driver);最后看看device相关的板级信息函数
int __init spi_register_board_info(struct spi_board_info const *info, unsigned n)
{struct boardinfo *bi;bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);if (!bi)return -ENOMEM;bi->n_board_info = n;memcpy(bi->board_info, info, n * sizeof *info);mutex_lock(&board_lock);list_add_tail(&bi->list, &board_list);mutex_unlock(&board_lock);return 0;
}
函数很简单,利用定义的spi_board_info信息,填充了boardinfo结构,并挂到board_list链表spi的控制层中重要函数,master的注册函数
int spi_register_master(struct spi_master *master)扫描并实例化spi设备scan_boardinfo(master); 寻找注册好的board_list链表中的devicelist_for_each_entry(bi, &board_list, list)因为可能存在多个spi总线,因此spi信息结构也会有多个,找到bus号匹配的就对了for (n = bi->n_board_info; n > 0; n--, chip++) {if (chip->bus_num != master->bus_num)//找到了就要实例化它上面的设备了 spi_new_device(master, chip) driver/spi/spidev.c
spi设备文件的自动产生代码分析这部分代码相当于注册了一个spi实际driver,既是核心平台无关代码,又是个具体实例,我们来看下一下具体做了什么,也好学习一spi_driver的各部分具体都需要做些什么,代码位于driver/spi/spidev.c下static int __init spidev_init(void)注册主设备号为153的spi字符设备,spidev_fops结构下面会介绍 status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);在sys/class下产生spidev这个节点,udev会利用其在dev下产生spidev spidev_class = class_create(THIS_MODULE, "spidev"); //注册spi驱动status = spi_register_driver(&spidev_spi_driver);注册的spi_driver 结构体spidev_spi_driver
static struct spi_driver spidev_spi = {.driver = {.name = "spidev",.owner = THIS_MODULE,},.probe = spidev_probe, //匹配成功.remove = __devexit_p(spidev_remove),};匹配函数
static int spidev_probe(struct spi_device *spi)下面两句用于在sys/class/spidev下产生类似于,spidev%d.%d形式的节点,这样,udev等工具就可以自动在dev下创建相应设备号的设备节点dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "spidev%d.%d",spi->master->bus_num, spi->chip_select);至此,spi驱动分析完毕。 问3:利用linux2.6.32内核自带的spi测试程序,测试的时候经过测试,mosi发送数据正确,但是mosi没有直连miso脚,也能收到数据用示波器测量miso脚,也没有接收到数据波形,默认高电平,怎么回事?
答3: 问4:逆向跟踪分析miso脚寄存器相关调用函数?
答4:spi_read_reg(drv_data, RA_UART_EMI_REC) 打印寄存器的值是正确的,现在是怎么把寄存器的数据放到驱动中间层? struct spi_driver_data
{/* Driver model hookup */struct platform_device *pdev;/* SPI framework hookup */struct spi_master *master;/* Driver message queue */struct workqueue_struct *workqueue;spinlock_t lock;struct list_head queue;int busy;int run;/* Message Transfer pump */struct work_struct pump_messages;struct tasklet_struct pump_transfers;struct spi_message* cur_msg;struct spi_transfer* next_transfer;u32 transfer_count;struct dc_spi_transfer tx;struct dc_spi_transfer rx;...} struct dc_spi_transfer
{unsigned int idx;unsigned int pos;struct spi_transfer* cur;struct spi_transfer* transfers[MAX_SPI_TRANSFER_PER_MESSAGE];
}struct spi_transfer {/* it's ok if tx_buf == rx_buf (right?)* for MicroWire, one buffer must be null* buffers must work with dma_*map_single() calls, unless* spi_message.is_dma_mapped reports a pre-existing mapping*/const void *tx_buf;void *rx_buf;unsigned len;dma_addr_t tx_dma;dma_addr_t rx_dma;unsigned cs_change:1;u8 bits_per_word;u16 delay_usecs;u32 speed_hz;struct list_head transfer_list;
}问5:分析spidev的ioctl接口函数?
答5:
在spidev.c中static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)spidev_message(spidev, ioc, n_ioc)spidev_sync(spidev, &msg)spi_async(spidev->spi, message)master->transfer(spi, message)//--------------
函数spi->master->transfer()在函数spi_bitbang_start()中被初始化为函数spi_bitbang_transfer()如下 if (!bitbang->master->transfer)bitbang->master->transfer = spi_bitbang_transfer; 函数spi_bitbang_transfer()又在函数s3c24xx_spi_probe()中被调用。 函数spi_bitbang_transfer()在文件spi_bitbang.c中实现。 //-------------------------------------------------------
调试打印记录
parse_spi_message: TX transfer for 3 bytes, buffer VA cc170000(ccf5aa55)parse_spi_message:
1 xfers,
wr 3,
rd 0
(pri=3,
qw=0,
bytes=0,
rout=3,
rxs=3)SPI_DEBUG_PRINT_INFO("%s: %d xfers, wr %d, rd %d (pri=%d, qw=%d, bytes=%d, rout=%d, rxs=%d)\n", __func__, drv_data->transfer_count, drv_data->total_write, drv_data->total_read,drv_data->priming, drv_data->qwords, drv_data->bytes, drv_data->readout, drv_data->rx_start_idx) int total_write; // total number of bytes to writeint total_read; // total number of bytes to readint total_write_or_read; // total number of bytes to read or writeint bytes; // number of bytes read-writesint qwords; // number of qword read-writesint readout; // number of byte reads at the end of transferint priming; // number of bytes to primeint rx_start_idx; // position where to start saving rx into input buffer问6:spi的probe函数执行过程,如何调用底层驱动函数?
答6:
int __init dc_usart_spi_probe(struct platform_device *pdev)init_queues(drv_data)tasklet_init(&drv_data->pump_transfers, pump_transfers, (unsigned long)drv_data)INIT_WORK(&drv_data->pump_messages, pump_messages)RELEASE_STATIC void pump_messages(struct work_struct *data)drv_data->cur_msg->state = parse_spi_message(drv_data)tasklet_schedule(&drv_data->pump_transfers) RELEASE_STATIC void pump_transfers(unsigned long data)dc_usart_nondma_transfer(drv_data)rxc = spi_receive_byte(drv_data)通过queue_work(drv_data->workqueue, &drv_data->pump_messages)调用工作任务函数pump_messages,
认为调用parse_spi_message函数中
else if (transfer->rx_buf)一直为假,所以drv_data->total_read=0,所以spi_receive_byte接收的寄存器数据没有填充spi_transfer
所以,应用接口层没有收到底层发来的数据当spi有接收数据的时候,通过什么机制调用parse_spi_message了解两个函数
tasklet_init
INIT_WORK
这篇关于spi驱动之can总线mcp2515驱动测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!