【海思篇】【Hi3516DV300】八、手撸一个海思SPI驱动
目的:使用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驱动相关推荐
- 第二篇-用Flutter手撸一个抖音国内版,看看有多炫
前言 继上一篇使用Flutter开发的抖音国际版 后再次撸一个国内版抖音,大部分功能已完成,主要是Flutter开发APP速度很爽, 先看下图 项目主要结构介绍 这次主要的改动在api.dart 及 ...
- 呆呆带你手撸一个思维导图-基础篇
希沃ENOW大前端 公司官网:CVTE(广州视源股份) 团队:CVTE旗下未来教育希沃软件平台中心enow团队 「本文作者:」 前言 你盼世界,我盼望你无bug.Hello 大家好,我是霖呆呆! 哈哈 ...
- 手撸一个动态数据源的Starter 完整编写一个Starter及融合项目的过程 保姆级教程
手撸一个动态数据源的Starter! 文章目录 手撸一个动态数据源的Starter! 前言 一.准备工作 1,演示 2,项目目录结构 3,POM文件 二.思路 三.编写代码 1,定义核心注解 Ds 2 ...
- .Net Core手撸一个基于Token的权限认证
说明 权限认证是确定用户身份的过程.可确定用户是否有访问资源的权力 今天给大家分享一下类似JWT这种基于token的鉴权机制 基于token的鉴权机制,它不需要在服务端去保留用户的认证信息或者会话信息 ...
- javascript实现图片轮播_手撸一个简易版轮播图(上)
手撸一个简易版轮播图 实现原理,通过控制 swiper-warpper 容器的定位来达到切换图片的效果. 页面布局 简易版轮播图 < > 页面样式 .container{width: 60 ...
- php 六边形 属性图 能力数值图,详解基于 Canvas 手撸一个六边形能力图
一.前言 六边形能力图如下,由 6 个 六边形组成,每一个顶点代表其在某一方面的能力.这篇文章我们就来看看如何基于 canvas 去绘制这么一个六边形能力图.当然,你也可以基于其他开源的 js 方案来 ...
- 五分钟,手撸一个Spring容器!
Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开Spring神秘的面 ...
- 使用Node.js手撸一个建静态Web服务器,内部CV指南
文章里有全部代码,也可以积分下载 操作步骤如上图 文章结束 话说这个键盘真漂亮~~ 文章目录 使用Node.js手撸一个建静态Web服务器 一.动静态服务器的概念 1.1 静态Web服务器概念 1.2 ...
- 10分钟手把手教你用Android手撸一个简易的个人记账App
用Android手撸一个简易的个人记账系统 ⛱️序言
最新文章
- mysql主节点数据恢复_Mysql 主从复制+数据恢复
- 搭建Windows server 2008 R2 KMS
- springboot集成Spring Security oauth2(八)
- Java开发心得:HttpServletRequest基本功能
- Linux进阶之使用Oh-My-Zsh打造炫酷终端
- 陕西师范大学远程教育学院计算机应用基础,陕西师范大学远程教育学院计算机应用基础机考备考试题.doc...
- bzoj3159: 决战
- 下载文件变成php文档,关于文件下载后变成PHP格式的解决办法
- Loadrunner教程–常用操做流程
- 推荐一款串口调试助手(win10,无广告,功能齐全,操作简单)
- 黑客来势汹汹,受害者能以牙还牙“黑回去”吗
- html 加响应头,response发送中文,设置响应头
- 『Kubernetes』Linux安装K8S集群过程笔记
- Storport MSI (Message Signaled Interrupts)
- 网络基础——牛客网刷题第五波
- 计算机主机突然断电有什么影响吗,电脑突然断电对硬盘有影响?实测后真相了...
- 在C#中实现SQLite的事务处理
- md开源云笔记php,推荐一款开源的云笔记 – Leanote(蚂蚁笔记)
- HAC集群添加新节点
- Android 编程好书推荐