NUC970的SPI寄存器足够简单,但是没有DMA支持有点遗憾,现在SPI也算高速设备了,一个4线的SPI FLASH都支持100MHz时钟,400Mbit的速度了,靠中断肯定是不行的,目前NUC970所支持的几种存储器里面,估计只能靠TF或者eMMC了,毕竟有DAM支持,nand flash现在用的少,占用IO也多,速度也就那样,块还出奇的大,还要自己做写均衡,坏块管理,eMMC都是BGA封装,目前有个这种的SD NAND 使用的SDIO接口的大容量flash,兼容SD卡驱动,可以实现QFN-8封装的1-2G大容量存储。

//足够简单的SPI寄存器


//SPI========================================================================================================
#define SPI0_BASE               (0xB8006200)      //寄存器基址
#define SPI1_BASE               (0xB8006300)      //寄存器基址typedef struct
{vu32 CNTRL;vu32 DIVIDER;vu32 SSR;u32 Reserved1;vu32 DATA[4];
}SPI_TypeDef;#define SPI0                       ((SPI_TypeDef *) SPI0_BASE)
#define SPI1                       ((SPI_TypeDef *) SPI1_BASE)

其中可以支持突发4次传输,理论上可以实现一次传输16字节,但是没啥意义呀,在时钟为18MHz的时候,通讯128bit占用时间为7uS,7us中断一次CPU也不划算,还不如直接阻塞,直接阻塞时使用8bit发送与一次发送16个8bit阻塞时间几乎没区别,如果时钟速度更高,这个时间会更短,就更不能用中断了(太频繁,会降低系统实时性),还不如用可剥夺的实时OS直接阻塞,这就是没有DMA的缺点,有DMA时至少读取512直接都不用一次中断,而且效率更高。

//SPI.c

/************************************************************************************************************** 文件名:         SPI.c* 功能:          NUC970 SPI驱动* 作者:           cp1300@139.com* 创建时间:      2020-09-01* 最后修改时间: 2020-09-01* 详细:         定时器支持
*************************************************************************************************************/
#include "nuc970_system.h"
#include "spi.h"//SPI基址
static const u32 scg_SPIx_Base[SPI_ChMax] = {SPI0_BASE, SPI1_BASE};
//SPI时钟使能
static const SYS_DEV_CLOCK scg_SPIx_DeviceClockEnable[SPI_ChMax]    = {DEV_SPI0, DEV_SPI1};
//外设复位
static const SYS_DEV_RESET scg_SPIx_DeviceReset[SPI_ChMax] = { DEV_RESET_SPI0,     DEV_RESET_SPI1};
//IO复用功能
static const GPIO_AF_Type scg_SPIx_AF_GPIO[SPI_ChMax][3] =
{{GPIO_PB7_SPI0_CLK,    GPIO_PB8_SPI0_DATA0,    GPIO_PB9_SPI0_DATA1}, {GPIO_PI6_SPI1_CLK,   GPIO_PI7_SPI1_DATA0,    GPIO_PI8_SPI1_DATA1},
};/*************************************************************************************************************************
*函数         :   bool SPIx_Init(SPI_CH_Type ch, const SPI_CONFIG *pConfig)
*功能         :   SPI初始化
*参数         :   ch:定时器通道号;pConfig:配置
*返回         :   TRUE:初始化成功;FALSE:初始化失败;
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :
*************************************************************************************************************************/
bool SPIx_Init(SPI_CH_Type ch, const SPI_CONFIG *pConfig)
{if(ch > (SPI_ChMax - 1)) return FALSE;                          //端口号超出范围SYS_DeviceClockEnable(scg_SPIx_DeviceClockEnable[ch], TRUE);   //使能时钟SYS_DeviceReset(scg_SPIx_DeviceReset[ch]);                        //复位外设SPIx_Config(ch, pConfig);                                     //SPI配置//IO初始化SYS_GPIOx_SetAF(scg_SPIx_AF_GPIO[ch][0]);                         //CLK 复用功能设置SYS_GPIOx_SetAF(scg_SPIx_AF_GPIO[ch][1]);                       //MOSI 复用功能设置SYS_GPIOx_SetAF(scg_SPIx_AF_GPIO[ch][2]);                      //MISO 复用功能设置return TRUE;
}/*************************************************************************************************************************
*函数         :   void SPIx_SetIOMode(SPI_CH_Type ch, SPI_IO_MODE mode)
*功能         :   设置IO模式
*参数         :   ch:SPI道号;mode:模式
*返回         :   无
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :
*************************************************************************************************************************/
void SPIx_SetIOMode(SPI_CH_Type ch, SPI_IO_MODE mode)
{u32 RegTemp;SPI_TypeDef *SPIx;if(ch > (SPI_ChMax - 1)) return;                              //端口号超出范围SPIx = (SPI_TypeDef *) scg_SPIx_Base[ch];                     //获取设备基址RegTemp = SPIx->CNTRL;RegTemp &= ~(BIT22|BIT21|BIT0);                          //清除之前配置,不要写入BIT0switch(mode){case SPI_DUAL_IO_MODE: //双IO模式{RegTemp |= BIT22;}break;case SPI_QUAD_IO_MODE: //四IO模式{RegTemp |= BIT21;}break;default:break;         //单IO模式}SPIx->CNTRL = RegTemp;
}/*************************************************************************************************************************
*函数         :   void SPIx_SetMultiIOOut(SPI_CH_Type ch, bool isOutput)
*功能         :   设置多IO模式(双IO或四IO模式)下IO方向
*参数         :   ch:SPI道号;isOutput:TRUE:输出方向;FALSE:输入方向
*返回         :   无
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :
*************************************************************************************************************************/
void SPIx_SetMultiIODirection(SPI_CH_Type ch, bool isOutput)
{SPI_TypeDef *SPIx;if(ch > (SPI_ChMax - 1)) return;                              //端口号超出范围SPIx = (SPI_TypeDef *) scg_SPIx_Base[ch];                     //获取设备基址if(isOutput)                                                    //输出模式{SPIx->CNTRL |= BIT20;}else{SPIx->CNTRL &= ~BIT20;}
}/*************************************************************************************************************************
*函数         :   void SPIx_SetTranBitLeng(SPI_CH_Type ch, u8 BitLen)
*功能         :   设置单次传输bit长度
*参数         :   ch:SPI道号;BitLen:bit长度1-32bit
*返回         :   无
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :
*************************************************************************************************************************/
void SPIx_SetTranBitLeng(SPI_CH_Type ch, u8 BitLen)
{SPI_TypeDef *SPIx;u32 temp;if(ch > (SPI_ChMax - 1)) return;                             //端口号超出范围SPIx = (SPI_TypeDef *) scg_SPIx_Base[ch];                     //获取设备基址temp = BitLen;if(temp < 1) temp = 1;if(temp > 32) temp = 32;SPIx->CNTRL &= ~(0x1F<<3);SPIx->CNTRL |= temp << 3;
}/*************************************************************************************************************************
*函数         :   void SPIx_SetOneTranCount(SPI_CH_Type ch, u8 Count)
*功能         :   设置单次收发次数
*参数         :   ch:SPI道号;Count:1-4次
*返回         :   无
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :   可以连续多次传输
*************************************************************************************************************************/
void SPIx_SetOneTranCount(SPI_CH_Type ch, u8 Count)
{SPI_TypeDef *SPIx;u32 temp;if(ch > (SPI_ChMax - 1)) return;                             //端口号超出范围SPIx = (SPI_TypeDef *) scg_SPIx_Base[ch];                     //获取设备基址temp = Count;if(temp < 1) temp = 1;if(temp > 4) temp = 4;temp -= 1;SPIx->CNTRL &= ~(0x3<<8);if(temp > 1){SPIx->CNTRL |= temp << 8;}
}/*************************************************************************************************************************
*函数         :   void SPIx_Config(SPI_CH_Type ch,const SPI_CONFIG *pConfig)
*功能         :   SPI配置
*参数         :   ch:SPI道号;pConfig:配置
*返回         :   无
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :   会回复到单IO模式,并且单次只传输一次,不会使用自动片选
*************************************************************************************************************************/
void SPIx_Config(SPI_CH_Type ch, const SPI_CONFIG *pConfig)
{u32 RegTemp;u32 temp1;SPI_TypeDef *SPIx;if(ch > (SPI_ChMax - 1)) return;                                //端口号超出范围SPIx = (SPI_TypeDef *) scg_SPIx_Base[ch];                     //获取设备基址//CNTRLRegTemp = 0;if(pConfig->isClkIdleHigh)                                       //时钟空闲高电平使能{RegTemp |= BIT31;}temp1 = pConfig->TranSleepCycle;if(temp1 < 2) temp1 = 2;if(temp1 > 17) temp1 = 17;temp1 -= 2;RegTemp |= temp1 << 12;                                         //发送空闲周期,连续发送时中间空闲几个时钟周期if(pConfig->isLSB)                                                //低位在前{RegTemp |= BIT10;}temp1 = pConfig->TranBitLeng;if(temp1 < 1) temp1 = 1;if(temp1 > 32) temp1 = 32;//temp1 -= 1;RegTemp |= temp1 << 3;if(pConfig->isTranNegEdge)                                       //发送数据下降沿有效{RegTemp |= BIT2;}if(pConfig->isReceNegEdge)                                     //接收数据下降沿有效{RegTemp |= BIT1;}SPIx->CNTRL = RegTemp;//DIVIDERRegTemp = 0;temp1 = pConfig->ClockDivider;if(temp1 < 2) temp1 = 2;if(temp1 > 131072)temp1 = 131072;temp1 = (temp1+1)/2;RegTemp = temp1 - 1;SPIx->DIVIDER = RegTemp;SPIx->SSR = 0;                                                  //关闭自动片选
}/*************************************************************************************************************************
*函数         :   void SPIx_SetSpeed(SPI_CH_Type ch, u32 ClockDivider)
*功能         :   SPI设置时钟速度
*参数         :   ch:SPI道号;ClockDivider:分频系数2-131072
*返回         :   无
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :
*************************************************************************************************************************/
void SPIx_SetSpeed(SPI_CH_Type ch, u32 ClockDivider)
{SPI_TypeDef *SPIx;if(ch > (SPI_ChMax - 1)) return;                              //端口号超出范围SPIx = (SPI_TypeDef *) scg_SPIx_Base[ch];                     //获取设备基址if(ClockDivider < 2) ClockDivider = 2;if(ClockDivider > 131072)ClockDivider = 131072;ClockDivider = (ClockDivider+1)/2;ClockDivider = ClockDivider - 1;SPIx->DIVIDER = ClockDivider;
}/*************************************************************************************************************************
*函数         :   u8 SPIx_ReadWriteByte(SPI_CH_Type ch,u8 TxData)
*功能         :   SPI读写一字节
*参数         :   ch:SPI道号;TxData:要发送的数据
*返回         :   读取的一字节
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :
*************************************************************************************************************************/
u8 SPIx_ReadWriteByte(SPI_CH_Type ch,u8 TxData)
{SPI_TypeDef *SPIx;if(ch > (SPI_ChMax - 1)) return 0;                                //端口号超出范围SPIx = (SPI_TypeDef *) scg_SPIx_Base[ch];                     //获取设备基址SPIx->DATA[0] = TxData;SPIx->CNTRL |= BIT0;                                         //开始发送数据nop;nop;nop;nop;while(SPIx->CNTRL & BIT0);                                       //等待发送完成return SPIx->DATA[0] & 0xFF;                                     //返回读取到的数据
}/*************************************************************************************************************************
*函数         :   u32 SPIx_ReadWriteData(SPI_CH_Type ch,u32 TxData)
*功能         :   SPI读写一次数据
*参数         :   ch:SPI道号;TxData:要发送的数据
*返回         :   读取的数据
*依赖         :   底层宏定义
*作者         :   cp1300@139.com
*时间             :   2020-09-01
*最后修改时间 :   2020-09-01
*说明         :   数据长度由通讯bit长度决定
*************************************************************************************************************************/
u32 SPIx_ReadWriteData(SPI_CH_Type ch,u32 TxData)
{SPI_TypeDef *SPIx;if(ch > (SPI_ChMax - 1)) return 0;                                //端口号超出范围SPIx = (SPI_TypeDef *) scg_SPIx_Base[ch];                     //获取设备基址SPIx->DATA[0] = TxData;while(SPIx->CNTRL & BIT0);                                        //等待发送完成return SPIx->DATA[0];                                            //返回读取到的数据
}

//SPI.h

/************************************************************************************************************** 文件名:         SPI.h* 功能:          NUC970 SPI驱动* 作者:           cp1300@139.com* 创建时间:      2020-09-01* 最后修改时间: 2020-09-01* 详细:         定时器支持
*************************************************************************************************************/
#ifndef _SPI_H_
#define _SPI_H_
#include "nuc970_system.h"//SPI通道选择
typedef enum
{SPI_CH0    =      0,              //SPI0SPI_CH1   =      1,              //SPI1
}SPI_CH_Type;
#define SPI_ChMax       2           //SPI数量//IO模式
typedef enum
{SPI_ONE_IO_MODE        =  0,      //单IO模式SPI_DUAL_IO_MODE =  1,      //双IO模式SPI_QUAD_IO_MODE =  2,      //四IO模式
}SPI_IO_MODE;//SPI配置,默认为单IO模式
typedef struct
{bool isClkIdleHigh;                //时钟空闲高电平使能bool isLSB;                      //是否低位在前bool isTranNegEdge;             //发送数据下降沿有效bool isReceNegEdge;              //接收数据下降沿有效u8 TranSleepCycle;               //发送空闲周期,连续发送时中间空闲几个时钟周期,设置范围2-17u8 TranBitLeng;                  //传输数据bit长度1-32位u32 ClockDivider;               //时钟分频最小2分频,只能是偶数2-131072
}SPI_CONFIG;//API
bool SPIx_Init(SPI_CH_Type ch, const SPI_CONFIG *pConfig);      //SPI初始化
void SPIx_SetSpeed(SPI_CH_Type ch, u32 ClockDivider);           //SPI设置时钟速度
void SPIx_Config(SPI_CH_Type ch, const SPI_CONFIG *pConfig);    //SPI配置
u8 SPIx_ReadWriteByte(SPI_CH_Type ch,u8 TxData);                //SPI读写一字节//不常用API
void SPIx_SetIOMode(SPI_CH_Type ch, SPI_IO_MODE mode);          //设置IO模式
void SPIx_SetMultiIODirection(SPI_CH_Type ch, bool isOutput);   //设置多IO模式(双IO或四IO模式)下IO方向
void SPIx_SetTranBitLeng(SPI_CH_Type ch, u8 BitLen);            //设置单次传输bit长度
void SPIx_SetOneTranCount(SPI_CH_Type ch, u8 Count);            //设置单次收发次数
u32 SPIx_ReadWriteData(SPI_CH_Type ch,u32 TxData);              //SPI读写一次数据#endif //_SPI_H_

//测试

/
//W25X64 FLASH支持
//SPI接口设置
#define SPI_FLASH_CS                    PBout(6)            //W25X16片选
#define W25X16_SPI_CH                   SPI_CH0
//W26X16 硬件接口初始化
void __inline W25X16_HardwaveInit(void)
{const SPI_CONFIG mConfig = {FALSE, FALSE,  FALSE, TRUE, 0, 8, 4};SPIx_Init(W25X16_SPI_CH, &mConfig);SYS_GPIOx_SetAF(GPIO_PB6_IO);SYS_GPIOx_OneInit(GPIOB, 6, OUT_PP, GPIO_IN_NONE);   //初始化一个IOSPI_FLASH_CS = 1;
}/***********************W25X16相关接口************************/
//W25XXX SPI通信接口
u8 BI_W25_SPI_ReadWrtieByte(u8 data)
{return SPIx_ReadWriteByte(W25X16_SPI_CH, data);
}//W25XXX SPI片选控制接口
void BI_W25_SPI_SetCS(u8 CS_Level)
{SPI_FLASH_CS = CS_Level;
}//W25XXX 信号量申请
void BI_W25_MutexPen(void)
{//INT8U err;//OSMutexPend(g_SysGlobal.w25x16_semp, 0, &err);       //申请W25X16信号量
}//W25XXX 信号量释放
void BI_W25_MutexPost(void)
{//OSMutexPost(g_SysGlobal.w25x16_semp);                //释放W25X16信号量
}//初始化W25Q128W25X16_HardwaveInit();                     //初始化W25X16底层硬件SPI接口W25X16_OSMutexCreate();                     //W25X16信号量初始化-需要在任务中进行初始化while(1){g_SysGlobal.FlashId = W25XXX_Init(&g_SysGlobal.W25X16_Handle, BI_W25_SPI_ReadWrtieByte,         //W25XXX SPI通信接口NULL,//BI_W25_BulkRead,             //SPI批量读取接口NULL,//BI_W25_BulkWrite,         //SPI批量写入接口BI_W25_SPI_SetCS,                    //W25XXX SPI片选控制接口Sleep,                                //系统ms延时BI_W25_MutexPen,                    //W25XXX 信号量申请BI_W25_MutexPost                  //W25XXX 信号量释放);uart_printf("FlashId:%d\r\n",g_SysGlobal.FlashId);if(g_SysGlobal.FlashId != FLASH_NULL) break;Sleep(500);}

完整的工程链接:https://download.csdn.net/download/cp1300/12797399

NUC970(ARM9)裸机SPI驱动相关推荐

  1. linux i2c adapter 增加设备_「正点原子Linux连载」第六十二章Linux SPI驱动实验(一)...

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南>关注官方微信号公众号,获取更多资料:正点原子 第六十二章Linux SPI驱动实验 上一 ...

  2. 【正点原子Linux连载】第六十二章 Linux SPI驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  3. (STM32)从零开始的RT-Thread之旅--SPI驱动ST7735(3)使用DMA

    上一篇完成了ST7735驱动的移植,并已经可以通过SPI在屏幕上显示字符了,这一章会把SPI修改为DMA的传输方式.由于RTT对于STM32H7的SPI的DMA传输方式目前支持的并不好,这就让上一章裸 ...

  4. 总线驱动--SPI驱动

    总线驱动–SPI驱动 文章目录 总线驱动--SPI驱动 SPI主机驱动 1.spi_master 申请与释放 2.spi_master 的注册与注销 SPI设备驱动 SPI 设备和驱动匹配过程 I.M ...

  5. linux SPI驱动实验

    文章目录 一.linux下SPI驱动框架简介 1. SPI主机驱动 1. spi_master 申请与释放 2.spi_master 的注册与注销 2. SPI设备驱动 3. SPI设备和驱动匹配过程 ...

  6. SPI驱动_linux

    目录 1.Linux下SPI驱动框架简介 1)SPI主机驱动 2)SPI设备驱动 3)SPI设备和驱动匹配过程 2.SPI设备驱动编写流程 1)SPI设备信息描述 2)SPI设备数据收发处理流程 3. ...

  7. nuc972 spi驱动修改提升SPI nor flash读写性能

    在nuc972板子上测试SPI noflash的时候发现启动比nand慢很多,从上电到进入console用了60秒. 通过抓SPI波形发现两个字节之间间隔有5us, 而发送一个字节所花费的时间只有1u ...

  8. Linux驱动修炼之道-SPI驱动框架源码分析(上)

    Linux驱动修炼之道-SPI驱动框架源码分析(上)   SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式.相关通讯设备可工作于m/s模式.主设备发起数据帧,允许多个从设 ...

  9. NanoPi NEO Air使用十一:编写SPI驱动点亮TFT屏幕,ST7789V

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

最新文章

  1. java学习之成员内部类
  2. 【解析】案例4-1.5 顺序存储的二叉树的最近公共祖先问题
  3. 论文阅读课3-GraphRel: Modeling Text as Relational Graphs for(实体关系联合抽取,重叠关系,关系之间的关系,自动提取特征)
  4. zynq linux内核驱动编写,【原创】Linux下驱动Zynq GPIO (Switch、button、led)
  5. 数据挖掘:大数据发展的核心驱动力
  6. 4019-平衡二叉树的高度的计算(C++,附思路)
  7. 简单理解盘索引地址的表示原理
  8. ubuntu ifconfig_VirtualBox中ubuntu的LAMP项目(温度采集)
  9. pytorch中创建多个空的tensor、pycharm从自己写的.py中引用函数下面画红线等问题
  10. ImportError: cannot import name ‘imread’ from ‘scipy.misc’ 解决办法
  11. 【Java程序设计】类与对象的基本概念(上)
  12. java二重积分_用java实现二重积分的计算
  13. ActiveMQ笔记(一)
  14. Python爬虫初学(4)登陆武汉理工大学教务处并转到成绩管理
  15. html中阳历生日转换成农历,公历转农历生日查询器,公历农历换算器?
  16. 有氧运动与无氧运动的区别
  17. (完全解决)argparse中dest是什么意思
  18. 2019强网杯 - 密码学-RSA-Coppersmith
  19. Typora常用快捷键(详细)
  20. Vue3和码上掘金实现猜数字小游戏

热门文章

  1. 黑羽压测 比 jmeter、locust、loadrunner 更简便,性能更强
  2. 如何查找SAP notes
  3. 怎么一键完美抠图?无需PS!快来看看!
  4. 第十一次 作业 视图的应用
  5. 员工满意度模型定期检讨工作规范有哪些?
  6. android 爱普生打印机,安卓打印 爱普生推出智能iPrint新应用
  7. Cisco Packet Tracer 思科中交换机端口安全配置与风暴控制
  8. 1个人,100天业余时间,用Flutter开发完一个商业APP,手握5家大厂offer
  9. idea快捷键,你要是不看就可惜了
  10. 我研究了一个月阿里的岗位JD,不曾想.....