spi驱动之can总线mcp2515驱动测试

2023-10-30 06:32
文章标签 驱动 测试 总线 spi mcp2515

本文主要是介绍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驱动测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/306210

相关文章

如何测试计算机的内存是否存在问题? 判断电脑内存故障的多种方法

《如何测试计算机的内存是否存在问题?判断电脑内存故障的多种方法》内存是电脑中非常重要的组件之一,如果内存出现故障,可能会导致电脑出现各种问题,如蓝屏、死机、程序崩溃等,如何判断内存是否出现故障呢?下... 如果你的电脑是崩溃、冻结还是不稳定,那么它的内存可能有问题。要进行检查,你可以使用Windows 11

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

Verybot之OpenCV应用一:安装与图像采集测试

在Verybot上安装OpenCV是很简单的,只需要执行:         sudo apt-get update         sudo apt-get install libopencv-dev         sudo apt-get install python-opencv         下面就对安装好的OpenCV进行一下测试,编写一个通过USB摄像头采

BIRT 报表的自动化测试

来源:http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-birttest/如何为 BIRT 报表编写自动化测试用例 BIRT 是一项很受欢迎的报表制作工具,但目前对其的测试还是以人工测试为主。本文介绍了如何对 BIRT 报表进行自动化测试,以及在实际项目中的一些测试实践,从而提高了测试的效率和准确性 -------

可测试,可维护,可移植:上位机软件分层设计的重要性

互联网中,软件工程师岗位会分前端工程师,后端工程师。这是由于互联网软件规模庞大,从业人员众多。前后端分别根据各自需求发展不一样的技术栈。那么上位机软件呢?它规模小,通常一个人就能开发一个项目。它还有必要分前后端吗? 有必要。本文从三个方面论述。分别是可测试,可维护,可移植。 可测试 软件黑盒测试更普遍,但很难覆盖所有应用场景。于是有了接口测试、模块化测试以及单元测试。都是通过降低测试对象