基于openwrt-MT7688该死的全双工驱动函数--连接LORA 芯片

这个玩意开始单纯的以为默认支持所有的工作模式,都知道它有两个片选,默认CS0是用作flash不作变动,我们操作就只有操作CS1, 先前一顿折腾都没出来,用自带的SPI-TEST函数去测试,一直报错无效参数无效参数,人都麻了。后来无可奈何,转到内核中去添加打印信息一步一步找错看错在那里,(注意:添加内核模块看打印信息,在内核配置菜单中选为M,我开始一直是*,弄半天没打印信息),后来打印纠错发现是默认MT7688中他喵的只支持半双工工作模式,最离谱的是我去问商家售后技术,他还说支持全双工,我把驱动函数发出去,他哑口无言,说需要自己配置修改,我就开始了我的死磕之路。今天刚弄出来,太开心了,具体一些后续补充,先上我的核心代码。

spi半双工的代码在openwrt-21.02/build_dir/target-mipsel_24kc_musl/linux-ramips_mt76x8/linux-5.4.171/drivers/spi目录下的 spi-mt7621.c,每人的板子及源码可能不同需要自己寻找,它原先自带的主要看 transfer_one_message函数(自带的):

static int mt7621_spi_transfer_one_message(struct spi_controller *master,
					   struct spi_message *m)
{
	struct mt7621_spi *rs = spi_controller_get_devdata(master);
	struct spi_device *spi = m->spi;
	unsigned int speed = spi->max_speed_hz;
	struct spi_transfer *t = NULL;
	int status = 0;

	mt7621_spi_wait_till_ready(rs);

	list_for_each_entry(t, &m->transfers, transfer_list)
		if (t->speed_hz < speed)
			speed = t->speed_hz;

	if (mt7621_spi_prepare(spi, speed)) {
		status = -EIO;
		goto msg_done;
	}

	/* Assert CS */
	mt7621_spi_set_cs(spi, 1);

	m->actual_length = 0;
	list_for_each_entry(t, &m->transfers, transfer_list) {
		if ((t->rx_buf) && (t->tx_buf)) {
			/*
			 * This controller will shift some extra data out
			 * of spi_opcode if (mosi_bit_cnt > 0) &&
			 * (cmd_bit_cnt == 0). So the claimed full-duplex
			 * support is broken since we have no way to read
			 * the MISO value during that bit.
			 */
			status = -EIO;
			goto msg_done;
		} else if (t->rx_buf) {
			mt7621_spi_read_half_duplex(rs, t->len, t->rx_buf);
		} else if (t->tx_buf) {
			mt7621_spi_write_half_duplex(rs, t->len, t->tx_buf);
		}
		m->actual_length += t->len;
	}

	/* Flush data and deassert CS */
	mt7621_spi_flush(rs);
	mt7621_spi_set_cs(spi, 0);

msg_done:
	m->status = status;
	spi_finalize_current_message(master);

	return 0;
}

上图代码就是自带的半双工,用来操作flash的CSO,但是这个函数 static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
                       struct spi_message *m) 是spi发送传递信息的关键,所以把这个函数更改为half_duplex,如下图:

static int mt7621_spi_transfer_one_message(struct spi_master *master,
					   struct spi_message *m)
{
	struct spi_device *spi = m->spi;
	int cs = spi->chip_select;
	if(cs == 0){
		printk("-----> cs = 0 00000<******\n");
		return mt7621_spi_transfer_half_duplex(master, m);	//可执行half
	}
	else if(cs == 1){
		printk("-----> cs = 1 11111<-----\n"); //调试时的信息,看是否片选为CS1
		return mt7621_spi_transfer_full_duplex(master, m);
	}
	
	return 0;
}

此时就需要添加full_duplex的函数了,此函数原型参考:https://blog.csdn.net/zzZhangYiLong/article/details/120697009:,

#define ONETXRX 16

static void mt7621_spi_reset_full(struct mt7621_spi *rs)
{
	u32 master = mt7621_spi_read(rs, MT7621_SPI_MASTER);
 
	master |= 7 << 29;
	master |= 1 << 2;
	//master &= ~(1 << 10);
        master |= (1 << 10);	//spi MASTER 模式寄存器第十位 置1,
 
	mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
}

static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
					   struct spi_message *m)
{
	struct mt7621_spi *rs = spi_master_get_devdata(master);
	struct spi_device *spi = m->spi;
	unsigned int speed = spi->max_speed_hz;
	struct spi_transfer *t = NULL;
	int status = 0;
	int i, j,k,len = 0;
    	int total=0;      //add 
	int rx_len = 0;
	u32 txdata[64] = { 0 };
    	u32 rxdata[64] = { 0 };
	u32 val = 0;

	mt7621_spi_reset_full(rs);
	mt7621_spi_wait_till_ready(rs);

	printk(">>>>> mt7621_spi_transfer_full_duplex <<<<<\n");
	
    list_for_each_entry(t, &m->transfers, transfer_list)
    {
        const u8 *buf = t->tx_buf;

        if (t->rx_buf)
            rx_len += t->len;

		if (!buf)
			continue;
        
        for (i = 0; i < t->len; i++, len++)
			txdata[len / 4] |= buf[i] << (8 * (len & 3));
		if (speed > t->speed_hz)
			speed = t->speed_hz;

    }

	if (mt7621_spi_prepare(spi, speed)) {
		status = -EIO;
		goto msg_done; 
	}

    total=len;
    while(total>ONETXRX)
    {
        for (i = 0;i < ONETXRX; i += 4,j += 4)
		    mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, txdata[j / 4]);

        val |= ONETXRX * 8;
        val |= (ONETXRX * 8) << 12;
        mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);

		mt7621_spi_write_half_duplex(rs, t->len, t->rx_buf);   ///********************/
			
        mt7621_spi_set_cs(spi, 1);
		mt7621_spi_reset_full(rs); //add   每个地方都加了这个函数后才能正常操作

        val = mt7621_spi_read(rs, MT7621_SPI_TRANS);

		mt7621_spi_read_half_duplex(rs, t->len, t->tx_buf);		// ********************

		
        val |= SPI_CTL_START;
        mt7621_spi_write(rs, MT7621_SPI_TRANS, val);

        mt7621_spi_wait_till_ready(rs);

        mt7621_spi_set_cs(spi, 0);
		mt7621_spi_reset_full(rs);	//add

        for (i = 0; i < ONETXRX; i += 4,k+=4)
		    rxdata[k / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA4 + i);
		
        total-=ONETXRX;
    }
    if(total>0)
    {
        for (i = 0;i < total; i += 4,j += 4)
		    mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, txdata[j / 4]);

        val |= total * 8;
        val |= (total * 8) << 12;
        mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);

        mt7621_spi_set_cs(spi, 1);
		mt7621_spi_reset_full(rs);	//add

        val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
        val |= SPI_CTL_START;
        mt7621_spi_write(rs, MT7621_SPI_TRANS, val);

        mt7621_spi_wait_till_ready(rs);

        mt7621_spi_set_cs(spi, 0);
		mt7621_spi_reset_full(rs);	//add

        for (i = 0; i < total; i += 4,k+=4)
		    rxdata[k / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA4 + i);
    }
    m->actual_length = rx_len;
    
    len = 0;
	list_for_each_entry(t, &m->transfers, transfer_list) {
		u8 *buf = t->rx_buf;

		if (!buf)
			continue;

		for (i = 0; i < t->len; i++, len++)
			buf[i] = rxdata[len / 4] >> (8 * (len & 3));
	}

msg_done:
	m->status = status;
	spi_finalize_current_message(master);

	return 0;

}

其中static void mt7621_spi_reset_full(struct mt7621_spi *rs) 这个函数个人觉得至关重要,他将master中的第10位置1了 , 可查看7688datesheet发现,关于半双工和全双工的描述在SPI_MASTER的BIT10,此处图片引用,自己难得去截数据手册的图嘞,MT7688 坑爹的 SPI Master 半双工全双工问题_WindLOR的博客-CSDN博客_mt7688 spi

将其置1后才是full duplex mode。突然想到还有一个spi.c中修改地方不确定,我是报错了所以修改后能正常运行:

static int __spi_validate(struct spi_device *spi, struct spi_message *message)
{
	struct spi_controller *ctlr = spi->controller;
	struct spi_transfer *xfer;
	int w_size;

	if (list_empty(&message->transfers)) {
		printk("---> message->transfers empty\n");
		return -EINVAL;
	}

	/* If an SPI controller does not support toggling the CS line on each
	 * transfer (indicated by the SPI_CS_WORD flag) or we are using a GPIO
	 * for the CS line, we can emulate the CS-per-word hardware function by
	 * splitting transfers into one-word transfers and ensuring that
	 * cs_change is set for each transfer.
	 */
	if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
					  spi->cs_gpiod ||
					  gpio_is_valid(spi->cs_gpio))) {
		size_t maxsize;
		int ret;

		maxsize = (spi->bits_per_word + 7) / 8;

		/* spi_split_transfers_maxsize() requires message->spi */
		message->spi = spi;

		ret = spi_split_transfers_maxsize(ctlr, message, maxsize,
						  GFP_KERNEL);
		printk("---> spi_split_transfers_maxsize = %d\n", ret);
		if (ret)
			return ret;

		list_for_each_entry(xfer, &message->transfers, transfer_list) {
			/* don't change cs_change on the last entry in the list */
			if (list_is_last(&xfer->transfer_list, &message->transfers))
				break;
			xfer->cs_change = 1;
		}
	}

	/* Half-duplex links include original MicroWire, and ones with
	 * only one data pin like SPI_3WIRE (switches direction) or where
	 * either MOSI or MISO is missing.  They can also be caused by
	 * software limitations.
	 */
	//printk("--> ctlr->flags=0x%x\n", ctlr->flags);
	if ((ctlr->flags & SPI_CONTROLLER_HALF_DUPLEX) ||
	    (spi->mode & SPI_3WIRE)) {
		unsigned flags = ctlr->flags;

		list_for_each_entry(xfer, &message->transfers, transfer_list) {
			/*if (xfer->rx_buf && xfer->tx_buf) {
				printk("--> xfer->rx_buf && xfer->tx_buf\n");
				return -EINVAL;
			}*/   我运行时这里会报错,屏蔽后执行全双工没问题
			if ((flags & SPI_CONTROLLER_NO_TX) && xfer->tx_buf) {
				printk("--> (flags & SPI_CONTROLLER_NO_TX) && xfer->tx_buf\n");
				return -EINVAL;
			}
			if ((flags & SPI_CONTROLLER_NO_RX) && xfer->rx_buf) {
				printk("--> (flags & SPI_CONTROLLER_NO_RX) && xfer->rx_buf\n");
				return -EINVAL;
			}
		}
	}

至此,大致的一个修改步骤及内容到此结束,期间真的花费了很多时间和精力,如果有人也在调试openwrt和lora设备的,会发现网上的资源真的很少很少,找到也不全,所以自己有成功的案例分享出来仅供参考,希望能给大家提供一些帮助,我大部分的参考博客即为文字两个链接,内网外网找一堆,就这两个有用,最终也是自己综合起来也算死磕出来了,因为自己说实话是根本不咋懂驱动函数的,第一次做这个东西。

        今天刚调试出结果就想着分享出来给大家,自己知道这个有多烦人,可能有些细节没写到,后面会完善了,不过关键核心代码我是贴出了,如果有帮助,希望不仅浏览,再点个赞,给我一点鼓励,如果那里写的有问题欢迎评论探讨指教,谢谢大家~!

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值