【国产mcu填坑篇】华大单片机(小华半导体)一、SPI的DMA应用(发送主机)HC32L136

  • 最近需要用华大的hc32l136的硬件SPI+DMA传输,瞎写很久没调好,看参考手册,瞎碰一天搞通了。。。

    • 先说下我之前犯的错误,也是最宝贵的经验,供参考
      • 没多看参考手册直接写(即使有点烂仍然提供了最高的参考价值。。。),重点看SPIDMAC章节
      • 错误使用了软件触发传输,测到的现象是前两个字节可以正确发送,后面的无论是发送数量和数据都对不上了,误以为软件触发可用,自己的配置有问题,实际测试软件触发和规格书讲的一样,是不可用的或者说是不可靠的
    • 再说下正确的使用方式,文末会粘上测试代码
      • 关键点就一个,触发方式不要选DmaSWTrig软件触发(至于最后实例能用的这个,从软件的角度看还是软件触发,但官方的角度似乎不认为这是软件触发,或许是软件触发SPI硬件再触发DMA所以叫硬件触发?不管也罢,,,)
  • 特别注意:DMA可能存在硬件bug,在发送的最后一个字节DMA发送完成标记位(硬件置位)有50%概率提前置位,如果CS拉高太快(优化等级太高并且直接操作寄存器来操作CS)会概率导致最后一个字节异常,可以在拉高CS前加一个很小的延时解决,我单独测试DMA时没有异常,随便打开一个定时器中断(频率越高越容易)就可以碰到了,错误时序如下:
    在这里插入图片描述

  • 语文课兴许没及格,下面是参考手册的一些相关描述,没看明白:

  1. 先讲SPI支持软硬件访问
    在这里插入图片描述
    2.再讲只支持硬件块传输模式,且SPI和系统时钟不同频时不支持硬件触发(官方对软件/硬件触发的解释不是很到位,至少和我理解的不太一样)
    在这里插入图片描述
  2. 但是spi时钟和系统时钟必然是不同频的,那硬件触发到底能不能用呢?
    在这里插入图片描述
  3. 再看,所谓的软件/硬件DMA传输模式就是软件/硬件请求方式不同,似乎哪个也不支持了。。。软硬件触发和软硬件传输似乎没有关系?
    在这里插入图片描述
  • 最后,还是实践出真知。。。
  • 测试程序参考,每200ms用SPI+DMA发送24个字节:
#define SPI_HANDLE M0P_SPI1
#define DMA_HANDLE DmaCh1

uint8_t data_tx_test[24] =
{
  0x11,0x22,0x23,0x44,0x55,0x66,0x77,0x88,
  0x11,0x22,0x23,0x44,0x55,0x66,0x77,0x88,
  0x11,0x22,0x23,0x44,0x55,0x66,0x77,0x88,
};

//主要是CS/CLK/MOSI三个脚,不相干引脚忽略即可
static void App_GpioInit(void)		
{
    stc_gpio_cfg_t           stcPortCfg;
    
    DDL_ZERO_STRUCT(stcPortCfg);							//结构体初始化清零
    
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE); //GPIO 外设时钟使能
    
    stcPortCfg.enDrv = GpioDrvH;
    stcPortCfg.enDir  = GpioDirOut;
	
    Gpio_Init(LCD_BK_PORT, LCD_BK_PIN, &stcPortCfg);  
	
    Gpio_Init(LCD_CS_PORT, LCD_CS_PIN, &stcPortCfg);  
	Gpio_SetAfMode(LCD_CS_PORT, LCD_CS_PIN,GpioAf1); 				//CS
	
    Gpio_Init(LCD_RESET_PORT, LCD_RESET_PIN, &stcPortCfg);  
	
    Gpio_Init(LCD_WR_PORT, LCD_WR_PIN, &stcPortCfg);  
	
    Gpio_Init(LCD_SCK_PORT, LCD_SCK_PIN, &stcPortCfg);  
	Gpio_SetAfMode(LCD_SCK_PORT, LCD_SCK_PIN,GpioAf1);  			//CLK
	
    Gpio_Init(LCD_SDA_PORT, LCD_SDA_PIN, &stcPortCfg);  
	Gpio_SetAfMode(LCD_SDA_PORT, LCD_SDA_PIN,GpioAf1);  			//MOSI	
}

static void App_SPIInit(void)
{
    stc_spi_cfg_t  SpiInitStruct;

    Sysctrl_SetPeripheralGate(SysctrlPeripheralSpi1,TRUE);

    //SPI0模块配置:主机
    SpiInitStruct.enSpiMode = SpiMskMaster;   		//配置位主机模式
    SpiInitStruct.enPclkDiv = SpiClkMskDiv2;  		//波特率:fsys/2
    SpiInitStruct.enCPOL    = SpiMskcpolhigh;  		//极性
	SpiInitStruct.enCPHA 	= SpiMskCphasecond; 	//第二电平采样
    Spi_Init(SPI_HANDLE, &SpiInitStruct);
	
	Spi_FuncEnable(SPI_HANDLE,SpiMskDmaTxEn);		//这里只使用了发送功能
}

static void App_DmaCfg(void)
{ 
    stc_dma_cfg_t stcDmaCfg;
   
    Sysctrl_SetPeripheralGate(SysctrlPeripheralDma,TRUE); 			//打开DMA时钟
  
    DDL_ZERO_STRUCT(stcDmaCfg);
  
    stcDmaCfg.enMode =  DmaMskBlock;                           		//选择块传输
    stcDmaCfg.u16BlockSize = 1;                             		//块传输个数
    stcDmaCfg.u16TransferCnt = 24;                    				//块传输次数,一次传输数据大小为 块传输个数*BUFFER_SIZE
    stcDmaCfg.enTransferWidth = DmaMsk8Bit;                   		//传输数据的宽度,此处选择字(8Bit)宽度
    stcDmaCfg.enSrcAddrMode = DmaMskSrcAddrInc;                		//源地址自增
    stcDmaCfg.enDstAddrMode = DmaMskDstAddrFix;                		//目的地址固定
    stcDmaCfg.enDestAddrReloadCtl = DmaMskDstAddrReloadEnable;		//使能重新加载传输目的地址
    stcDmaCfg.enSrcAddrReloadCtl = DmaMskSrcAddrReloadEnable;		//使能重新加载传输源地址
    stcDmaCfg.enSrcBcTcReloadCtl = DmaMskBcTcReloadEnable;			//使能重新加载BC/TC值
    stcDmaCfg.u32SrcAddress = (uint32_t)&data_tx_test[0]; 			//指定传输源地址
    stcDmaCfg.u32DstAddress = (uint32_t)&(M0P_SPI1->DATA);    		//指定传输目的地址
    stcDmaCfg.enRequestNum = DmaSPI1TXTrig;                        	//设置为硬件触发
    stcDmaCfg.enTransferMode = DmaMskOneTransfer;              		//dma只传输一次,DMAC传输完成时清除CONFA:ENS位
    stcDmaCfg.enPriority = DmaMskPriorityFix;                  		//各通道固定优先级,CH0优先级 > CH1优先级
    
    Dma_InitChannel(DMA_HANDLE,&stcDmaCfg);                        	//初始化dma通道0
  
    Dma_Enable();
    //Dma_EnableChannel(DMA_HANDLE);								//开启通道即开启一次发送
}

void dma_test(void)
{
	en_dma_stat_t ste;
	while(1)
	{ 

		delay1ms(200);
		M0P_SPI1->SSN = FALSE;
		Dma_EnableChannel(DMA_HANDLE);								//启动传输,所以这种方式到底算软件还是硬件??
		
		ste = Dma_GetStat(DMA_HANDLE);
		while(ste != DmaTransferComplete)
		{
			ste = Dma_GetStat(DMA_HANDLE);
		}
		delay10us(1);												//!!!这个延时是必要的,防止数据多时上面的等待不能有效判断最后一个字节
		M0P_SPI1->SSN = TRUE;
		
	}
}


void demo(void)
{
	App_GpioInit();
	App_DmaCfg();
	App_SPIInit();
	
	dma_test();
}
  • 实测SPI主机发送ok:
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记edusrc一处信息泄露

一、信息收集 在搜索某一学校的资产时,找到了一处学工系统。 登录进去,发现有两种登陆方式,一种是统一身份认证,一种是DB认证。 统一身份认证是需要通过学生的学号和密码进行登录的,利用谷歌语法可以搜索到相关学…

多特征变量序列预测(四)Transformer-BiLSTM风速预测模型

目录 往期精彩内容: 前言 1 多特征变量数据集制作与预处理 1.1 导入数据 1.2 数据集制作与预处理 2 基于Pytorch的Transformer BiLSTM 预测模型 2.1 定义Transformer BiLSTM预测模型 2.2 设置参数,训练模型 3 模型评估与可视化 3.1 结果可视…

计算机视觉技术的应用前景如何?

计算机视觉技术在各个领域都有广阔的应用前景。以下是一些计算机视觉技术可能的应用: 1. 安全和监控:计算机视觉可以用于视频监控、入侵检测、人脸识别等安全和监控领域。它可以帮助监测和识别异常行为或威胁,并提供实时警报。 2. 自动驾驶和…

如何使用iPad通过Code App+cpolar实现公网地址远程访问vscode

🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​💫个人格言:“没有罗马,那就自己创造罗马~” 文章目录 1. 在iPad下载Code APP2.安装cpolar内网穿透2.1 cpolar 安装2.2 创建TCP隧道 3. iPad远程vscode4. …

BootStrap 实现轮播图

Bootstrap中文网 1、下载BootStrap 2、引入相关文件 在下载好的文件夹中找到下面的文件&#xff0c;复制到自己的项目中并引入 <link rel"stylesheet" href"bootstrap/css/bootstrap.min.css" /><script src"bootstrap/js/jquery.min.js…

中仕公考:2024年度河南省公务员考试公告发布!共招录9900人!

河南省2024年度统一考试录用公务员公告于今日发布&#xff0c;共计划招录9900人。 报名时间&#xff1a;1月18日9&#xff1a;00-1月24日17&#xff1a;00 笔试时间&#xff1a;3月16日-3月17日 报名方式&#xff1a;登录“河南人事考试网”进行网上报名 2024年省考29.5%的…

HTML--JavaScript--语法基础

变量与常量 这个基本上没啥问题 变量命名规则&#xff1a; 变量由字母、数字、下划线、$组成&#xff0c;且变量第一个字符不能为数字 变量不能是系统关键字和保留字 语法&#xff1a; var 变量名 值&#xff1b;所有Javacript变量都由var声明 定义赋值字符串&#xff1a; …

OpenHarmony——基于HDF驱动框架构建的Display驱动模型

概述 功能简介 LCD&#xff08;Liquid Crystal Display&#xff09;驱动编程&#xff0c;通过对显示器上电、初始化显示器驱动IC&#xff08;Integrated Circuit&#xff09;内部寄存器等操作&#xff0c;使其可以正常工作。 基于HDF&#xff08;Hardware Driver Foundation…

1-Docker-基础

本文内容多处参考黑马程序员的公开资料&#xff0c;仅用来个人梳理&#xff0c;原资料地址&#xff1a;https://b11et3un53m.feishu.cn/wiki/MWQIw4Zvhil0I5ktPHwcoqZdnec Docker介绍 为什么要用Docker&#xff1f; 以Mysql安装为例&#xff0c;想要在Linux系统上安装Mysql&…

前任开发在代码里下毒了,支付下单居然没加幂等

分享是最有效的学习方式。 故事 又是一个风和日丽没好的一天&#xff0c;小猫戴着耳机&#xff0c;安逸地听着音乐&#xff0c;撸着代码&#xff0c;这种没有会议的日子真的是巴适得板。 不料祸从天降&#xff0c;组长火急火燎地跑过来找到了小猫。“快排查一下&#xff0c;目…

【Emgu CV教程】5.1、几何变换之平移

图像的几何变换对于图像处理来说&#xff0c;也是最基础的那一档次&#xff0c;包括平移、旋转、缩放、透视变换等等&#xff0c;也就是对图像整理形状的改变&#xff0c;用到的函数都比较简单&#xff0c;理解起来也很容易。但是为了凑字数&#xff0c;还是一个函数一个函数的…

类和对象特性

#include<iostream> #include<string> using namespace std; class peron{ public:peron(string person){cout << "peron调用构造函数" << endl;tperson person;}~peron(){cout << "peron调用析构函数" << endl;}//手…

22k+star炒鸡好用的开源的网盘神器FileBrowser Docker自建个人网盘神器教程

目录 简介 1.拉取镜像 2.创建并启动容器 2.1创建目录 2.2启初始化一个容器用于导出配置文件和数据库&#xff0c;只挂载数据目录 2.3先将数据库文件和配置文件复制出来 2.4停止容器并删除容器 2.5创建完整的容器 3.愉快地使用 3.1示例&#xff1a; 3.2图片预览 3.3json…

快速更改flutter已有项目的项目名称和id等

如果你使用了别人已有的仓库模板或者想更改现有项目的名称&#xff0c;是一件非常繁琐的工作&#xff0c;需要修改全平台的文件还是相当麻烦的&#xff0c;所以这里推荐一个小工具&#xff0c;可以帮助大家快速实现更改项目名称的目的&#xff0c;这个工具地址&#xff1a;rena…

YOLOv8改进 | 检测头篇 | 利用DBB重参数化模块魔改检测头实现暴力涨点 (支持检测、分割、关键点检测)

一、本文介绍 本文给大家带来的改进机制是二次创新的机制,二次创新是我们发表论文中关键的一环,本文给大家带来的二次创新机制是通过DiverseBranchBlock(DBB)模块来改进我们的检测头形成一个新的检测头Detect_DBB,其中DBB是一种重参数化模块,其训练时采用复杂结构,推理时…

Java Chassis 3技术解密:注册中心分区隔离

原文链接&#xff1a;Java Chassis 3技术解密&#xff1a;注册中心分区隔离-云社区-华为云 注册中心负责实例的注册和发现&#xff0c;对微服务可靠运行起到举足轻重的作用。实例变更感知周期是注册中心最重要的技术指标之一。感知周期代表提供者的实例注册或者下线后&#xf…

MVP的思维方式

MVP释义&#xff1a;做最小可行产品&#xff0c;做最有价值的人 首席产品官MVP社区 2023-09-04 08:53 北京 MVP&#xff0c;即“Minimum Viable Product”&#xff0c;中文翻译为“最小可行产品”&#xff0c;指的是在产品开发过程中&#xff0c;将资源集中在最核心的功能上&a…

Redis教程——Redis bitmap位图操作(图解)

在平时开发过程中&#xff0c;经常会有一些 bool 类型数据需要存取。比如记录用户一年内签到的次数&#xff0c;签了是 1&#xff0c;没签是 0。如果使用 key-value 来存储&#xff0c;那么每个用户都要记录 365 次&#xff0c;当用户成百上亿时&#xff0c;需要的存储空间将非…

帆软后台(外观配置-主题)文件上传漏洞

漏洞利用 帆软上传主题获取shell&#xff08;管理系统-外观配置&#xff09; 添加主题上传的压缩包中放入shell.jsp马 &#xff08;没有添加主题功能直接构造数据包&#xff09; POST /WebReport/ReportServer?opfr_attach&cmdah_upload&filenametest.zip&widt…

uniapp中,子组件给父页面传值(父组件)

前言 最近在做的一个小程序项目中&#xff0c;有一个身份切换的功能&#xff0c;点击切换按钮时&#xff0c;子组件向父组件传递身份信息&#xff0c;父页面依据这个身份信息对页面进行显示与隐藏。 具体实现 子组件中定义一个点击事件,在这里是identitySwitching()方法 &l…