zoukankan      html  css  js  c++  java
  • 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.c
    	spi控制器驱动层 :	平台移植相关层,每条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_message driver有几个重要的函数 :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_probe probe里完毕了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; } 函数非常easy,利用定义的spi_board_info信息,填充了boardinfo结构。并挂到board_list链表 spi的控制层中重要函数。master的注冊函数 int spi_register_master(struct spi_master *master) 扫描并实例化spi设备 scan_boardinfo(master); 寻找注冊好的board_list链表中的device list_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) ", __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 write int total_read; // total number of bytes to read int total_write_or_read; // total number of bytes to read or write int bytes; // number of bytes read-writes int qwords; // number of qword read-writes int readout; // number of byte reads at the end of transfer int priming; // number of bytes to prime int 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



  • 相关阅读:
    opengl 4.5离线文档下载
    c++下利用URLOpenStream下载
    实时阴影渲染(三):软阴影深度校正
    实时阴影渲染(二):软阴影
    实时阴影渲染(一):PSSM平行分割阴影图
    Ogre材质shader模版
    本地springboot项目连接本地mysql报错。com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
    Caused by: java.lang.IllegalStateException: AnnotationAwareAspectJAutoProxyCreator is only available on Java 1.5 and higher
    关于布隆过滤器
    CSS系列------选择器和选择器的优先级
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7371512.html
Copyright © 2011-2022 走看看