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

news2025/2/13 3:01:35

前言

  面对各种各样的客户需求,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_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_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);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/645585.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java容器排序

Java的容器 在Java中&#xff0c;我们想要保存对象可以使用很多种手段。最简单的就是数组。但是数组具有固定的尺寸&#xff0c;而通常来说&#xff0c;程序总是在运行时根据条件来创建对象&#xff0c;我们无法预知将要创建对象的个数以及类型&#xff0c;所以Java推出了容器…

动态规划-最长的回文序列

这里写自定义目录标题 1 描述2 样例2.1 样例12.2 样例2 3 解题思路以及实现方法3.1 解题思路3.1.1 确定状态3.1.2 转移方程3.1.3 初始条件和边界情况3.1.4 计算顺序 3.2 题解3.2.1 C实现3.2.2 java实现 该题是lintcode上 667 最长的回文序列&#xff0c;该题的解题思路亦是参…

方法选对,事半功倍:数据分析方法

人们发明了数据可视化&#xff0c;利用人类大脑更善于处理图像信息的特点&#xff0c;透过图形化的手段&#xff0c;用图表清晰有效地传达和沟通信息。把以往庞杂、繁乱的数据报表转化成简洁明了的可视化图表。 通过数据可视化制作出的图表&#xff0c;不再像传统分析方案那样…

JavaScript Web APIs学习总结

以后声明变量我们有限使用哪一个&#xff1f; const 有了变量先给const&#xff0c;如果发现它后面是要被修改的&#xff0c;再改为let 为什么const声明的对象可以修改里面的属性&#xff1f; 因为对象是引用类型&#xff0c;里面存储的是地址&#xff0c;只要地址不变&…

月入6000+的CSGO游戏搬砖项目操作细节和要求

月入6000的CSGO游戏搬砖项目操作细节和要求 最近咨询CSGO搬砖项目的人较多&#xff0c;在此整理一份统一的项目操作细节和要求。 1、什么是国外Steam游戏装备汇率差项目&#xff1f; 这个项目的基本原理是&#xff1a;购买国外Steam游戏平台上的装备&#xff0c;再在国内网易…

SSR渲染--02--nuxt demo(vue)

cmd 打开命令提示符&#xff0c;创建nuxt npm i create-nuxt-app -g create-nuxt-app vue-honor-of-kings&#xff08;这个是我的项目名&#xff0c;输入自己的项目名&#xff09; npm run dev创建的具体配置 【写demo为了方便&#xff0c;我直接下载了Element的包&#xff0…

游戏画面延迟的原因及解决方法

游戏画面延迟&#xff0c;是玩家在游戏中操作后&#xff0c;画面反应出现滞后或卡顿的现象。这会严重影响玩家的游戏体验和竞技水平&#xff0c;尤其是在一些需要高速反应和精确操作的3A大作中&#xff0c;画面延迟可能导致玩家错失良机或者被敌人击败。 那么&#xff0c;游戏…

电能质量监测装置在某半导体公司的应用

摘 要&#xff1a;半导体生产制造业在国民经济中起着举足轻重的作用&#xff0c;相关企业的规模也越来越大。其供配电系统稳定、可靠的运维不仅是其安全生产的基本保证&#xff0c;还关系到产品质量和生产的顺利进行。而半导体行业中大部分工艺设备对电能质量比较敏感&#xff…

《CDP企业数据云平台从入门到实践》——如何迁移CDH/HDP到CDP(2)

原文&#xff1a;《CDP企业数据云平台从入门到实践》——如何迁移CDH/HDP到CDP&#xff08;2&#xff09;-阿里云开发者社区 简介&#xff1a; 《CDP企业数据云平台从入门到实践》——如何迁移CDH/HDP到CDP&#xff08;2&#xff09; 《CDP企业数据云平台从入门到实践》——如…

Windows安装和配置VCenter

Vcenter的环境搭建和配置 Vcenter简介 Vcenter一般指 VMware vCenter™ Server&#xff0c;VMware vCenterServer 提供了一个可伸缩、可扩展的平台&#xff0c;为 虚拟化管理奠定了基础。 VMware vCenter Server&#xff08;以前称为 VMware VirtualCenter&#xff09;&#…

Qt6.5.1+WebRTC学习笔记(十一)开发环境搭建(ubuntu22.04)

前言 win10开发测试已经一段时间了&#xff0c;最近将程序移植到ubuntu测试了下&#xff0c;改动不是很大&#xff0c;本教程记录下环境搭建过程 一、准备 1.操作系统ubuntu22.04 64位 x86架构&#xff08;建议更新到最新&#xff09; 2.合理的上网方式&#xff0c;需要正常…

模板匹配笔记

模板匹配是一种最基本、最原始的模式识别的方法。通过对比某一特定物体的图案位于图像的什么地方&#xff0c;进而识别出物体。它是图像处理中最基本、最常用的匹配方法。它的局限性主要是它只能进行平行移动&#xff0c;若原图像中的匹配目标发生旋转或大小变化&#xff0c;该…

运维(SRE)成长之路-第2天 文本编辑工具之神VIM

vi和vim简介 在Linux中我们经常编辑修改文本文件&#xff0c;即由ASCII, Unicode 或其它编码的纯文字的文件。之前介绍过nano&#xff0c;实际工作中我们会使用更为专业&#xff0c;功能强大的工具 文本编辑种类&#xff1a; 全屏编辑器&#xff1a;nano&#xff08;字符工具…

Spark SQL数据源:Parquet文件

文章目录 一、Parquet简介二、读取和写入Parquet的方法&#xff08;一&#xff09;利用parquet()方法读取parquet文件1、数据准备2、读取parquet文件3、显示数据帧内容 &#xff08;二&#xff09;利用parquet()方法写入parquet文件1、写入parquet文件2、查看生成的parquet文件…

Spring Security--多个过滤器链和多个用户表

请求从客户端出发&#xff0c;到达客户端&#xff0c;也就是servlet&#xff0c;中间有很多过滤器的&#xff0c;其中就有一个过滤器链代理&#xff0c;里面包含了过滤器的一个集合。而且Spring Security Filter并不是直接嵌入到Web Filter中的&#xff0c;而是通过FilterChain…

海纳“千川”:得物多场景统一推荐平台|精选

1 千川由来 得物的推荐场景&#xff0c;除了首页瀑布流等几个比较大的场景之外&#xff0c;还有很多长尾的小场景&#xff0c;包括&#xff1a;频道、会场、购中购后场景、品牌墙等。这类场景存在单个场景体量小&#xff08;UV和GMV均偏小&#xff09;、场景零散、类型多元的情…

炫龙笔记本怎么用U盘重装Win10系统?

炫龙笔记本怎么用U盘重装Win10系统&#xff1f;很多使用炫龙笔记本电脑的用户在问&#xff0c;怎么使用U盘来重装一下电脑的Win10系统&#xff0c;这时候用户先要准备一个U盘&#xff0c;还有一个能够正常联网的炫龙笔记本电脑&#xff0c;最后按照以下炫龙笔记本用U盘重装Win1…

可算是熬出头了,测试3年,费时6个月,入职阿里,涨薪14K

本科毕业后就一直从事测试的工作&#xff0c;和多数人一样&#xff0c;最开始从事点点点的工作&#xff0c;看着自己的同学一步一步往上走&#xff0c;自己还是在原地踏步&#xff0c;说实话这不是自己想要的状态。 一年半后开始沪漂生活&#xff0c;又摸爬滚打了一年半&#…

FL Studio21.0.3最新版本下载 可切换中文语言

FL Studio是最好的! 在我12/13岁的时候&#xff0c;一个DJ向我介绍了FL Studio。它让我大吃一惊&#xff0c;怎么能在几秒钟内做出一个鼓的模式。后来我又玩了好几年&#xff0c;到了15岁时&#xff0c;我确定我想成为一名制作人。FL Studio帮助我把脑海中的想法快速地变成一个…

开发者必备:动态贴纸和美颜SDK的集成教程

动态贴纸和美颜技术不仅能带来多样的拍摄方案&#xff0c;还可以增加应用的吸引力。本篇文章将为开发者提供动态贴纸和美颜SDK的集成教程、技术方案&#xff0c;帮助开发者快速实现这些功能。 一、动态贴纸美颜SDK集成教程 导入SDK 在Android Studio中&#xff0c;开发者需要…