目的:使用SPI接口让LCD绘制LOGO;让更多的爱好者了解海思、加入海思。

目录

1 查询SPI相关的寄存器

2 SPI的管脚复用

3 SPI的时钟开启

4 SPI1_CSN0启用和配置

5 实现读写寄存器函数

6 spi开启关闭函数

7 spi帧格式、极性、相性及位宽函数

8 spi串行时钟频率函数

9 spi自动手动模式

10 spi数据传输函数

11 调用例子

总结


说明:

根据《外围设备驱动操作指南》里的用户态SPI,想要在LCD绘制LOGO,按照它样例来,结果不成功,原因未知;

看网友说这个SSP操作要自己实现,然后我就参考chip下的《用户指南》和Hi3516CV500_SDK_V2.0.1.1\smp\a7_linux\drv\extdrv\ssp-st7789下的驱动文档自己来实现SSP;

本章主要记录下SPI的实现,关于LCD(ILI9342)的详细调试会在其他章节里介绍;

1 查询SPI相关的寄存器

《用户指南.pdf》和《3516DV300_PINOUT_EN.xlsx》查询如下,(我样例中用的是SPI1和SPI1_CSN0):

/*
SPI0 基地址是 0x120C_0000
SPI1 基地址是 0x120C_1000
SPI2 基地址是 0x120C_2000
*/
#define SSP_BASE            (0x120c1000)/*
SPI1 pinmux
*/
#define SPI1_SCLK           (0x112F0020)
#define SPI1_SDO            (0x112F0024)
#define SPI1_CSN0             (0x112f0028)
#define SPI1_SDI            (0x112F002C)/*
PERI_CRG111 - SPI Clock Control
*/
#define PERI_CRG111         (0x120101BC)/*
MISC_CTRL0  - SP1_CSN0 Check
*/
#define MISC_CTRL0           (0x12030000+0x0000)#define SSP_SIZE             0x1000          // 4KB
#define DEFAULT_MD_LEN       (128)
#define IO_ADDRESS_VERIFY(x) (x)/* SSP register definition .*/
#define SSP_CR0              IO_ADDRESS_VERIFY(SSP_BASE + 0x00)
#define SSP_CR1              IO_ADDRESS_VERIFY(SSP_BASE + 0x04)
#define SSP_DR               IO_ADDRESS_VERIFY(SSP_BASE + 0x08)
#define SSP_SR               IO_ADDRESS_VERIFY(SSP_BASE + 0x0C)
#define SSP_CPSR             IO_ADDRESS_VERIFY(SSP_BASE + 0x10)
#define SSP_IMSC             IO_ADDRESS_VERIFY(SSP_BASE + 0x14)
#define SSP_RIS              IO_ADDRESS_VERIFY(SSP_BASE + 0x18)
#define SSP_MIS              IO_ADDRESS_VERIFY(SSP_BASE + 0x1C)
#define SSP_ICR              IO_ADDRESS_VERIFY(SSP_BASE + 0x20)
#define SSP_DMACR            IO_ADDRESS_VERIFY(SSP_BASE + 0x24)
#define SSP_SPITXFIFOCR      IO_ADDRESS_VERIFY(SSP_BASE + 0x28)
#define SSP_SPIRXFIFOCR      IO_ADDRESS_VERIFY(SSP_BASE + 0x2C)#define SPI_SR_BSY           (0x1 << 4)/* spi busy flag */
#define SPI_SR_TFE           (0x1 << 0)/* Whether to send fifo is empty */
#define SPI_DATA_WIDTH       (9)
#define SPI_SPO              (1)/* 极性 */
#define SPI_SPH              (1)/* 相性 */
#define SPI_SCR              (0)//(8)
#define SPI_CPSDVSR          (2)//(8)
#define SPI_FRAMEMODE        (0)#define MAX_WAIT 10000

2 SPI的管脚复用

static void spi1_pinmux()
{ssp_writew(SPI1_SCLK, 0x4f1);ssp_writew(SPI1_SDO, 0x4f1);ssp_writew(SPI1_SDI, 0x4f1);spi1_csn0_pinmux();printf("spi1_pinmux\n");
}

3 SPI的时钟开启

独立软复位:由寄存器 PERI_CRG111 的 bit[17:15] 控制。相应位写“ 0 ”, SPI 退出软复位;相应位写“ 1 ”, SPI 进入软复位。上电缺省值为 0 。
时钟门控:由寄存器 PERI_CRG111 的 bit[14:12] 控制。相应位写“ 0 ”,关闭时钟;相应位写“ 1 ”,打开时钟。上电缺省值为 1 。

PERI_CRG111的第13bit为SPI1始终们控配置寄存器

0: 关闭时钟

1: 打开时钟

static void spi1_open_clock()
{unsigned int peri_cfg111;ssp_readw(PERI_CRG111, peri_cfg111);ssp_writew(PERI_CRG111, peri_cfg111|0x1<<13);printf("spi1_open_clock\n");
}

4 SPI1_CSN0启用和配置

MISC_CTRL0的第2bit为SPI1信号输出片选选择

0: SPI1_CSN0

1: SPI1_CSN1

MISC_CTRL0的第3bit为SPI1片选信号0极性控制

0: SPI1_CSN0低有效

1: SPI1_CSN0高有效

static void spi1_csn0_pinmux()
{// 复选spi_csn0ssp_writew(SPI1_CSN0, 0x0501);// 配置csn0低有效 且选择片选0unsigned int misc_ctrl0;ssp_readw(MISC_CTRL0, misc_ctrl0);misc_ctrl0 &= ~(0x1<<3); // 低有效misc_ctrl0 &= ~(0x1<<2); // 片选0ssp_writew(MISC_CTRL0, misc_ctrl0);
}

5 实现读写寄存器函数

参考himm和himd

#ifdef __HuaweiLite__
#define  ssp_readw(addr,ret)            (ret =(*(volatile unsigned int *)(addr)))
#define  ssp_writew(addr,value)         ((*(volatile unsigned int *)(addr)) = (value))
#else
static const char dev[]="/dev/mem";static void write_reg(unsigned int Addr, unsigned int Value)
{int fd = open (dev, O_RDWR | O_SYNC);if (fd < 0){printf("open %s error!\n", dev);return ;}/* addr align in page_size(4K) */unsigned long phy_addr_in_page;unsigned long page_diff;phy_addr_in_page = Addr & PAGE_SIZE_MASK;page_diff = Addr - phy_addr_in_page;/* size in page_size */unsigned long size_in_page;unsigned long size = PAGE_SIZE;size_in_page =((size + page_diff - 1) & PAGE_SIZE_MASK) + PAGE_SIZE;void *addr = mmap((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr_in_page);if (addr == MAP_FAILED){printf("mmap @ 0x%x error!\n", phy_addr_in_page);close(fd);return;}unsigned int *addr_cur = (unsigned int *)(addr+page_diff);*addr_cur = Value;/* munmap */if(munmap(addr, size_in_page) != 0 ){printf("munmap failed!\n");}close(fd);//printf("ssp_write_reg %x %d\n", Addr, Value);
}static unsigned int read_reg(unsigned int Addr)
{unsigned int Value = 0;int fd = open (dev, O_RDWR | O_SYNC);if (fd < 0){printf("open %s error!\n", dev);return -1;}/* addr align in page_size(4K) */unsigned long phy_addr_in_page;unsigned long page_diff;phy_addr_in_page = Addr & PAGE_SIZE_MASK;page_diff = Addr - phy_addr_in_page;/* size in page_size */unsigned long size_in_page;unsigned long size = PAGE_SIZE;size_in_page =((size + page_diff - 1) & PAGE_SIZE_MASK) + PAGE_SIZE;void *addr = mmap((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr_in_page);if (addr == MAP_FAILED){printf("mmap @ 0x%x error!\n", phy_addr_in_page);close(fd);return -1;}unsigned int *addr_cur = (unsigned int *)(addr+page_diff);Value = *addr_cur;/* munmap */if(munmap(addr, size_in_page) != 0 ){printf("munmap failed!\n");}close(fd);//printf("ssp_read_reg %x %d\n", Addr, Value);return Value;
}#define  ssp_readw(addr,ret)            (ret=read_reg(addr))
#define  ssp_writew(addr,value)         (write_reg(addr, value))
#endif

6 spi开启关闭函数

SSP_CR1的第1bit为设置SPI使能

0: 不使能

1: 使能

static void spi_enable(void)
{ssp_writew(SSP_CR1, 0x42);
}static void spi_disable(void)
{ssp_writew(SSP_CR1, 0x40);
}void hi_ssp_enable(void)
{int ret = 0;ssp_readw(SSP_CR1,ret);ret = (ret & 0xFFFD) | 0x2;ret = ret | (0x1 << 4); /* big/little end, 1: little, 0: big */ret = ret | (0x1 << 15); /* wait en */ssp_writew(SSP_CR1,ret);
}void hi_ssp_disable(void)
{int ret = 0;ssp_readw(SSP_CR1,ret);ret = ret & (~(0x1 << 1));ssp_writew(SSP_CR1,ret);
}

7 spi帧格式、极性、相性及位宽函数

SSP_CR0的[4-5]bit为帧格式选择,这里我们选择了Motorola SPI

00: Motorla SPI帧格式

01: TI同步串行格式

10: National Microwire帧格式

11: 保留

SSP_CR0的第6bit为SPICLKOUT极性(SPO)

SSP_CR0的第7bit为SPICLKOUT相性(SPH)

SSP_CR0的[0-3]bit为设置数据位宽:

0011: 4bit

1000: 9bit

1101: 14bit

0100: 5bit

1001: 10bit

int hi_ssp_set_frameform(unsigned char framemode,unsigned char spo,unsigned char sph,unsigned char datawidth)
{int ret = 0;ssp_readw(SSP_CR0,ret);if(framemode > 3){printf("[ERROR]set frame parameter err.\n");return -1;}ret = (ret & 0xFFCF) | (framemode << 4);if((ret & 0x30) == 0){if(spo > 1){printf("[ERROR]set spo parameter err.\n");return -1;}if(sph > 1){printf("[ERROR]set sph parameter err.\n");return -1;}ret = (ret & 0xFF3F) | (sph << 7) | (spo << 6);}if((datawidth > 16) || (datawidth < 4)){printf("[ERROR]set datawidth parameter err.\n");return -1;}ret = (ret & 0xFFF0) | (datawidth -1);ssp_writew(SSP_CR0,ret);return 0;
}

8 spi串行时钟频率函数

输出 SPI 时钟频率计算方式如下:
Fsspclkout = Fsspclk/ ( CPSDVR x ( 1+SCR ))
Fsspclk : SPI 的工作参考时钟 100M 。
CPSDVR 、 SCR 请查询相应寄存器。

SPICR0的[8-15]bit为串行时钟率(SCR),取值范围0~255;

SPICPSR的[0-7]bit为时钟分频因子(CPSDVR),此值必须是2~254之间的偶数,也就是0bit位必须为0;

int hi_ssp_set_serialclock(unsigned char scr,unsigned char cpsdvsr)
{int ret = 0;ssp_readw(SSP_CR0,ret);ret = (ret & 0xFF) | (scr << 8);ssp_writew(SSP_CR0,ret);if((cpsdvsr & 0x1)){printf("[ERROR]set cpsdvsr parameter err.\n");return -1;}ssp_writew(SSP_CPSR,cpsdvsr);return 0;
}

9 spi自动手动模式

SSP_CR1的第6bit介绍:

0: 片选信号由芯片逻辑根据所选时序自动产生

1: 当采用Motorola SPI帧格式时,片选CS信号由SPI使能信号控制,是能后片选拉低,否则片选拉高。

int hi_ssp_alt_mode_set(int enable)
{int ret = 0;ssp_readw(SSP_CR1,ret);if (enable){ret = ret & (~0x40);}else{ret = (ret & 0xFF) | 0x40;}ssp_writew(SSP_CR1,ret);return 0;
}

10 spi数据传输函数

static int hi_spi_check_timeout(void)
{unsigned int value =  0;unsigned int tmp = 0;while (1){ssp_readw(SSP_SR,value);if ((value & SPI_SR_TFE) && (!(value & SPI_SR_BSY))){break;}if (tmp++ > MAX_WAIT){printf("spi transfer wait timeout!\n");return -1;}usleep(1);}return 0;
}void spi_write_a9byte(unsigned char cmd_dat,unsigned char dat)
{unsigned short spi_data = 0;int ret = 0;if(cmd_dat){spi_data = 1 << 8;}else{spi_data = 0 << 8;}spi_data |= dat;//printf("spi_data: %x %x\n", spi_data, dat);spi_enable();ssp_writew(SSP_DR,spi_data);ret =  hi_spi_check_timeout();if(ret != 0){printf("spi_send timeout\n");}spi_disable();
}void spi_write_a16byte(unsigned short spi_dat)
{int ret = 0;spi_enable();ssp_writew(SSP_DR, spi_dat);//printf("spi_data:0x%x\n",spi_dat);ret =  hi_spi_check_timeout();if(ret != 0){printf("spi_send timeout\n");}spi_disable();
}

11 调用例子

void hi_ssp_demo(void)
{spi1_pinmux();spi1_open_clock();spi_disable();hi_ssp_set_frameform(SPI_FRAMEMODE, SPI_SPO, SPI_SPH, SPI_DATA_WIDTH);hi_ssp_set_serialclock(SPI_SCR, SPI_CPSDVSR);hi_ssp_alt_mode_set(1);hi_ssp_enable();spi_write_a16byte(0x1111);spi_write_a16byte(0x1112);spi_write_a16byte(0x1113);spi_write_a16byte(0x1114);hi_ssp_disable();
}

总结

1 SPI时钟、数据线、片选线管脚要先复用;

2 SPI模式、极性、相性、位宽、时钟速率要配置好;

3 SPI时钟要开启;我的样例一直在发送数据后检测SR_TFE中阻塞,原因是SPI_SCLK没有开启。

4 片选使能、高低有效位要配置好;

5 如果用于应用层,由于重复打开/dev/mem及mamp,使通讯速度变慢,可以针对性优化。

【海思篇】【Hi3516DV300】八、手撸一个海思SPI驱动相关推荐

  1. 第二篇-用Flutter手撸一个抖音国内版,看看有多炫

    前言 继上一篇使用Flutter开发的抖音国际版 后再次撸一个国内版抖音,大部分功能已完成,主要是Flutter开发APP速度很爽,  先看下图 项目主要结构介绍 这次主要的改动在api.dart 及 ...

  2. 呆呆带你手撸一个思维导图-基础篇

    希沃ENOW大前端 公司官网:CVTE(广州视源股份) 团队:CVTE旗下未来教育希沃软件平台中心enow团队 「本文作者:」 前言 你盼世界,我盼望你无bug.Hello 大家好,我是霖呆呆! 哈哈 ...

  3. 手撸一个动态数据源的Starter 完整编写一个Starter及融合项目的过程 保姆级教程

    手撸一个动态数据源的Starter! 文章目录 手撸一个动态数据源的Starter! 前言 一.准备工作 1,演示 2,项目目录结构 3,POM文件 二.思路 三.编写代码 1,定义核心注解 Ds 2 ...

  4. .Net Core手撸一个基于Token的权限认证

    说明 权限认证是确定用户身份的过程.可确定用户是否有访问资源的权力 今天给大家分享一下类似JWT这种基于token的鉴权机制 基于token的鉴权机制,它不需要在服务端去保留用户的认证信息或者会话信息 ...

  5. javascript实现图片轮播_手撸一个简易版轮播图(上)

    手撸一个简易版轮播图 实现原理,通过控制 swiper-warpper 容器的定位来达到切换图片的效果. 页面布局 简易版轮播图 < > 页面样式 .container{width: 60 ...

  6. php 六边形 属性图 能力数值图,详解基于 Canvas 手撸一个六边形能力图

    一.前言 六边形能力图如下,由 6 个 六边形组成,每一个顶点代表其在某一方面的能力.这篇文章我们就来看看如何基于 canvas 去绘制这么一个六边形能力图.当然,你也可以基于其他开源的 js 方案来 ...

  7. 五分钟,手撸一个Spring容器!

    Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开Spring神秘的面 ...

  8. 使用Node.js手撸一个建静态Web服务器,内部CV指南

    文章里有全部代码,也可以积分下载 操作步骤如上图 文章结束 话说这个键盘真漂亮~~ 文章目录 使用Node.js手撸一个建静态Web服务器 一.动静态服务器的概念 1.1 静态Web服务器概念 1.2 ...

  9. 10分钟手把手教你用Android手撸一个简易的个人记账App

    用Android手撸一个简易的个人记账系统 ⛱️序言

最新文章

  1. mysql主节点数据恢复_Mysql 主从复制+数据恢复
  2. 搭建Windows server 2008 R2 KMS
  3. springboot集成Spring Security oauth2(八)
  4. Java开发心得:HttpServletRequest基本功能
  5. Linux进阶之使用Oh-My-Zsh打造炫酷终端
  6. 陕西师范大学远程教育学院计算机应用基础,陕西师范大学远程教育学院计算机应用基础机考备考试题.doc...
  7. bzoj3159: 决战
  8. 下载文件变成php文档,关于文件下载后变成PHP格式的解决办法
  9. Loadrunner教程–常用操做流程
  10. 推荐一款串口调试助手(win10,无广告,功能齐全,操作简单)
  11. 黑客来势汹汹,受害者能以牙还牙“黑回去”吗
  12. html 加响应头,response发送中文,设置响应头
  13. 『Kubernetes』Linux安装K8S集群过程笔记
  14. Storport MSI (Message Signaled Interrupts)
  15. 网络基础——牛客网刷题第五波
  16. 计算机主机突然断电有什么影响吗,电脑突然断电对硬盘有影响?实测后真相了...
  17. 在C#中实现SQLite的事务处理
  18. md开源云笔记php,推荐一款开源的云笔记 – Leanote(蚂蚁笔记)
  19. HAC集群添加新节点
  20. Android 编程好书推荐

热门文章

  1. 新手如何快速入门深度学习
  2. 计算机专业学数电模电哪本书,我该怎么学数电和模电啊?
  3. 分布式-分布式存储笔记
  4. apollo学习之---planning理论到实践(5-3)---路径边界计算
  5. windows7下安装ACHI驱动
  6. win10设置自动关机
  7. 内存取证工具——volatility 常用命令
  8. 胸大肌(09):仰卧屈臂上拉
  9. 查看mysql账户的权限INSERT command denied to user 'dataview'@'118.144.137.111' for table
  10. 集成学习-Voting