imx6ull_SPI

SPI简介

SPI 是Motorola 公司推出的一种同步串行接口技术,是一种高速、全双工的同步通信总线。SPI 以主从方式工作,通常是有一个主设备和一个或多个从设备,一般SPI 需要4 根线,但是也可以使用三根线(单向传输)
这四根线如下:
①、CS/SS,Slave Select/Chip Select,这个是片选信号线,用于选择需要进行通信的从设备
I2C 主机是通过发送从机设备地址来选择需要进行通信的从机设备的,SPI 主机不需要发送从机
设备,直接将相应的从机设备片选信号拉低即可。

②、SCK,Serial Clock,串行时钟,和I2C 的SCL 一样,为SPI 通信提供时钟

③、MOSI/SDO,Master Out Slave In/Serial Data Output,简称主出从入信号线,这根数据线
只能用于主机向从机发送数据,也就是主机输出,从机输入。

④、MISO/SDI,Master In Slave Out/Serial Data Input,简称主入从出信号线,这根数据线只
能用户从机向主机发送数据,也就是主机输入,从机输出。
在这里插入图片描述

四种工作形式:通过串行时钟极性(CPOL)和相位(CPHA)的搭配

在这里插入图片描述
含义:CPOL:极性、CPHA:相位
如1:CPOL=1:高、CPHA=1:第二个相位。
高->低->高:第一个高->低为上升沿,第二个低->高·下降沿·
2:CPOL=1:高、CPHA=0:第一个相位。
高->低:第一个高->低为上升沿
①、CPOL=1,串行时钟空闲状态为高电平,CPHA=1,串行时钟的第二个跳变沿下降沿采集数据。
②、CPOL=1,串行时钟空闲状态为高电平,CPHA=0,串行时钟的第一个跳变沿下降沿采集数据。
③、CPOL=0,串行时钟空闲状态为低电平,CPHA=1,串行时钟的第二个跳变沿下降沿采集数据。
④、CPOL=0,串行时钟空闲状态为低电平,CPHA=0,串行时钟的第一个跳变沿上升沿采集数据。

SPI驱动

spi_imx.c

先找到spi_imx_driver, 这是imx在linux内核基础上写的

static struct platform_driver spi_imx_driver = {
	.driver = {
		   .name = DRIVER_NAME,
		   .of_match_table = spi_imx_dt_ids,//设备树中匹配
		   .pm = IMX_SPI_PM,
	},
	.id_table = spi_imx_devtype,//无设备树匹配
	.probe = spi_imx_probe,
	.remove = spi_imx_remove,
};

spi_imx_probe

static int spi_imx_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	const struct of_device_id *of_id =
			of_match_device(spi_imx_dt_ids, &pdev->dev);
	struct spi_imx_master *mxc_platform_info =
			dev_get_platdata(&pdev->dev);
	struct spi_master *master;
	struct spi_imx_data *spi_imx;
	struct resource *res;
	int i, ret, num_cs, irq;

	if (!np && !mxc_platform_info) {
		dev_err(&pdev->dev, "can't get the platform data\n");
		return -EINVAL;
	}

	ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
	if (ret < 0) {
		if (mxc_platform_info)
			num_cs = mxc_platform_info->num_chipselect;
		else
			return ret;
	}

	master = spi_alloc_master(&pdev->dev,
			sizeof(struct spi_imx_data) + sizeof(int) * num_cs);
	if (!master)
		return -ENOMEM;

	platform_set_drvdata(pdev, master);

	master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
	master->bus_num = pdev->id;
	master->num_chipselect = num_cs;

	spi_imx = spi_master_get_devdata(master);
	spi_imx->bitbang.master = master;

	for (i = 0; i < master->num_chipselect; i++) {
		int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
		if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
			cs_gpio = mxc_platform_info->chipselect[i];

		spi_imx->chipselect[i] = cs_gpio;
		if (!gpio_is_valid(cs_gpio))
			continue;

		ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i],
					DRIVER_NAME);
		if (ret) {
			dev_err(&pdev->dev, "can't get cs gpios\n");
			goto out_master_put;
		}
	}

这上部分都是platform_device 的基本操作

下部分出现bitbang为关键,bitbang的作用是GPIO口模仿SPI模式,其他的为驱动正常操作

SPI发送:

spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;
		spi_imx->rx = spi_imx_buf_rx_u8;
		spi_imx->tx = spi_imx_buf_tx_u8;

spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
bitbang下的txrx_bufs,函数为 spi_imx_transfer

spi_imx_transfer  
	->spi_imx_pio_transfer
		->spi_imx_push->spi_imx
			->tx(spi_imx);

SPI接收
中断函数接收数据

spi_imx_isr
	->spi_imx->rx(spi_imx);

bitbang启动

spi_bitbang_start(struct spi_bitbang *bitbang)
master->transfer_one_message = spi_bitbang_transfer_one;
spi_bitbang_transfer_one
	->bitbang->txrx_bufs(spi, t)=spi_imx_transfer
									->->tx(spi_imx)=spi_imx_buf_tx_u8

SPI设备驱动

发送

定义一个spi_transfer
对下面3个成员赋值

	struct spi_transfer t[] = {
		{
			.tx_buf = tx_buff,
			.rx_buf = rx_buff,
			.len = len,
	};

定义一个 spi_message

	spi_message_init(&spi_message);//初始化
	spi_message_add_tail(&spi_transfer , &spi_message);//将spi_transfer 打包进spi_message

选择设备,选择发送方式(同步,异步)
同步 :spi_sync(spi_device, &spi_message);
异步:spi_async(spi_device, &spi_message);

读和写只需要一个spi_transfer 就行了

tx_buf 放寄存器地址
rx_buf 写要存放信息的地址
t->len =1(发送的数据:寄存器大小)+len(读取的数据:此表示接收信息的大小)

	unsigned char txdata[1];
	struct spi_message m;
	struct spi_transfer *t;
	/* 一共发送len+1个字节的数据,第一个字节为
	寄存器首地址,一共要读取len个字节长度的数据,*/
	txdata[0] = reg | 0x80;		/* 写数据的时候首寄存器地址bit8要置1 */			
	t->tx_buf = txdata;			/* 要发送的数据 */
    t->rx_buf = rxdata;			/* 要读取的数据 */
	t->len = len+1;				/* t->len=发送的长度+读取的长度 */
	spi_message_init(&m);		/* 初始化spi_message */
	spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
	ret = spi_sync(spi, &m);	/* 同步发送 */

SPI选片都是软选,发送数据前的片选信号拉低都是在发送一段spi_message 前拉低,发完就拉高
,现在操作不像裸机

txdata =sizeof(char)+len,寄存器地址+要写入的内容

	unsigned char *txdata;
	struct spi_message m;
	struct spi_transfer *t;
	txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
	/* 一共发送len+1个字节的数据,第一个字节为
	寄存器首地址,len为要写入的寄存器的集合,*/
	*txdata = reg & ~0x80;	/* 写数据的时候首寄存器地址bit8要清零 */
    memcpy(txdata+1, buf, len);	/* 把len个寄存器拷贝到txdata里,等待发送 */
	t->tx_buf = txdata;			/* 要发送的数据 */
	t->len = len+1;				/* t->len=发送的长度+读取的长度 */
	spi_message_init(&m);		/* 初始化spi_message */
	spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
	ret = spi_sync(spi, &m);	/* 同步发送 */

读和写的 t->len = len+1; 这个1都是寄存器地址长度一个字节,len的长度为tx_buf+rx_buf ,tx_buf为寄存器地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值