测试程序,通过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的拉高动作;能够正常响应
