一、前言

因为部门的一个负责海思驱动开发的老同事另谋高就了,部门又暂时找不到人来对接他的任务,所以领导就让我这个菜鸟来硬着头皮顶上了。在这我也对这位老同事表示深刻的感谢,在对接的期间,那么耐心教导我,让我这个刚出来社会不久、又没怎么接触过海思平台的菜鸟学习到了很多东西。

回归正题:部门在海思3531A产品调试的时候发现SPI通信不正常,使用示波器发现SPI的CS片选引脚怎么也拉不低,SCLK(时钟)、SDI(数据输入引脚)和SDO(数据输出引脚)的信号也不正常。在我不停地阅读官方资料和询问大神们,最终调好了。

所以接下来,本文章会针对 海思SPI配置的问题 来记录下我调试的整一个过程,包括官方资料的阅读。

二、SPI管脚信息获取

在一个新的平台上面开发,我们首先需要阅读官方的资料,并获取到自己需要的资料。这里主要是针对与 SPI配置 相关的资料查找。

2.1 SPI_SCLK、SPI_SDI、SPI_SDO管脚复用寄存器

因为涉及到SPI的使用(如果不熟悉SPI通信协议的,可以先去搜索了解下先),那就会涉及到管脚的使用,所以我们先找到芯片的管脚信息表,这个管脚信息表一般芯片平台的官方资料里面都会有,而且一般都会放在硬件的目录下。海思平台的管脚信息表的路径是 00.hardware\chip\document_cn\HI3531AV100_PINOUT_CN.xlsx ,打开可以看到这个表格有很多页,这些页的内容包括管脚的功能/复用功能、默认值、特性以及管脚复用寄存器等信息。

我们在HI3531AV100_PINOUT_CN.xlsx这个表格的《1.管脚信息表》页找到SPI管脚。

  • 《1.管脚信息表》页列说明:
Pin Num Pin Name Voltage(V) Default IO Type Mux Control Register Name Drive&Slew Control Register Name Function n
管脚号 管脚名称 管脚输入或输出的高/低电平 默认的管脚是输入/输出高还是低等。 IO口的类型 复用寄存器名称,有的话就代表该管脚有复用功能。如果不是硬件复用可在《3.管脚复用寄存器》页查找该名称,可以看到该管脚的复用寄存器地址以及功能描述;如果是硬件复用,则可以在《5.硬件复用关系》页查找到。 这是管脚的驱动能力寄存器,可在《4.管脚驱动能力寄存器》页查看。 这是对应管脚的功能描述。
  • 我们可以看到寄存器部分 HI3531AV100_PINOUT_CN.xlsx 表格就只是说了管脚复用的一些寄存器,那么其他管脚的寄存器呢?一般其他的寄存器文档也是会在这个目录下的,海思平台的这个文档是在 00.hardware\chip\document_cn\Hi3531A H.264编解码处理器用户指南.pdf

通过上面管脚信息表,我们发现和SPi相关的有7个管脚,而且都是复用管脚,其中四个是片选管脚,其他则是SPi的时钟(SPI_SCLK)、数据输入(SPI_SDI)、数据输出(SPI_SDO)管脚。我们需要使用SPI的功能,那么就需要正确配置每个引脚。

接下来我们先根据上面的复用管脚的复用寄存器名称,到《3.管脚复用寄存器》页或者《5.硬件复用关系》页里面找:

  1. 片选管脚SPI_CSN0-SPI_CSN3:

片选管脚是硬件复用的,这一般不需要软件来进行配置,需要硬件工程师来配合。通过个人的调试经验来说,需要用到的的片选管脚硬件上不要接上拉电阻或者下拉电阻,否则海思芯片有可能会无法控制该片选管脚。

  1. SPI_SCLK、SPI_SDI、SPI_SDO管脚复用寄存器:

通过上图得知,SPI_SCLK、SPI_SDI、SPI_SDO管脚的默认值都不是和SPI通信相关的,所以我们需要配置这些管脚为SPI功能,也就是要往寄存器写0x01的值。

2.2 片选SPI_CSN0-SPI_CSN3管脚寄存器

在HI3531AV100_PINOUT_CN.xlsx 表格中,SPI_SCLK、SPI_SDI、SPI_SDO管脚的寄存器的地址和值都找到了,但是片选管脚的寄存器呢?不在这个表格,那就是在上面提到的 00.hardware\chip\document_cn\Hi3531A H.264编解码处理器用户指南.pdf 文档中了。打开文档,我们发现该片选的寄存器是在 3.系统/3.5.系统控制器/3.5.5.外设控制寄存器 的目录中,是外设功能选择寄存器2(MISC_CTRL5)控制的片选:

  • 外设控制寄存器概览:

  • MISC_CTRL5寄存器:

这样的话我么就知道片选引脚的寄存器地址是 0x1212 0014、对应的值也可以通过上图MISC_CTRL5寄存器得知了。

三、配置和使能与SPI相关的管脚

到了这里,SPI所有引脚的寄存器地址和对应的值都知道了,那这些应该怎么配置呢?

先不要着急,我们先来打开 01.software\board\document_cn\Hi3531A SDK 安装以及升级使用说明.txt 这个文档(当我们第一次拿到官方SDK的时候就应该看这个来进入着手,里面内容写得不是很详细,但是覆盖面挺大的。01.software\board\document_cn这个目录下的文档都是与软件开发相关的文档),看一下有没有和管脚配置的相关的引导。

可以发现是有说到与管脚配置相关的说明,而且正好是复用管脚的使用。通过以上的文字可知,很有可能我们的相关管脚就是在mpp/ko目录下的sh脚本中配置的,我们到SDK的mpp/ko目录看下sh脚本。其中pinmux_vicap_i2c_i2s.sh脚本的内容如下:

#!/bin/sh

echo "run $0 begin!!!";
#I2C
himm 0x120F01CC 0x1; # 0:GPIO19_6 01:I2C0_SDA
himm 0x120F01D0 0x1; # 0:GPIO19_7 01:I2C0_SCL
himm 0x120F0148 0x2; # 0: GPI13_0 01: SPI_SCLK 2:I2C1_SCL #刚好是SPI_SCLK复用寄存器的地址,这里设置为了I2C1_SCL功能
himm 0x120F014C 0x2; # 0: TEST_CLK 01: SPI_SDO 2:I2C1_SDA #是SPI_SDO复用寄存器的地址,这里设置为了I2C1_SDA功能 #VICAP
himm 0x120F0000 0x2;
himm 0x120F0024 0x2;
himm 0x120F0048 0x2; # 0:GPIO21_2 01:VI_ADC_REFCLK0 2:VI1_CLK
himm 0x120F004c 0x2;
himm 0x120F0070 0x2;
himm 0x120F0094 0x2; # 0:GPIO21_5 01:VI_ADC_REFCLK1 2:VI3_CLK
himm 0x120F0098 0x2;
himm 0x120F00bc 0x2;
himm 0x120F00E0 0x2; # 0:GPIO12_1 01:VI_ADC_REFCLK2 2:VI5_CLK
himm 0x120F00e4 0x2;
himm 0x120F0108 0x2;
himm 0x120F012C 0x2; # 0:GPIO20_6 01:VI_ADC_REFCLK3 2:VI7_CLK #UART
himm 0x120F0200 0x3; # 0: GPIO18_2 1:VOU_SLV_DAT11 2:UART1_RTSN 3:UART0_RTSN
himm 0x120F0204 0x3; # 0: GPIO18_3 1:VOU_SLV_DAT10 2:UART1_CTSN 3:UART0_CTSN #I2S
himm 0x120F0130 0x1; # 0: GPIO11_0 1: I2S0_BCLK_RX
himm 0x120F0134 0x1; # 0: GPIO11_1 1: I2S0_WS_RX
himm 0x120F0138 0x1; # 0: GPIO11_2 1: I2S0_SD_RX himm 0x120F013C 0x1; # 0: GPIO11_3 1: I2S1_BCLK_RX
himm 0x120F0140 0x1; # 0: GPIO11_4 1: I2S1_WS_RX
himm 0x120F0144 0x1; # 0: GPIO11_5 1: I2S1_SD_RX himm 0x120F01AC 0x1; # 0: GPIO11_6 1: I2S2_BCLK_RX
himm 0x120F01B0 0x1; # 0: GPIO11_7 1: I2S2_WS_RX
himm 0x120F01B4 0x1; # 0: GPIO12_0 1: I2S2_SD_RX
himm 0x120F01B8 0x1; # 0: GPIO12_3 1: I2S2_SD_TX 2:SLAVE_MODE
himm 0x120F01BC 0x1; # 0: GPIO12_4 1: I2S2_MCLK 2:BOOT_SEL1 himm 0x120F0198 0x1; # 0: GPIO12_5 1: I2S3_BCLK_TX
himm 0x120F019C 0x1; # 0: GPIO12_6 1: I2S3_WS_TX
himm 0x120F01A0 0x1; # 0: GPIO12_7 1: I2S3_SD_TX 2:PCIE_REFCLK_SEL echo "run $0 end!!!";

通过这个配置文件,我们可以看到是用himm来配置引脚属性的,这是海思的一款工具。而且可以看到SPI有两个管脚的功能被复用成了I2C了,所以我们需要重新配置管脚的功能。那怎么修改呢?本文章也是会使用himm工具来配置,接下来讲解下himm工具的使用。

3.1 海思himm工具配置管脚

海思提供的himm工具,能在linux命令行中,直接对gpio进行操作。此工具可以在已经编译好的SDK中 osdrv/tools/board/reg-tools-1.0.0/bin 这个目录下可以看到。

我们看一看himm工具的本质,ls -al

-rwxr-xr-x 1 root  root  45540 Nov 20 23:57 btools
lrwxrwxrwx 1 root root 6 Nov 20 23:57 himm -> btools

可以看到himm工具其实就是btools可执行文件,因此如果板子上没有这个工具,则我们只需要将btools放到板子上,并且建立链接,做好之后就可以使用了。

himm执行的格式:

himm 寄存器地址 需要设置的值

那现在我们就用himm来配置SPI管脚,通过第二章,我们都知道SPI相关的寄存器地址和相关的功能了。现在我将管脚配置成SPI功能、使用片选0管脚并且低电平有效,对应寄存器的地址和值如下:

寄存器 寄存器地址 value
SPI_SCLK管脚复用寄存器 0x120F0148 0x1
SPI_SDO管脚复用寄存器 0x120F014C 0x1
SPI_SDI管脚复用寄存器 0x120F0150 0x1
片选CS外设功能选择寄存器2 0x12120014 0x0

使用的配置指令如下:

himm 0x120F0148 0x1;  # 0: GPI13_0   01: SPI_SCLK  2:I2C1_SCL
himm 0x120F014C 0x1; # 0: TEST_CLK 01: SPI_SDO 2:I2C1_SDA
himm 0x120F0150 0x1; # 0: TEST_CLK 01: SPI_SDO 2:I2C1_SDA
himm 0x12120014 0x0;

以上的指令可以直接在板子上终端输入,但是这是一次性的设置,重启之后配置就不在了,如下图:

为了每次启动都有效,我们可以把以上的配置指令添加到上面提及到的mpp/ko/pinmux_vicap_i2c_i2s.sh脚本里面,然后编译根文件系统到板子上即可,也可以直接在板子上修改这个脚本:/nand/ko/pinmux_vicap_i2c_i2s.sh,如下图:

四、用户态APP使用SPI

我们配好了SPI管脚,那么在上层应用app怎么使用这个SPI功能呢?既然这个芯片有这个SPI的硬件功能,那么一般来说都会有直接使用它的软件接口。该SPI的使用指南就在 01.software\board\document_cn\外围设备驱动 操作指南.pdf 这个文档里,我们跳到 5.SPI操作指南 的章节阅读,你就知道怎么操作啦。

看到 5.3.1 SPI读写命令示例 这里的时候,我们可以看到上面说的四个片选管脚并不是一个SPI控制器来控制的,Hi3531A的SPI控制器0有1个片选、控制器1有3个片选,我们在到开发板看一看 ls /dev/ 是不是有两个SPI的驱动,一个是spidev0.0,另外一个是spidev0.1。也就是说,如果你使用spidev0.0的驱动节点,那么就只能使用片选0;如果是使用spidev0.1,则可以选择片选1-3。所以在配置片选的时候,要注意这一点。

4.1 示例

下面示例是用户态的SPI读写程序(也可以参考 外围设备驱动 操作指南.pdf 文档的示例),隔500ms发送一次1-9的数据:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h> unsigned char isExit = 0;
unsigned char mode = 0;
unsigned char bits = 8;
unsigned int speed = 9*1000*1000;
unsigned short delay = 0;
unsigned char cs_change = 1; static unsigned int m_spi_write(int fd, int len, unsigned char* sbuf, unsigned char* rbuf); void interrupt(int sig)
{
isExit = 1;
} static unsigned int m_spi_write(int fd, int len, unsigned char* sbuf, unsigned char* rbuf)
{
int ret;
struct spi_ioc_transfer tr; tr.tx_buf = (unsigned long)sbuf;
tr.rx_buf = (unsigned long)rbuf;
tr.len = len;
tr.delay_usecs = delay;
tr.speed_hz = speed;
tr.bits_per_word = bits;
tr.cs_change = cs_change; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if(ret < 1){
printf("spi_write error\n");
return -1;
} return tr.len;
} int spi_init(unsigned char *arg)
{
int fd = 0;
int ret = 0;
unsigned int reg_value = 0; fd = open(arg, O_RDWR);
if (fd<0)
{
printf("Open /dev/spidev0.0 dev error!\n");
return -1;
} printf("Open /dev/spidev0.0 dev success!\n"); ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if(ret == -1){
printf("set spi WR mode error\n");
return -1;
} ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if(ret == -1){
printf("set spi RD mode error\n");
return -1;
} ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if(ret == -1){
printf("set spi write bits per word error\n");
return -1;
} ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if(ret == -1){
printf("set spi read bits per word error\n");
return -1;
} ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if(ret == -1){
printf("set spi write Max speed error\n");
return -1;
} ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if(ret == -1){
printf("set spi write Max speed error\n");
return -1;
} printf("int spi success, mode:%d, bits:%d, speed:%d MHz\n", mode, bits, speed/1000000); return fd;
} int main(int argc , char* argv[])
{
int i = 0, fd = -1;
unsigned int rsize = 0;;
unsigned char sbuf[1024] = {1,2,3,4,5,6,7,8,9};
unsigned char rbuf[1024] = {0}; if(argc != 2){
printf("pls input ./hisi2mcu /dev/spidev0.0 or /dev/spidev0.1");
return -1;
} signal(SIGINT, interrupt);
signal(SIGTERM, interrupt); fd = spi_init((unsigned char *)argv[1]);
if(fd < 0){
printf("spi_init error\n");
return -1;
} while(1)
{
if(isExit == 1)
break; memset(rbuf, 0, sizeof(rbuf)); rsize = m_spi_write(fd, strlen(sbuf), sbuf, rbuf);
printf("hisi recv szie = %d\n", rsize); if(rsize == 0)
continue; for(i=0; i<rsize; ++i)
{
printf("0x%02x ", rbuf[i]); if((i+1)%9 == 0)
printf("\n");
} printf("\n");
usleep(500*1000);
} close(fd);
return 0;
}

4.2 效果

使用示波器来采集SPI管脚的输出,如下图:

因为没有从设备发送数据,所以输入引脚是空的。

【海思】Hi3531A SPI功能的详细配置以及使用的更多相关文章

  1. 海思3516系列芯片SPI速率慢问题深入分析与优化(基于PL022 SPI 控制器)

    海思3516系列芯片SPI速率慢问题深入分析与优化(基于PL022 SPI 控制器) 我在某个海思主控的项目中需要使用SPI接口来驱动一块液晶屏,液晶屏主控为 st7789,分辨率 240x240,图 ...

  2. 海思uboot启动流程详细分析(二)

    1. 第二个start.S 从start_armboot开始,在startup.c中有包含#include <config.h> 在config.h中: /* Automatically ...

  3. 海思uboot启动流程详细分析(转)

    海思uboot启动流程详细分析(一) 海思uboot启动流程详细分析(二) 海思uboot启动流程详细分析(三)  

  4. Hi3531a海思logo加载的实现流程

    海思篇之开机logo的加载(Hi3531a命令版) 2019-02-02 11:31:51 Wilburn0 阅读数 479更多 分类专栏: 海思开发   版权声明:本文为博主原创文章,遵循CC 4. ...

  5. 主流芯片解决方案Ambarella的高清网络摄像机、德州仪器和控制海思

    (本文由四川艾普作为数码科技有限公司 苏斌.范清华 收集) 高清网络视频监控发展到今天.正的高清时代.诸多有实力的高清摄像机厂家的产品线也逐渐完好起来,高清网络视频监控的配套产品有更加丰富和成熟.与此 ...

  6. 第2季:从官方例程深度学习海思SDK及API

    2.1.官方mppsample的总体分析2.1.sample的整体架构(1)sample其实是很多个例程,所以有很多个main(2)每一个例程面向一个典型应用,common是通用性主体函数,我们只分析 ...

  7. 用VS2013+VELT-0.1.4进行海思平台 Linux内核 的开发

    快乐虾 http://blog.csdn.net/lights_joy/(QQ群:Visual EmbedLinux Tools 375515651) 欢迎转载,但请保留作者信息 本文仅适用于vs20 ...

  8. 用vs2013+velt-0.1.4进行嵌入式开发 进行海思平台 UBOOT 开发

    1.1    什么是VELT VELT的全称是Visual EmbedLinuxTools,它是一个与visual gdb类似的visual studio插件,用以辅助完成Linux开发.利用这个插件 ...

  9. 海思hi35xx 开发学习(3):视频输入

    视频输入(VI)模块实现的功能:通过 MIPI Rx(含 MIPI 接口.LVDS 接口和 HISPI 接口),SLVS-EC,BT.1120,BT.656,BT.601,DC 等接口接收视频数据.V ...

随机推荐

  1. vue项目中mockjs的使用

    mock.js是一个库,源码托管:https://github.com/nuysoft/Mock github上的原话:Mock.js是一个模拟数据生成器,可帮助前端开发和原型与后端进度分开,并减少某 ...

  2. VueX(简)

    最近又看了vue的文档,借此整理一下知识.用于自我加深理解 vueX是vue官方推出的状态管理机制. 上面一张图是核心 主要为: State:存储数据 Mutation: 更改数据 Action: 暴 ...

  3. Graph-to-ID task

    首先图像是一个二维的结构,CNN提取图片的特征,但是是local的,通过kenel的形式,不断的图上移动,通过卷积的形式, 无论移动到哪个位置,内部的结构都是不变的,这就是参数共享. 这个所说的图像显 ...

  4. lua table面向对象扩展

    一 .table扩展 -- 返回table大小 table.size = function(t) local count = 0 for _ in pairs(t) do count = count ...

  5. B. GameGame 解析(思維、博弈)

    Codeforce 1383 B. GameGame 解析(思維.博弈) 今天我們來看看CF1383B 題目連結 題目 兩個人在玩遊戲,有一個長度為\(n\)的數列\(a\),每次每個人選一個數字和目 ...

  6. Kubernetes 配置私有镜像仓库时,没有权限访问的问题

    使用 K8S 部署服务时,如果指定的镜像地址是内部镜像仓库,那么在下载镜像的时候可能会报权限错误.这是由于在 K8S 中部署服务时,K8S 需要到 Harbor 中进行一次验证,这个验证与节点中使用 ...

  7. RestfulApi 学习笔记——简单介绍(一)

    前言 什么是restapi? 直接看:http://www.ruanyifeng.com/blog/2014/05/restful_api.html 阮一峰的blog,即可明白,下面是一些例子,增强理 ...

  8. python机器学习实现线性回归

    线性回归 关注公众号"轻松学编程"了解更多. [关键词]最小二乘法,线性 一.普通线性回归 1.原理 分类的目标变量是标称型数据,而回归将会对连续型的数据做出预测. 应当怎样从一大 ...

  9. python开发--python函数-(持续更新)

    1. 打印 : print() # 打印,输出 2. 变量 : var = 'hello' # 变量var , 把'hello' 赋值给变量 var 3. if 函数 : # 代码块 4个空格或者一个 ...

  10. MySQL全面瓦解8:查询的正则匹配

    概述 上一章 查询的过滤条件,我们了解了MySQL可以通过 like % 通配符来进行模糊匹配.同样的,它也支持其他正则表达式的匹配,我们在MySQL中使用 REGEXP 操作符来进行正则表达式匹配. ...