Linux spi总线驱动


spi 硬件


spi_flash的gpio引脚安排:
Vcc : 3.3v j1-pin17
Vdd : 0 j1-pin20
spimiso :GPG5 j3-pin33
spimosi :GPG6 j3-pin35
spiclk :GPG7 j3-pin34
flash_cs:GPG2 j3-pin36


标准的spi工作模式do在时钟的上升沿将数据写到flash,
在时钟的下降沿将数据从flash读回来。


spi控制器往spi设备发送数据是通过Do引脚,总是一位一位地发,先发高位,再发低位.第一个写出去的数据是bit7,第二个写出去的数据是bit6,依次写到bit0
spi控制器通过Di引脚将spi设备的数据读回来时,也是先读高位再读低位.第一个读回来的数据是bit7,第二个都回来的是bit6,依次读到bit0
读的时候是连续地读的,两个字节的数据接着回来。


flash的操作:
program:在烧写之前要先erase对应地址,erase之后要去除flash芯片的写保护(设置stat reg就可以去除芯片的写保护),
读写stat reg之前也要去除stat reg的保护(主机向spi flash发出指定command)

read:在读flash的特定地址之前要先按照芯片手册的特定指令发出去就行了。


status regs:
WEL:0 ->write disable :在完成一个擦除,烧写或者写操作之后WEL会自动恢复到0状态
必须先状态写使能才可以:页烧写,扇区擦除,块擦除,片擦除或者写状态寄存器

BUSY: 1:正忙,0:有空(read only)
WEL(read only): 1:when write enable 0:when write disable
BP2,BP1,BP0: 决定每一块内存是否有写保护,通过设置这几位可以进行块保护,默认为取消所有块保护.------>重要

在WP引脚没有控制的条件下,将SRP1,SRP0都设置为0,在write_enble并且WEL=1的前提下,statu regs可写。
SRP0,SRP1:是可读可写位,作用是确定状态寄存器是否可以读写(软件保护,硬件保护,电源支持保护,单次烧写保护);
没有默认状态(要对状态寄存器进行操作,必须先设置这两位)------>重要

TB:与SRP0,SRP1,WEL配合使用,用于设置块保护,default 0;
SEC: 与BP2,BP1,BP0配合使用,用于设置块保护,default 0;
CMP: 与SEC,TB,BP2,BP1,BP0配合使用为点阵保护提供灵活的设置,default 0;
SUS(read only): 1:command 75, 0:command 7a;
LB3,LB2,LB0: 安全寄存器保护位,默认为0,不保护。

setting the Status Register Protect (SRP0, SRP1) and Block Protect (SEC,TB, BP2, BP1 and BP0) bits.
These settings allow a portion or all of the memory to be configured as read only


spi linux驱动程序*


内核里有关spi的使用情况介绍:linux-5.8.5\Documentation\spi\spi-summary.rst

spi_device相关:
static struct ads7846_platform_data ads_info = {
.vref_delay_usecs = 100,
.x_plate_ohms = 580,
.y_plate_ohms = 410,
};

static struct spi_board_info spi_board_info[] __initdata = {
	{
		.modalias	= "ads7846",
		.platform_data	= &ads_info,//遥相呼应1
		.mode		= SPI_MODE_0,
		.irq		= GPIO_IRQ(31),
		.max_speed_hz	= 120000 /* max sample rate at 3V */ * 16,
		.bus_num	= 1,
		.chip_select	= 0,
	},
};

下面这个函数的作用register SPI devices for a given board ->:linux-5.8.5\drivers\spi\spi.c
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));//将一款单板上的所有spi设备一起注册进去。

spi_driver相关:
static struct spi_driver CHIP_driver = {
.driver = {
.name = “CHIP”,
.owner = THIS_MODULE,
.pm = &CHIP_pm_ops,
},

	.probe		= CHIP_probe,
	.remove		= CHIP_remove,
};

static int CHIP_probe(struct spi_device *spi)
{
	struct CHIP			*chip;
	struct CHIP_platform_data	*pdata;

	/* assuming the driver requires board-specific data: */
	pdata = &spi->dev.platform_data;   //遥相呼应2  
	if (!pdata)
		return -ENODEV;

	/* get memory for driver's per-chip state */
	chip = kzalloc(sizeof *chip, GFP_KERNEL);
	if (!chip)
		return -ENOMEM;
	spi_set_drvdata(spi, chip);

	... etc
	return 0;
}

spi_async(),
spi_read(), 
spi_write(), 
spi_write_then_read(),
spi_w8r16(): writing an eight bit command and reading a sixteen bit response
spi_transfer,spi_transfer,
	If you like, spi_message_alloc() and spi_message_free() convenience
routines are available to allocate and zero-initialize an spi_message
with several transfers.

spi_device的设备驱动的编写可以参考linux-5.8.5\arch\sh\boards\board-sh7757lcr.c



字符设备驱动的copy_from_user最多只可以传输4k的数据。


重点:向内核注册的spi_device的片选引脚(cs-pin)按正常来说是由所注册的spi_device来指定,要修改spi_control的驱动代码,提供set_cs函数,也就是修改spi_master的驱动程序
提供可以从spi_device获取片选引脚的set_cs函数,详细参考:
linux-5.8.5\drivers\spi\spi-s3c24xx.c line:566~582
linux-5.8.5\arch\arm\plat-samsung\devs.c line:887~905

  但是spi_control的驱动程序一般也有一个默认的片选引脚(假如spi_device注册的时候没有提供片选引脚,即使用默认的片选引脚),此时master的驱动程序不需要提供set_cs函数,
  只需要提供默认的片选引脚pin_cs,详细参考:	 
 linux-5.8.5\arch\arm\plat-samsung\devs.c line:890
  zhangjiaqi\linux-5.8.5\drivers\spi\spi-s3c24xx.c    line:566~580

spi_master驱动:两个核心参数:设备驱动层创建的spi_device* spi,设备驱动层创建的message(message里面包含有设备驱动层(spi_driver)设置的要收发的数据spi_transfer),
spi_master的功能就是将设备驱动层spi_driver包装好的多个数据message收发出去,这多个message是放在一个message链表里面。

			spi_message:一个spi_message表示一个不可以打断的发送过程,里面包含的transfer可以有一个或者多个,这些transfer是连续的,即同一个片选信号的情况下
			             完成传输。
			
			一次spi_write -> 一个message -> n个transfer -> 每个transfer有len字节数据 -> 依次将每个transfer的每一字节数据发送出去 -> 每次中断发8bit

设备驱动层调用底层驱动去收发数据的过程:
spi_write_then_read
spi_sync(spi, &message)
__spi_sync(spi, message)
status = spi_async_locked(spi, message); //核心
__spi_async(spi, message);
ctlr->transfer(spi, message);//调用底层spi_control的transfer函数,传递进去的两个参数分别是设备驱动创建的spi_device,spi_write或者spi_read
为每次数据传输而建立的message,里面包含此次传输的spi_transfer(一个或者多个);

分配spi_master的函数:spi_alloc_master(struct device *host,unsigned int size),这个函数会返回一个spi_control指针,这个指针所指向的内存大小为(sizeof(spi_control)+size(第二个参数))
第一个参数为设备数生成的platform_device->dev的指针,第二个参数为任意指定的priv_data函数的大小可以为null

1.带操作系统的需要打开时钟
2.读激励也是要查询是否正忙
3.w25q16的烧写每次最多只能256字节的烧写,256字节256字节地烧写,注意不可跨页操作。
4.每调用一次spi_write,spi_master总线驱动会片选一次
5.在board-nokia770.c中注册spi device时(单板相关的文件):
static struct spi_board_info nokia770_spi_board_info[] __initdata = {
[0] = {
.modalias = “lcd_mipid”,
.bus_num = 2,
.chip_select = 3, //这里的片选引脚是标号3,他们的片选管脚是集成在spi_controller的某个寄存器的,这个3表示是使用这个寄存器的第3个io口作为片选引脚
//而不是某个简单的io口(gpg2,gpg3这些);
.max_speed_hz = 12000000,
.platform_data = &nokia770_mipid_platform_data,
},
[1] = {
.modalias = “ads7846”,
.bus_num = 2,
.chip_select = 0,
.max_speed_hz = 2500000,
.platform_data = &nokia770_ads7846_platform_data,
},
};
6.调试总结:
1.总线驱动卡住probe函数不能往下走,没反应,解决办法:读写函数__raw_write,__raw_read没做好,目标寄存器偏移值没做好。
2.不能正确读取出id值:原因片选函数没有实现正真的片选,新的内核不能直接用gpio_set_pin(S3C2410_GPG(2),0)这个函数来实现片选了,
在新的内核里spi_device传过来的只是一个gpio标号比如2或者3(不再是S3C2410_GPG(2)),新的内核spi_controller的寄存器是跟spi的所有可用
cs引脚放在一起。cs引脚不再是开发者随便找一个gpio口充当,而是spi_controller register里有一排cs口可供选择。
3.使用spi_transfer和spi_message来传输数据时,忘记spi_message_init(&message);导致不能正确写数据。
4.测试spi_flash的设备驱动:可以读写出正确的flash id号,并且可以正确mkfs.vfat 这个spi_flash
mkfs.vfat /dev/mtdblock4
mount -t vfat /dev/mtdblock4 /mnt 会提示:
mount: mounting /dev/mtdblock4 on myshare failed: Invalid argument

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嵌入式Linux SPI驱动是用于控制嵌入式系统中SPI(串行外设接口)设备的软件模块。SPI接口是一种全双工的串行通信协议,用于连接微控制器和外部设备,如传感器、存储器和显示器等。在嵌入式系统中,SPI驱动的作用是实现Linux操作系统与SPI设备之间的通信。这种通信可以通过硬件SPI控制器完成,也可以通过软件SPI设备模拟实现。 嵌入式Linux SPI驱动通常由以下几部分组成:SPI核心框架、SPI设备驱动SPI控制器驱动SPI核心框架提供了SPI总线的抽象,在Linux内核中实现了SPI总线的注册、协议处理和设备管理等功能。SPI设备驱动则负责具体的SPI设备通信协议的实现,例如读写数据、配置寄存器等。SPI控制器驱动则负责操控硬件SPI控制器的行为,例如设置时钟频率、数据位宽、传输模式等。在嵌入式系统中,这些驱动模块需要编译进内核或作为模块加载到内核中。 为了实现嵌入式Linux SPI驱动,开发者需要首先了解目标嵌入式系统的硬件架构和SPI设备的技术规范,然后编写相应的SPI设备驱动SPI控制器驱动,并将其与SPI核心框架进行适配。在开发过程中,开发者需要考虑通信稳定性、性能优化、资源管理等问题,确保SPI设备能够与Linux操作系统正常通信。 总的来说,嵌入式Linux SPI驱动是嵌入式系统中非常重要的一部分,它为Linux操作系统提供了与SPI设备进行高效通信的能力,使得嵌入式系统能够更好地与外部设备进行数据交换和控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值