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

这篇具有很好参考价值的文章主要介绍了海思3559万能平台搭建:SPI输出h264码流。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

1. 管脚复用:

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

  直接用命令行配一下即可

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

2. 寄存器定义

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

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

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

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

配置为5M时钟

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

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

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

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

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

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

  6. 打开matlab,设置文件夹路径后输入下面代码将csv中的有效数据转化为二进制文件,用potplayer打开能看到码流即正确文章来源地址https://www.toymoban.com/news/detail-482788.html

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万能平台搭建:SPI输出h264码流的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • H264码流中 SPS PPS SEI 详解

    1 客户端抓包 在做客户端视频解码时,一般都会使用Wireshark抓包工具对接收的H264码流进行分析,如下所示: 在这里我们可以看到对解码视频起关键作用的SPS和PPS。  2、双击SPS内容如下: 那么从上面的sps中我们知道图像的宽,高。 宽=(19+1 )*16=320 高=(14+1)*16=240 SPS语法元

    2023年04月20日
    浏览(51)
  • 基于Live555实现RTSP服务器来推送H264实时码流

    实现了一个单播的rtsp服务器来推送实时的h264码流,参考了官方的testProgs目录下的testOnDemandRTSPServer例程和liveMedia目录下的DeviceSource.cpp文件。我这边是把编码出来的h264码流放入了一个缓冲队列,然后从缓冲队列里取出来进行推流。 rtsp.h: rtsp.cpp:  

    2024年02月05日
    浏览(48)
  • RK3568平台开发系列讲解(音视频篇)H264 的编码结构

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢视频编码的码流结构其实就是指视频经过编码之后得到的二进制数据是怎么组织的,换句话说,就是编码后的码流我们怎么将一帧帧编码后的图像数据分离出来,以及在二进制码流数据中,哪一块数

    2024年02月09日
    浏览(66)
  • 视频编码(H264编码)

    基本步骤:    1、打开编码器          2、转换NV12到YUV420,这是因为FFmpeg的x264编码器只支持YUV420          3、准备编码数据AVFrame         创建frame:  创建AVPacket: AVPacket* pck = av_packet_alloc(); 4、H264编码       avcodec_send_frame(#AVCodecContext *avctx#, #const AVFrame *frame#)    

    2024年02月03日
    浏览(47)
  • wireshark导出H264裸流

    下载抓包工具:首先,您需要下载并安装一个网络抓包工具,例如Wireshark(https://www.wireshark.org)或tcpdump(https://www.tcpdump.org)。这些工具可用于捕获网络流量并保存为pcap或pcapng文件。在这里插入代码片 打开抓包工具:打开所选择的抓包工具,并启动网络监控。 下载rtp_h264

    2024年02月15日
    浏览(41)
  • OpenCV保存H264视频的问题

    windows 10 64bit opencv 4.5.3 看到有小伙伴在聊,如何将目标检测的结果保存成视频的相关问题?本篇我们就来看看。 在绝大多数的目标检测项目中,都是使用 opencv 这个开源的计算机视觉库来进行图片、视频或者摄像头的读写。 关于视频保存,来看下面的代码示例 主要是 VideoW

    2023年04月09日
    浏览(48)
  • h264编码概述七(SPS解析)

    SPS即Sequence Paramater Set,又称作序列参数集。SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。 H.264标准协议中规定的SPS格式位于文档的7.3.2.1.1,如下图所示: 1、profile_idc 根据《T-REC-H.264-201402-I!!PDF-E》的附件A.2定义,profiles有如下类型: profile_idc的值用来确定码流符

    2024年02月12日
    浏览(40)
  • 从MediaRecord录像中读取H264参数

    许多以前做个一个测试项目,想把android手机视频接入视频服务器,做法是这样的: 把android手机的实时视频上传到后台转发服务器,转发服务器把音视频流包装成IPC rtsp服务器形式,流媒体连接手机转发服务器就像连接IPC设备一样,使用rtsp的拉流模式获取视频流。 从而实现手

    2023年04月08日
    浏览(42)
  • 基于WebAssembly无插件解码H264/H265码流播放器

    基于WebAssembly无插件解码H264/H265码流播放器 之前看到一篇文章:web无插件解码播放H264/H265(WebAssembly解码HTML5播放) H.265/HEVC在Web视频播放的实践 按照文章思路,已经复现了web端无插件解码H265码流。首先说明下我这边的环境,框架。 在海思主板上移植了web服务器nginx,用于pc端请

    2024年01月16日
    浏览(46)
  • 关于Ubuntu python程序利用lixb264生成h264格式的视频相关问题

    从官网下载:https://www.nasm.us/pub/nasm/releasebuilds/2.13.03/ 安装包页面上的nasm-2.13.03.tar.bz2 x264库下载地址:http://www.videolan.org/developers/x264.html 下载 x264-master.tar.bz2 利用命令 x264 --version 查看是否配置成功 从官网下载ffmpeg ffmpeg-snapshot.tar.bz2 sudo ldconfig 若转出视频为h.264格式则说明成功

    2023年04月08日
    浏览(92)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包