海思3559万能平台搭建:SPI输出h264码流

前言

  面对各种各样的客户需求,spi接口也是一种传码流的形式,spi同步422可以保证抗干扰能力强的同时传输距离也很长,本文会介绍海思平台spi作为主机的发送功能以及发送码流的处理方式

1. 管脚复用:

  首先需要配置的肯定是管脚复用,以spi0为例
海思3559万能平台搭建:SPI输出h264码流_第1张图片
   查看pinout表格
海思3559万能平台搭建:SPI输出h264码流_第2张图片

  直接用命令行配一下即可

devmem 0x1f000068 32 0x14f1
devmem 0x1f00006c 32 0x14f1
devmem 0x1f000070 32 0x1401
devmem 0x1f000074 32 0x14f1

2. 寄存器定义

海思3559万能平台搭建:SPI输出h264码流_第3张图片
海思3559万能平台搭建:SPI输出h264码流_第4张图片
  控制寄存器0有两个需要重点注意的,输出频率和数据位宽
海思3559万能平台搭建:SPI输出h264码流_第5张图片
  FSSPCLK参考时钟100M,比如应用层配了5Mb的速率,SCR则为9,CPSDVSR为2
海思3559万能平台搭建:SPI输出h264码流_第6张图片
  控制寄存器1可以看见spi的使能等信息,
在这里插入图片描述
  状态寄存器可以看当前的运行情况
海思3559万能平台搭建:SPI输出h264码流_第7张图片

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 
 *
 * 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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时钟

海思3559万能平台搭建:SPI输出h264码流_第8张图片
  时钟测试没有问题,
海思3559万能平台搭建:SPI输出h264码流_第9张图片
  看数据
  每个BYTE(8个bit)带间隔用时2.2us,byte间隔1.87us左右,传输速率正确
海思3559万能平台搭建:SPI输出h264码流_第10张图片

海思3559万能平台搭建:SPI输出h264码流_第11张图片
  每32gebyte为一包,每发32个byte共用时102us,
海思3559万能平台搭建:SPI输出h264码流_第12张图片
  包间隔35.4us
海思3559万能平台搭建:SPI输出h264码流_第13张图片

配置为5M时钟

海思3559万能平台搭建:SPI输出h264码流_第14张图片
  每个BYTE时间还是2.2us,但是byte之间的间隔为0.8us了,
海思3559万能平台搭建:SPI输出h264码流_第15张图片
海思3559万能平台搭建:SPI输出h264码流_第16张图片
  每32个BYTE也就是每包的时间仍然不变,102us左右,包间隔32.5us几乎也保持一致
海思3559万能平台搭建:SPI输出h264码流_第17张图片

海思3559万能平台搭建:SPI输出h264码流_第18张图片
  也就是说不特意优化,通过提高时钟频率来提速不太行,瞬时速率快了每个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 
#include 
#include 
#include 
#include 

  首先需要打开设备文件,调用 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_MODE32SPI_IOC_WR_MODE32 。用于查询(RD)和设置(WR)完整的 SPI 通信的工作模式,不再局限于单字节传输。需要传递一个 uint32 指针,可用的选项与 SPI_IOC_WR_MODE 相同。
SPI_IOC_RD_LSB_FIRSTSPI_IOC_WR_LSB_FIRST 。用于查询(RD)和设置(WR)SPI 的发送顺序,需要传递一个字符指针,0 表示 MSB ,其他值表示 LSB 。
SPI_IOC_RD_BITS_PER_WORDSPI_IOC_WR_BITS_PER_WORD 。用于查询(RD)和设置(WR)SPI 通信的字长,即一次通信发送的 bit 数,需要传递一个字符指针, 0 表示 8bits 。
SPI_IOC_RD_MAX_SPEED_HZSPI_IOC_WR_MAX_SPEED_HZ 。用于查询(RD)和设置(WR)SPI 的最大传输速率(比特率),单位是 Hz,需要传递一个 uint32 型的指针。
配置完毕后就是可以读写,标准的 read() 和 write() 函数显然只能实现半双工,在这些函数调用之间,片选会被停用,要实现全双工需要调用 ioctl() 函数的 SPI_IOC_MESSAGE(n) 请求,n 用于指定传输的次数,读写的数据需要用 struct spi_ioc_transfer 型的指针传递:

#include 

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 
#include 
#include 
#include 
#define __USE_GNU
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 



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,添加协议
海思3559万能平台搭建:SPI输出h264码流_第19张图片

  设置管脚信息用于解码,屏蔽掉其他信息只保留输出data

海思3559万能平台搭建:SPI输出h264码流_第20张图片
海思3559万能平台搭建:SPI输出h264码流_第21张图片

  3. 左上角设置采样率和采样时间后点击开始并运行测试代码
海思3559万能平台搭建:SPI输出h264码流_第22张图片

  4. 等待右侧解码完毕后,输出csv信息
海思3559万能平台搭建:SPI输出h264码流_第23张图片

  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);

你可能感兴趣的:(海思3559,linux,海思3559,spi,视频编解码)