2026-06-24 17:31:17 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* spi_slave_recv_loop.c - SPI从设备持续接收程序
|
|
|
|
|
|
*
|
|
|
|
|
|
* 功能:SoC作为SPI从设备,配置与MCU主设备对齐,循环接收并打印数据
|
|
|
|
|
|
* MCU每秒发送一帧(8字节)
|
|
|
|
|
|
*
|
|
|
|
|
|
* MCU主设备配置(需对齐):
|
|
|
|
|
|
* Mode : SPI_MODE_0 (CPOL=0, CPHA=0)
|
|
|
|
|
|
* Bits/Word : 8
|
|
|
|
|
|
* Speed : 1 MHz
|
|
|
|
|
|
* 发送数据 : {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
|
|
|
|
|
|
*
|
|
|
|
|
|
* 编译:aarch64-linux-gnu-gcc -o spi_slave_recv_loop spi_slave_recv_loop.c
|
|
|
|
|
|
* 运行:./spi_slave_recv_loop
|
|
|
|
|
|
* 退出:按 Ctrl+C
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
#include <linux/spi/spidev.h>
|
|
|
|
|
|
|
|
|
|
|
|
/* ========== SPI配置(必须与MCU主设备完全一致)========== */
|
|
|
|
|
|
#define SPI_DEVICE "/dev/spidev4.0"
|
2026-07-02 14:38:23 +08:00
|
|
|
|
#define SPI_MODE SPI_MODE_2 /* CPOL=1, CPHA=1 */
|
2026-06-24 17:31:17 +08:00
|
|
|
|
#define SPI_BITS 8
|
|
|
|
|
|
#define SPI_SPEED 1000000 /* 1MHz */
|
2026-07-02 14:38:23 +08:00
|
|
|
|
#define FRAME_LEN 128 /* 每帧256字节 */
|
2026-06-24 17:31:17 +08:00
|
|
|
|
/* ===================================================== */
|
|
|
|
|
|
|
|
|
|
|
|
static volatile int keep_running = 1;
|
|
|
|
|
|
|
2026-07-02 14:38:23 +08:00
|
|
|
|
static int is_all_zero(const uint8_t *buf, int len) {
|
|
|
|
|
|
for (int i = 0; i < len; i++)
|
|
|
|
|
|
if (buf[i] != 0)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-24 17:31:17 +08:00
|
|
|
|
void sigint_handler(int sig) {
|
|
|
|
|
|
(void)sig;
|
|
|
|
|
|
keep_running = 0;
|
|
|
|
|
|
printf("\n[INFO] User interrupted, exiting...\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main(void)
|
|
|
|
|
|
{
|
2026-07-02 14:38:23 +08:00
|
|
|
|
uint8_t rx_buf[FRAME_LEN];
|
|
|
|
|
|
uint8_t tx_buf[FRAME_LEN] = {0xAA, 0xBB, 0xCC, 0xDD};
|
|
|
|
|
|
tx_buf[FRAME_LEN-4] = 0x11;
|
|
|
|
|
|
tx_buf[FRAME_LEN-3] = 0x22;
|
|
|
|
|
|
tx_buf[FRAME_LEN-2] = 0x33;
|
|
|
|
|
|
tx_buf[FRAME_LEN-1] = 0x44;
|
|
|
|
|
|
|
2026-06-24 17:31:17 +08:00
|
|
|
|
int fd, ret;
|
|
|
|
|
|
unsigned int frame_count = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* 注册退出信号 */
|
|
|
|
|
|
signal(SIGINT, sigint_handler);
|
|
|
|
|
|
|
|
|
|
|
|
/* 1. 打开 SPI 设备 */
|
|
|
|
|
|
fd = open(SPI_DEVICE, O_RDWR);
|
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
|
perror("open " SPI_DEVICE);
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 2. 配置 SPI 模式 */
|
|
|
|
|
|
uint8_t mode = SPI_MODE;
|
|
|
|
|
|
mode |= SPI_LSB_FIRST;
|
|
|
|
|
|
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
|
|
|
|
|
|
if (ret == -1) {
|
|
|
|
|
|
perror("SPI_IOC_WR_MODE");
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 3. 配置每字位数 */
|
|
|
|
|
|
uint8_t bits = SPI_BITS;
|
|
|
|
|
|
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
|
|
|
|
|
|
if (ret == -1) {
|
|
|
|
|
|
perror("SPI_IOC_WR_BITS_PER_WORD");
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 4. 配置最大时钟频率 */
|
|
|
|
|
|
uint32_t speed = SPI_SPEED;
|
|
|
|
|
|
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
|
|
|
|
|
|
if (ret == -1) {
|
|
|
|
|
|
perror("SPI_IOC_WR_MAX_SPEED_HZ");
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 5. 回读验证配置 */
|
|
|
|
|
|
ioctl(fd, SPI_IOC_RD_MODE, &mode);
|
|
|
|
|
|
ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
|
|
|
|
|
|
ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
|
|
|
|
|
|
|
|
|
|
|
|
printf("========================================\n");
|
|
|
|
|
|
printf("SPI Slave Config (aligned with MCU)\n");
|
|
|
|
|
|
printf("========================================\n");
|
|
|
|
|
|
printf("Device : %s\n", SPI_DEVICE);
|
|
|
|
|
|
printf("Mode : %d (CPOL=%d, CPHA=%d)\n",
|
|
|
|
|
|
mode, (mode & SPI_CPOL) ? 1 : 0, (mode & SPI_CPHA) ? 1 : 0);
|
|
|
|
|
|
printf("Bits/Word : %d\n", bits);
|
|
|
|
|
|
printf("Max Speed : %d Hz\n", speed);
|
|
|
|
|
|
printf("Frame length: %d bytes\n", FRAME_LEN);
|
|
|
|
|
|
printf("========================================\n");
|
|
|
|
|
|
printf("Waiting for MCU (master) to send data...\n");
|
|
|
|
|
|
printf("Press Ctrl+C to stop.\n\n");
|
|
|
|
|
|
|
|
|
|
|
|
/* 6. 循环接收数据 */
|
|
|
|
|
|
while (keep_running) {
|
2026-07-02 14:38:23 +08:00
|
|
|
|
memset(rx_buf, 0, FRAME_LEN);
|
|
|
|
|
|
|
|
|
|
|
|
struct spi_ioc_transfer tr = {
|
|
|
|
|
|
.tx_buf = (unsigned long)tx_buf,
|
|
|
|
|
|
.rx_buf = (unsigned long)rx_buf,
|
|
|
|
|
|
.len = FRAME_LEN,
|
|
|
|
|
|
.speed_hz = SPI_SPEED,
|
|
|
|
|
|
.bits_per_word = SPI_BITS,
|
|
|
|
|
|
};
|
|
|
|
|
|
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
|
2026-06-24 17:31:17 +08:00
|
|
|
|
if (ret < 0) {
|
2026-07-02 14:38:23 +08:00
|
|
|
|
perror("spi transfer");
|
2026-06-24 17:31:17 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2026-07-02 14:38:23 +08:00
|
|
|
|
ret = FRAME_LEN; /* transfer 成功则返回 FRAME_LEN */
|
2026-06-24 17:31:17 +08:00
|
|
|
|
|
|
|
|
|
|
frame_count++;
|
2026-07-02 14:38:23 +08:00
|
|
|
|
|
|
|
|
|
|
/* 只有 tx 或 rx 数据不全为零时才打印 */
|
|
|
|
|
|
if (is_all_zero(tx_buf, FRAME_LEN) && is_all_zero(rx_buf, FRAME_LEN))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2026-06-24 17:31:17 +08:00
|
|
|
|
printf("\n========== Frame #%u (%d bytes) ==========\n", frame_count, ret);
|
2026-07-02 14:38:23 +08:00
|
|
|
|
|
|
|
|
|
|
if (!is_all_zero(tx_buf, FRAME_LEN)) {
|
|
|
|
|
|
printf("TX HEX: ");
|
|
|
|
|
|
for (int i = 0; i < ret; i++)
|
|
|
|
|
|
printf("0x%02X ", tx_buf[i]);
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!is_all_zero(rx_buf, FRAME_LEN)) {
|
|
|
|
|
|
printf("RX HEX: ");
|
|
|
|
|
|
for (int i = 0; i < ret; i++)
|
|
|
|
|
|
printf("0x%02X ", rx_buf[i]);
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-24 17:31:17 +08:00
|
|
|
|
printf("=========================================\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|