前言
面对各种各样的客户需求,spi接口也是一种传码流的形式,spi同步422可以保证抗干扰能力强的同时传输距离也很长,本文会介绍海思平台spi作为主机的发送功能以及发送码流的处理方式
1. 管脚复用:
首先需要配置的肯定是管脚复用,以spi0为例
查看pinout表格
直接用命令行配一下即可
devmem 0x1f000068 32 0x14f1
devmem 0x1f00006c 32 0x14f1
devmem 0x1f000070 32 0x1401
devmem 0x1f000074 32 0x14f1
2. 寄存器定义
控制寄存器0有两个需要重点注意的,输出频率和数据位宽
FSSPCLK参考时钟100M,比如应用层配了5Mb的速率,SCR则为9,CPSDVSR为2
控制寄存器1可以看见spi的使能等信息,
状态寄存器可以看当前的运行情况
3.测速代码
/Hi3559AV100_SDK_V2.0.3.1/osdrv/opensource/kernel/linux-4.9.y_multi-core/tools/spi
海思的spi驱动使用的是pl022,直接在drivers/spi/下可以看到spi-pl022.o文件,对应驱动简单写一个demo,直接while1发送0x55,拿示波器就可以轻易看到时钟是否和配置一致,速率最快可以达到多少(虽然spi的传输是和时钟有关,比如5m时钟,最快即可一秒发送5M bit,但是在操作系统下时钟并不会连续的一直发,猜测是因为linux并不能直接读写寄存器,而是通过映射访问的物理地址,存在时间延迟)实际测速在不优化的情况下,while1发送的实际平均速率为2.5Mb每秒
/*
* SPI testing utility (using spidev driver)
*
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* Cross-compile with cross-gcc -I/path/to/cross-kernel/include
*/
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev0.0";
static uint32_t mode;
static uint8_t bits = 8;
static char *input_file;
static char *output_file;
static uint32_t speed = 5000000;
static uint16_t delay;
static int verbose;
// uint8_t default_tx[] = {
// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// 0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// 0xF0, 0x0D,
// };
uint8_t default_tx[] = {
0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55,
};
uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
char *input_tx;
static void hex_dump(const void *src, size_t length, size_t line_size,
char *prefix)
{
int i = 0;
const unsigned char *address = src;
const unsigned char *line = address;
unsigned char c;
printf("%s | ", prefix);
while (length-- > 0) {
printf("%02X ", *address++);
if (!(++i % line_size) || (length == 0 && i % line_size)) {
if (length == 0) {
while (i++ % line_size)
printf("__ ");
}
printf(" | "); /* right close */
while (line < address) {
c = *line++;
printf("%c", (c < 33 || c == 255) ? 0x2E : c);
}
printf("\n");
if (length > 0)
printf("%s | ", prefix);
}
}
}
/*
* Unescape - process hexadecimal escape character
* converts shell input "\x23" -> 0x23
*/
static int unescape(char *_dst, char *_src, size_t len)
{
int ret = 0;
int match;
char *src = _src;
char *dst = _dst;
unsigned int ch;
while (*src) {
if (*src == '\\' && *(src+1) == 'x') {
match = sscanf(src + 2, "%2x", &ch);
if (!match)
pabort("malformed input string");
src += 4;
*dst++ = (unsigned char)ch;
} else {
*dst++ = *src++;
}
ret++;
}
return ret;
}
static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
int ret;
int out_fd;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
if (mode & SPI_TX_QUAD)
tr.tx_nbits = 4;
else if (mode & SPI_TX_DUAL)
tr.tx_nbits = 2;
if (mode & SPI_RX_QUAD)
tr.rx_nbits = 4;
else if (mode & SPI_RX_DUAL)
tr.rx_nbits = 2;
if (!(mode & SPI_LOOP)) {
if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))
tr.rx_buf = 0;
else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))
tr.tx_buf = 0;
}
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
if (verbose)
hex_dump(tx, len, 32, "TX");
if (output_file) {
out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (out_fd < 0)
pabort("could not open output file");
ret = write(out_fd, rx, len);
if (ret != len)
pabort("not all bytes written to output file");
close(out_fd);
}
// if (verbose || !output_file)
// hex_dump(rx, len, 32, "RX");
}
static void print_usage(const char *prog)
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word\n"
" -i --input input data from a file (e.g. \"test.bin\")\n"
" -o --output output data to a file (e.g. \"results.bin\")\n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -3 --3wire SI/SO signals shared\n"
" -v --verbose Verbose (show tx buffer)\n"
" -p Send data (e.g. \"1234\\xde\\xad\")\n"
" -N --no-cs no chip select\n"
" -R --ready slave pulls low to pause\n"
" -2 --dual dual transfer\n"
" -4 --quad quad transfer\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = {
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ "dual", 0, 0, '2' },
{ "verbose", 0, 0, 'v' },
{ "quad", 0, 0, '4' },
{ NULL, 0, 0, 0 },
};
int c;
/* int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); */
/* */
c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:v",
lopts, NULL);
printf("parse_opts c is %d\n", c);
if (c == -1)
break;
switch (c) {
case 'D':
device = optarg;
break;
case 's':
speed = atoi(optarg);
break;
case 'd':
delay = atoi(optarg);
break;
case 'b':
bits = atoi(optarg);
break;
case 'i':
input_file = optarg;
break;
case 'o':
output_file = optarg;
break;
case 'l':
mode |= SPI_LOOP;
break;
case 'H':
mode |= SPI_CPHA;
break;
case 'O':
mode |= SPI_CPOL;
break;
case 'L':
mode |= SPI_LSB_FIRST;
break;
case 'C':
mode |= SPI_CS_HIGH;
break;
case '3':
mode |= SPI_3WIRE;
break;
case 'N':
mode |= SPI_NO_CS;
break;
case 'v':
verbose = 1;
break;
case 'R':
mode |= SPI_READY;
break;
case 'p':
input_tx = optarg;
break;
case '2':
mode |= SPI_TX_DUAL;
break;
case '4':
mode |= SPI_TX_QUAD;
break;
default:
print_usage(argv[0]);
break;
}
}
if (mode & SPI_LOOP) {
if (mode & SPI_TX_DUAL)
mode |= SPI_RX_DUAL;
if (mode & SPI_TX_QUAD)
mode |= SPI_RX_QUAD;
}
}
static void transfer_escaped_string(int fd, char *str)
{
size_t size = strlen(str);
uint8_t *tx;
uint8_t *rx;
tx = malloc(size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(size);
if (!rx)
pabort("can't allocate rx buffer");
size = unescape((char *)tx, str, size);
transfer(fd, tx, rx, size);
free(rx);
free(tx);
}
static void transfer_file(int fd, char *filename)
{
ssize_t bytes;
struct stat sb;
int tx_fd;
uint8_t *tx;
uint8_t *rx;
if (stat(filename, &sb) == -1)
pabort("can't stat input file");
tx_fd = open(filename, O_RDONLY);
if (fd < 0)
pabort("can't open input file");
tx = malloc(sb.st_size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(sb.st_size);
if (!rx)
pabort("can't allocate rx buffer");
bytes = read(tx_fd, tx, sb.st_size);
if (bytes != sb.st_size)
pabort("failed to read input file");
transfer(fd, tx, rx, sb.st_size);
free(rx);
free(tx);
close(tx_fd);
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
parse_opts(argc, argv);
fd = open(device, O_RDWR);
if (fd < 0)
printf("can't open device");
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret == -1)
printf("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
if (ret == -1)
printf("can't get spi mode");
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
printf("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
printf("can't get bits per word");
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("can't get max speed hz");
// printf("spi mode: 0x%x\n", mode);
// printf("bits per word: %d\n", bits);
// printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
if (input_tx && input_file)
printf("only one of -p and --input may be selected");
if (input_tx)
transfer_escaped_string(fd, input_tx);
else if (input_file)
transfer_file(fd, input_file);
else
{
printf("spi init ....trans default_tx test:\n");
transfer(fd, default_tx, default_rx, sizeof(default_tx));
}
// while(1)
// {
// transfer(fd, default_tx, default_rx, sizeof(default_tx));
// }
close(fd);
return ret;
}
4.示波器
配置为25M时钟
时钟测试没有问题,
看数据
每个BYTE(8个bit)带间隔用时2.2us,byte间隔1.87us左右,传输速率正确
每32gebyte为一包,每发32个byte共用时102us,
包间隔35.4us
配置为5M时钟
每个BYTE时间还是2.2us,但是byte之间的间隔为0.8us了,
每32个BYTE也就是每包的时间仍然不变,102us左右,包间隔32.5us几乎也保持一致
也就是说不特意优化,通过提高时钟频率来提速不太行,瞬时速率快了每个byte间隔也变大了,好在现在的速率也能满足要求,大约算下来2.5Mb左右每秒
分析速率差异原因
linux 不同于单片机裸机程序那样可以直接访问寄存器,如果需要读写物理寄存器,在内核态使用 ioremap (在用户态使用 mmap)将一段物理地址映射到内核态(或用户态)的虚拟地址空间,再对映射后的地址进行读写。
来看一下实际读写寄存器的这两个接口,很简单就是直接读写某个地址处的数据(前提是这个地址必须经过映射)。
#define ssp_readw(addr,ret) (ret =(*(volatile unsigned int *)(addr)))
#define ssp_writew(addr,value) ((*(volatile unsigned int *)(addr)) = (value))
首先可以明确一点,读写寄存器的操作必然会经过MMU。
对于写寄存器来说,不需要考虑同步、脏数据等问题,MMU 应该是直接将这个数据写到物理地址了。
对于读寄存器来说,有 volatile 关键字的存在,这里的代码不会去优化,每次读取必须从物理地址进行读取,这里可能需要 cache 回写等操作导致导致读取的速度非常慢。
5.发送码流代码
同样用到之前的缓冲池,可以先将发送的内容同步保存进文件用作对比
缓存池和信号量初始化
spi.cpp
Linux 的 SPI 驱动生成的 spi 设备文件格式 /dev/spidevB.C ,并提供了功能有限的 API ,可以用 open() 和 close() 函数打开和关闭设备,用 read() 和 write() 函数读写数据,用 ioctl() 发送请求。需要的头文件:
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
首先需要打开设备文件,调用 open() 和 close() 是标准操作,没有特殊之处,例如 open(“/dev/spidev0.0”, O_RDWR) 。然后调用 ioctl() 进行设置,常用的请求有:
SPI_IOC_RD_MODE 和 SPI_IOC_WR_MODE
。用于查询(RD)和设置(WR)单字节 SPI 通信的工作模式,包括时钟极性和时钟相位等特性,需要传递一个字符指针,每个位表示一种特性,可用的宏定义在内核源码的 include/uapi/linux/spi/spi.h 文件中,常用的有:
SPI_MODE_0 , 表示 CPOL=0,CPHA=0 。
SPI_MODE_1 , 表示 CPOL=0,CPHA=1 。
SPI_MODE_2 , 表示 CPOL=1,CPHA=0 。
SPI_MODE_3 , 表示 CPOL=1,CPHA=1 。
SPI_CS_HIGH , 表示片选信号高电平有效。
SPI_LSB_FIRST, 表示按照 LSB 发送,默认是 MSB 发送。
SPI_IOC_RD_MODE32
和 SPI_IOC_WR_MODE32
。用于查询(RD)和设置(WR)完整的 SPI 通信的工作模式,不再局限于单字节传输。需要传递一个 uint32 指针,可用的选项与 SPI_IOC_WR_MODE
相同。
SPI_IOC_RD_LSB_FIRST
和 SPI_IOC_WR_LSB_FIRST
。用于查询(RD)和设置(WR)SPI 的发送顺序,需要传递一个字符指针,0 表示 MSB ,其他值表示 LSB 。
SPI_IOC_RD_BITS_PER_WORD
和 SPI_IOC_WR_BITS_PER_WORD
。用于查询(RD)和设置(WR)SPI 通信的字长,即一次通信发送的 bit 数,需要传递一个字符指针, 0 表示 8bits 。
SPI_IOC_RD_MAX_SPEED_HZ
和 SPI_IOC_WR_MAX_SPEED_HZ
。用于查询(RD)和设置(WR)SPI 的最大传输速率(比特率),单位是 Hz,需要传递一个 uint32 型的指针。
配置完毕后就是可以读写,标准的 read() 和 write() 函数显然只能实现半双工,在这些函数调用之间,片选会被停用,要实现全双工需要调用 ioctl() 函数的 SPI_IOC_MESSAGE(n) 请求,n 用于指定传输的次数,读写的数据需要用 struct spi_ioc_transfer 型的指针传递:
#include <linux/spi/spidev.h>
struct spi_ioc_transfer {
__u64 tx_buf; // 发送缓冲区的指针,里面的数据会发出去,如果为空,会发出 0
__u64 rx_buf; // 接收缓冲区的指针,接收的数据会放在这里,可以为空
__u32 len; // 一次传输的数据长度,单位是字节
__u32 speed_hz; // 临时改变 SPI 的速率
__u16 delay_usecs; // 如果非零,表示两次传输直接的间隔,单位是微秒
__u8 bits_per_word; // 临时改变字长
__u8 cs_change; // 如果非零,下次传输前会取消片选
__u8 tx_nbits;
__u8 rx_nbits;
__u8 word_delay_usecs;
__u8 pad;
}
/******************************************************************************
Copyright (C), 2023, Sunwise Space. Co., Ltd.
******************************************************************************
File Name : spi.cpp
Version :
Author : xin.han
Created : 2023.03.10
Description :
******************************************************************************/
extern "C" {
#include "spi.h"
}
#include "BufferPool.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
extern BuffPool *visible_h264BuffPool_spi;
extern sem_t sem_spi_visible_flg;//全局信号量,传递spi缓冲池
extern HI_BOOL spi_visibleEnable;
static const char *device = "/dev/spidev0.0";
static uint32_t mode;
static uint8_t bits = 8;
static uint32_t speed = 4000000;
// uint8_t default_tx[] = {
// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// 0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// 0xF0, 0x0D,
// };
uint8_t default_tx[] = {
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
0x55, 0xAA,
};
uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
char *input_tx;
FILE* pFd_spih264save = NULL;
void *spi_test_task(void* arg)
{
cpu_set_t mask;//cpu核的集合
cpu_set_t get;//获取在集合中的cpu
int num = sysconf(_SC_NPROCESSORS_CONF);
printf("frame_check_task:system has %d processor(s)\n", num);
CPU_ZERO(&mask);//置空
CPU_SET(0, &mask);//设置亲和力值
if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)//设置线程CPU亲和力
{
fprintf(stderr, "set thread affinity failed\n");
}
if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0)//获取线程CPU亲和力
{
fprintf(stderr, "get thread affinity failed\n");
}
printf("...........................................spitest..........................\n");
int ret = 0;
int fd;
Buffer* buff=NULL;
HI_CHAR aszFileName[VENC_MAX_CHN_NUM][64];
// parse_opts(argc, argv);
fd = open(device, O_RDWR);
if (fd < 0)
printf("can't open device");
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret == -1)
printf("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
if (ret == -1)
printf("can't get spi mode");
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
printf("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
printf("can't get bits per word");
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("can't get max speed hz");
printf("spi mode: 0x%x\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
printf("trans default_tx\n");
transfer(fd, default_tx, default_rx, sizeof(default_tx));
uint32_t pkg_cnt,pkg_rem;
unsigned char pkg_len = 32;
unsigned int i = 0;
snprintf(aszFileName[0],32, "./RTSP/spi.h264");
pFd_spih264save = fopen(aszFileName[0], "wb");
if (!pFd_spih264save)
{
SAMPLE_PRT("open file[%s] failed!\n",
aszFileName[0]);
return NULL;
}
while(1)
{
sem_wait(&sem_spi_visible_flg);
if(visible_h264BuffPool_spi==NULL)
{
continue;
}
buff=visible_h264BuffPool_spi->getFullBuff();
if(buff==NULL)
{
visible_h264BuffPool_spi->putEmptyBuff(buff);
continue;
}
// transfer(fd, (uint8_t*)buff->data, default_rx, buff->dlen);
pkg_cnt = buff->dlen / pkg_len;//数据包可以分为几个包上传
pkg_rem = buff->dlen % pkg_len;//多余的数据最后一个数据包再上传
printf("buff->dlen is %d,pkg_cnt is %d,pkg_rem is %d\n",buff->dlen,pkg_cnt,pkg_rem);
if(pkg_cnt !=0)
{
for( i = 0;i< pkg_cnt ;i++)
{
transfer(fd, (uint8_t*)buff->data+(i*pkg_len), default_rx, pkg_len);
fwrite((uint8_t*)buff->data+(i*pkg_len),pkg_len ,1, pFd_spih264save);
fflush(pFd_spih264save);
// (uint8_t*)buff->data += pkg_len;
}
}
if(pkg_rem != 0)
{
transfer(fd, (uint8_t*)buff->data+(i*pkg_len), default_rx, pkg_rem);
fwrite((uint8_t*)buff->data+(i*pkg_len),pkg_len ,1, pFd_spih264save);
fflush(pFd_spih264save);
}
// transfer(fd, default_tx, default_rx, sizeof(default_tx));
visible_h264BuffPool_spi->putEmptyBuff(buff);
}
close(fd);
return 0;
}
spi.h
/************************************************************************************************
*****Describe: This program is writen to operate HI35xx SPI devices. *****
*****Author: xin.han *****
*****Date: 2023-03-10 *****
*************************************************************************************************/
#ifndef _SPI_H_
#define _SPI_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define __USE_GNU
#include <sched.h>
#include <pthread.h>
#include <signal.h>
#include <pthread.h>
#include <fcntl.h>
#include <time.h>
#include <stdint.h>
#include <sys/prctl.h>
#include <semaphore.h>
#include "sample_comm.h"
#include "hi_comm_ive.h"
#include "hi_comm_video.h"
#include "hi_ive.h"
#include "mpi_ive.h"
#include "hi_common.h"//IVE库
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len);
void *spi_test_task(void* arg);
#endif
spidev.c
因为C和C++在初始化结构时的差异,会报错sorry, unimplemented: non-trivial designated initializers not supported,驱动部分用C写
#include "spi.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev0.0";
static uint32_t mode;
static uint8_t bits = 8;
static char *input_file;
static char *output_file;
static uint32_t speed = 5000000;
static uint16_t delay;
static int verbose;
char *input_tx;
static void hex_dump(const void *src, size_t length, size_t line_size,
char *prefix)
{
int i = 0;
const unsigned char *address = src;
const unsigned char *line = address;
unsigned char c;
printf("%s | ", prefix);
while (length-- > 0) {
printf("%02X ", *address++);
if (!(++i % line_size) || (length == 0 && i % line_size)) {
if (length == 0) {
while (i++ % line_size)
printf("__ ");
}
printf(" | "); /* right close */
while (line < address) {
c = *line++;
printf("%c", (c < 33 || c == 255) ? 0x2E : c);
}
printf("\n");
if (length > 0)
printf("%s | ", prefix);
}
}
}
/*
* Unescape - process hexadecimal escape character
* converts shell input "\x23" -> 0x23
*/
static int unescape(char *_dst, char *_src, size_t len)
{
int ret = 0;
int match;
char *src = _src;
char *dst = _dst;
unsigned int ch;
while (*src) {
if (*src == '\\' && *(src+1) == 'x') {
match = sscanf(src + 2, "%2x", &ch);
if (!match)
pabort("malformed input string");
src += 4;
*dst++ = (unsigned char)ch;
} else {
*dst++ = *src++;
}
ret++;
}
return ret;
}
void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
int ret;
int out_fd;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
if (mode & SPI_TX_QUAD)
tr.tx_nbits = 4;
else if (mode & SPI_TX_DUAL)
tr.tx_nbits = 2;
if (mode & SPI_RX_QUAD)
tr.rx_nbits = 4;
else if (mode & SPI_RX_DUAL)
tr.rx_nbits = 2;
if (!(mode & SPI_LOOP)) {
if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))
tr.rx_buf = 0;
else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))
tr.tx_buf = 0;
}
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
if (verbose)
hex_dump(tx, len, 32, "TX");
if (output_file) {
out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (out_fd < 0)
pabort("could not open output file");
ret = write(out_fd, rx, len);
if (ret != len)
pabort("not all bytes written to output file");
close(out_fd);
}
// if (verbose || !output_file)
// hex_dump(rx, len, 32, "RX");
}
static void print_usage(const char *prog)
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word\n"
" -i --input input data from a file (e.g. \"test.bin\")\n"
" -o --output output data to a file (e.g. \"results.bin\")\n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -3 --3wire SI/SO signals shared\n"
" -v --verbose Verbose (show tx buffer)\n"
" -p Send data (e.g. \"1234\\xde\\xad\")\n"
" -N --no-cs no chip select\n"
" -R --ready slave pulls low to pause\n"
" -2 --dual dual transfer\n"
" -4 --quad quad transfer\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = {
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ "dual", 0, 0, '2' },
{ "verbose", 0, 0, 'v' },
{ "quad", 0, 0, '4' },
{ NULL, 0, 0, 0 },
};
int c;
/* int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); */
/* */
c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:v",
lopts, NULL);
printf("parse_opts c is %d\n", c);
if (c == -1)
break;
switch (c) {
case 'D':
device = optarg;
break;
case 's':
speed = atoi(optarg);
break;
case 'd':
delay = atoi(optarg);
break;
case 'b':
bits = atoi(optarg);
break;
case 'i':
input_file = optarg;
break;
case 'o':
output_file = optarg;
break;
case 'l':
mode |= SPI_LOOP;
break;
case 'H':
mode |= SPI_CPHA;
break;
case 'O':
mode |= SPI_CPOL;
break;
case 'L':
mode |= SPI_LSB_FIRST;
break;
case 'C':
mode |= SPI_CS_HIGH;
break;
case '3':
mode |= SPI_3WIRE;
break;
case 'N':
mode |= SPI_NO_CS;
break;
case 'v':
verbose = 1;
break;
case 'R':
mode |= SPI_READY;
break;
case 'p':
input_tx = optarg;
break;
case '2':
mode |= SPI_TX_DUAL;
break;
case '4':
mode |= SPI_TX_QUAD;
break;
default:
print_usage(argv[0]);
break;
}
}
if (mode & SPI_LOOP) {
if (mode & SPI_TX_DUAL)
mode |= SPI_RX_DUAL;
if (mode & SPI_TX_QUAD)
mode |= SPI_RX_QUAD;
}
}
static void transfer_escaped_string(int fd, char *str)
{
size_t size = strlen(str);
uint8_t *tx;
uint8_t *rx;
tx = malloc(size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(size);
if (!rx)
pabort("can't allocate rx buffer");
size = unescape((char *)tx, str, size);
transfer(fd, tx, rx, size);
free(rx);
free(tx);
}
static void transfer_file(int fd, char *filename)
{
ssize_t bytes;
struct stat sb;
int tx_fd;
uint8_t *tx;
uint8_t *rx;
if (stat(filename, &sb) == -1)
pabort("can't stat input file");
tx_fd = open(filename, O_RDONLY);
if (fd < 0)
pabort("can't open input file");
tx = malloc(sb.st_size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(sb.st_size);
if (!rx)
pabort("can't allocate rx buffer");
bytes = read(tx_fd, tx, sb.st_size);
if (bytes != sb.st_size)
pabort("failed to read input file");
transfer(fd, tx, rx, sb.st_size);
free(rx);
free(tx);
close(tx_fd);
}
spi发送长度有限,单次发送过长会提示发送失败,原因也会清楚地提示发送过长,将码流长度拆分成每包32 BYTE即可
6.测试
1. 运行3559平台脚本,确保管脚复用和有图象输入
2. 3559板子引出clk,mosi,cs管脚,分别链接到逻辑分析仪的通道0 1 2 ,打开逻辑分析仪上位机DSview,添加协议
设置管脚信息用于解码,屏蔽掉其他信息只保留输出data
3. 左上角设置采样率和采样时间后点击开始并运行测试代码
4. 等待右侧解码完毕后,输出csv信息
6. 打开matlab,设置文件夹路径后输入下面代码将csv中的有效数据转化为二进制文件,用potplayer打开能看到码流即正确
close all
clear all
clc
file_id = fopen('spixin.csv')
C = textscan(file_id, '%f%f%s','Delimiter',',','HeaderLines', 1)
raw3 = C{3};
len = length(raw3);
u8data = uint8(zeros(len,1));
for i= 1:len
u8data(i) = uint8(hex2dec(raw3(i)));
end
fclose(file_id);
fid = fopen('spih.h264', 'wb+');
fwrite(fid, u8data);
fclose(fid);