SPI主控的CS引发的读不到设备寄存器

测试程序,通过spidev节点来访问设备的寄存器;比如读取0x30寄存器获取设备的状态,0x30是按mode3发送出来了的

但是miso没反馈,实际上也没波形;所以确定主控支持的时钟频率;和设备支持的时钟频率---是否匹配;然后做全双工的回环测试,是ok的,证明主机功能正常

复制代码
/*
 * SPI testing utility (using spidev driver)
 * for example: insmod /usr/lib/modules/5.15.104/kernel/driver/spi/spidev.ko
 */
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define device   "/dev/spidev7.0"

typedef enum
{
    SPIMODE0  =   SPI_MODE_0,
    SPIMODE1  =   SPI_MODE_1,
    SPIMODE2  =   SPI_MODE_2,
    SPIMODE3  =   SPI_MODE_3,
}SPI_MODE;

/**************************************************************************

SPI default support frequency

**************************************************************************/
typedef enum
{
    S_960K  =  960000,
    S_4_8M  =  4800000,
    S_9_6M  =  9600000,
    S_16M  =  16000000,
    S_19_2M  =  19200000,
    S_25M  =  25000000,
    S_50M  =  50000000,
}SPI_SPEED;


static uint8_t spi_bits = 8;
static uint32_t  spi_speed = 10000000;
static uint16_t spi_delay;

/**************************************************************************
dev_name:need to insmod spidev.ko
mode:the default is mode0
    CPOL:when spi work in idles,the level of sclk, 1:HIGH,0:LOW
    CHPA:sample on the first few edges of sclk     0:the first edge 1:the second edge
bits:bits of word
speed:the default support spi frequency
**************************************************************************/

int spi_init(char *dev_name,SPI_MODE mode,uint8_t bits,SPI_SPEED speed)
{
    int ret = -1;
    int fd_spi = open(dev_name, O_RDWR);

    printf("< open(%s, O_RDWR)=%d >\n", dev_name, fd_spi);
    if (fd_spi < 0)
    {
        printf("< Fail to open spi >\n");
        return -1;
    }
    /*
    * spi mode
    */
    ret = ioctl(fd_spi, SPI_IOC_WR_MODE32, &mode);
    if (ret == -1)
    {
        perror("can't set spi mode");
        close(fd_spi);
        return ret;
    }
    /*
     * bits per word
     */
    spi_bits = bits;
    ret = ioctl(fd_spi, SPI_IOC_WR_BITS_PER_WORD, &spi_bits);
    if (ret == -1)
    {
        perror("can't set bits per word");
        close(fd_spi);
        return ret;
    }

    /*
    * speed
    */
    spi_speed = (uint32_t)speed;
    ret = ioctl(fd_spi, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);
    if (ret == -1)
    {
        perror("can't set max speed hz");
        close(fd_spi);
        return ret;
    }
    printf("spi mode:0x%x\n", mode);
    printf("bits per word: %d\n", spi_bits);
    printf("max speed    : %d Hz (%d KHz)\n", spi_speed, spi_speed/1000);

    return fd_spi;
}

收发短接,全双工
int spi_write_read(int fd,uint8_t* write_buf,uint8_t* read_buf,uint32_t len)
{
    int ret;
    struct spi_ioc_transfer tr = {
    .tx_buf = (unsigned long)write_buf,
    .rx_buf = (unsigned long)read_buf,
    .len = len,
    .delay_usecs = spi_delay,
    .speed_hz = spi_speed,
    .bits_per_word = spi_bits,
    };
    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret < 0)
    {
        perror("can't send spi message");
        return ret;
    }

    return ret;
}

int spi_deinit(int fd)
{
    close(fd);
    return 0;
}


int main(int argc, char *argv[])
{
    int fd;
    int i;
    uint8_t  writebuf[1024];
    uint8_t  readbuf[1024];

    fd = spi_init(device,SPIMODE3,8,S_19_2M);

    for(i = 0 ;i < 1024;i++)
        writebuf[i] = i%256;

    spi_write_read(fd,writebuf,readbuf,1024);

    for (i = 0; i<1024; i++) {
        if (!(i % 32))
        puts("");
        printf("%.2X ", readbuf[i]);
    }
    puts("");

    spi_deinit(fd);
    return  0 ;
}

测试结果是ok的

继续逻辑分析仪抓所有线路:fae反馈第一个时钟(发送0x30命令之后),第二个时钟(读取ta100的状态) 的过程CS不该拉高;通用的driver/spi/spi.c 中的 spi_transfer_one_message 对cs_change的处理,该主控的spi控制器驱动做了其他处理

然后通过spi_ioc_transfer数组,让一次msg有两个transfer,能保证时序,读到寄存器的值

复制代码
先发寄存器地址0x30,再读寄存器值
/*
 * SPI testing utility (using spidev driver)
 * for example: insmod /usr/lib/modules/5.15.104/kernel/driver/spi/spidev.ko
 */
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define device   "/dev/spidev7.0"

typedef enum
{
    SPIMODE0  =   SPI_MODE_0,
    SPIMODE1  =   SPI_MODE_1,
    SPIMODE2  =   SPI_MODE_2,
    SPIMODE3  =   SPI_MODE_3,
}SPI_MODE;

/**************************************************************************

SPI default support frequency

**************************************************************************/
typedef enum
{
    S_960K  =  960000,
    S_4_8M  =  4800000,
    S_9_6M  =  9600000,
    S_16M  =  16000000,
    S_19_2M  =  19200000,
    S_25M  =  25000000,
    S_50M  =  50000000,
}SPI_SPEED;


static uint8_t spi_bits = 8;
static uint32_t  spi_speed = 10000000;
static uint16_t spi_delay;

/**************************************************************************
dev_name:need to insmod spidev.ko
mode:the default is mode0
    CPOL:when spi work in idles,the level of sclk, 1:HIGH,0:LOW
    CHPA:sample on the first few edges of sclk     0:the first edge 1:the second edge
bits:bits of word
speed:the default support spi frequency
**************************************************************************/

int spi_init(char *dev_name,SPI_MODE mode,uint8_t bits,SPI_SPEED speed)
{
    int ret = -1;
    int fd_spi = open(dev_name, O_RDWR);

    printf("< open(%s, O_RDWR)=%d >\n", dev_name, fd_spi);
    if (fd_spi < 0)
    {
        printf("< Fail to open spi >\n");
        return -1;
    }
    /*
    * spi mode
    */
    ret = ioctl(fd_spi, SPI_IOC_WR_MODE32, &mode);
    if (ret == -1)
    {
        perror("can't set spi mode");
        close(fd_spi);
        return ret;
    }
    /*
     * bits per word
     */
    spi_bits = bits;
    ret = ioctl(fd_spi, SPI_IOC_WR_BITS_PER_WORD, &spi_bits);
    if (ret == -1)
    {
        perror("can't set bits per word");
        close(fd_spi);
        return ret;
    }

    /*
    * speed
    */
    spi_speed = (uint32_t)speed;
    ret = ioctl(fd_spi, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);
    if (ret == -1)
    {
        perror("can't set max speed hz");
        close(fd_spi);
        return ret;
    }
    printf("spi mode:0x%x\n", mode);
    printf("bits per word: %d\n", spi_bits);
    printf("max speed    : %d Hz (%d KHz)\n", spi_speed, spi_speed/1000);

    return fd_spi;
}
通过spi_ioc_transfer数组,让一次msg有两个transfer
int ql_spi_write_read(int fd,uint8_t* write_buf,uint8_t* read_buf,uint32_t len)
{
    int ret;
    struct spi_ioc_transfer tr[2] = {
    {
    .tx_buf = (unsigned long)write_buf,
    .rx_buf = 0,
    .len = len,
    .delay_usecs = spi_delay,
    .speed_hz = spi_speed,
    .bits_per_word = spi_bits,
    .cs_change = 0,
    },
    {
    .tx_buf = 0,
    .rx_buf = (unsigned long)read_buf,
    .len = len,
    .delay_usecs = spi_delay,
    .speed_hz = spi_speed,
    .bits_per_word = spi_bits,
    .cs_change = 0,
    },
    };
    ret = ioctl(fd, SPI_IOC_MESSAGE(2), tr);
    if (ret < 0)
    {
        perror("can't send spi message");
        return ret;
    }

    return ret;
}

int spi_deinit(int fd)
{
    close(fd);
    return 0;
}


int main(int argc, char *argv[])
{
    int fd;
    
    uint8_t  writebuf[1];
    uint8_t  readbuf[1];

    fd = spi_init(device,SPIMODE3,8,S_16M);

    writebuf[0] = 0x30;

    spi_write_read(fd,writebuf,readbuf,1);

    printf("%.2X ", readbuf[0]);
   
    puts("");

    spi_deinit(fd);
    return  0 ;
}

里面对cs_change做了判断,一次msg的传输链表的最后一个spi_transfer的cs_change是1,就保持cs ;非最后一个spi_transfer的cs_change是1,就改变cs,是0就保持cs

复制代码
static int spi_transfer_one_message(struct spi_controller *ctlr,
				    struct spi_message *msg)
{
	struct spi_transfer *xfer;
	bool keep_cs = false;
	int ret = 0;
	struct spi_statistics *statm = &ctlr->statistics;
	struct spi_statistics *stats = &msg->spi->statistics;

	spi_set_cs(msg->spi, true, false);

	SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
	SPI_STATISTICS_INCREMENT_FIELD(stats, messages);

	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
		trace_spi_transfer_start(msg, xfer);

		spi_statistics_add_transfer_stats(statm, xfer, ctlr);
		spi_statistics_add_transfer_stats(stats, xfer, ctlr);

		if (!ctlr->ptp_sts_supported) {
			xfer->ptp_sts_word_pre = 0;
			ptp_read_system_prets(xfer->ptp_sts);
		}

		if ((xfer->tx_buf || xfer->rx_buf) && xfer->len) {
			reinit_completion(&ctlr->xfer_completion);

fallback_pio:
			ret = ctlr->transfer_one(ctlr, msg->spi, xfer);
			if (ret < 0) {
				if (ctlr->cur_msg_mapped &&
				   (xfer->error & SPI_TRANS_FAIL_NO_START)) {
					__spi_unmap_msg(ctlr, msg);
					ctlr->fallback = true;
					xfer->error &= ~SPI_TRANS_FAIL_NO_START;
					goto fallback_pio;
				}

				SPI_STATISTICS_INCREMENT_FIELD(statm,
							       errors);
				SPI_STATISTICS_INCREMENT_FIELD(stats,
							       errors);
				dev_err(&msg->spi->dev,
					"SPI transfer failed: %d\n", ret);
				goto out;
			}

			if (ret > 0) {
				ret = spi_transfer_wait(ctlr, msg, xfer);
				if (ret < 0)
					msg->status = ret;
			}
		} else {
			if (xfer->len)
				dev_err(&msg->spi->dev,
					"Bufferless transfer has length %u\n",
					xfer->len);
		}

		if (!ctlr->ptp_sts_supported) {
			ptp_read_system_postts(xfer->ptp_sts);
			xfer->ptp_sts_word_post = xfer->len;
		}

		trace_spi_transfer_stop(msg, xfer);

		if (msg->status != -EINPROGRESS)
			goto out;

		spi_transfer_delay_exec(xfer);

		if (xfer->cs_change) {
			if (list_is_last(&xfer->transfer_list,
					 &msg->transfers)) {
				keep_cs = true;
			} else {
				spi_set_cs(msg->spi, false, false);
				_spi_transfer_cs_change_delay(msg, xfer);
				spi_set_cs(msg->spi, true, false);
			}
		}

		msg->actual_length += xfer->len;
	}

out:
	if (ret != 0 || !keep_cs)
		spi_set_cs(msg->spi, false, false);

	if (msg->status == -EINPROGRESS)
		msg->status = ret;

	if (msg->status && ctlr->handle_err)
		ctlr->handle_err(ctlr, msg);

	spi_finalize_current_message(ctlr);

	return ret;
}

但是设备的库都是一次message只包含一个spi_transfer,那么继续:

更改设备树:去掉spi节点的pinctrl的cs属性,让其不受驱动控制;幸运的是屏蔽掉cs的spi功能,确实让cs没受到影响;通过sysfs :cs直接拉低;每个数据都返回0x02;推测:一次收发结束,还是要拉搞,结束这次行为,不然后续再发什么都还是返回02

在测试程序,加上对cs的拉高动作;能够正常响应

相关推荐
平凡灵感码头3 小时前
STM32 程序内存分布详解
stm32·单片机·嵌入式硬件
QUST-Learn3D3 小时前
C++单头文件实现windows进程间通信(基于命名管道)
c++·windows·单片机
btzhy4 小时前
STM32单片机:基本定时器应用:精确定时(STM32L4xx)
stm32·单片机·嵌入式硬件·基本定时器应用:精确定时
云山工作室10 小时前
基于单片机智能水产养殖系统设计(论文+源码)
单片机·嵌入式硬件·毕业设计·毕设
xiaomin201712 小时前
【STM32 HAL库】高级定时器TIM8输出PWM
stm32·单片机·嵌入式硬件
沐欣工作室_lvyiyi13 小时前
基于单片机的小型农业气象监测系统(论文+源码)
单片机·嵌入式硬件·物联网·毕业设计·气象监测
飞猿_SIR14 小时前
基于海思Hi3798MV200Android7.0聊聊HDMI色深模式和电视HDR
android·嵌入式硬件·音视频
范纹杉想快点毕业15 小时前
单片机开发中的队列数据结构详解,队列数据结构在单片机软件开发中的应用详解,C语言
c语言·数据库·stm32·单片机·嵌入式硬件·mongodb·fpga开发
三佛科技-1341638421215 小时前
鼻毛修剪器MCU方案开发设计
单片机·嵌入式硬件·智能家居·pcb工艺