STM32 执行规定 DMA_Cmd(DMA1_Channel5,ENABLE);时寄存器没被写入

由于CPU复位后GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的

但是我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数

由于CPU复位后GPIO缺省都昰浮空输入模式,因此下面这个步骤不是必须的

但是我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数

/* 第3步已经莋了因此这步可以不做

/* CPU的小缺陷:串口配置好,如果直接Send则第1个字节发送不出去

如下语句解决第1个字节无法正确发送出去的问题 */

/* CPU的小缺陷:串口配置好,如果直接Send则第1个字节发送不出去

如下语句解决第1个字节无法正确发送出去的问题 */

数组定义,含义如题名:

//将数据送DMA存储地址

//USART用DMA传输替代查询方式发送克服被高优先级中断而产生丢帧现象。

//将数据送DMA存储地址

//USART用DMA传输替代查询方式发送克服被高优先级Φ断而产生丢帧现象。

本章节为大家讲解标准SPI接线方式驅动模数转换器DAC856X制作了中断和DMA两种驱动方式。

74.1 初学者重要提示

74.11 DAC856X驱动移植和使用(中断更新方式)

74.13 实验例程设计框架

74.1 初学者重要提示

1、  学習本章节前务必优先学习第72章。

  •  H7的SPI外设比F4系列的灵活性强太多了主要表现在两个方面:数据的传输支持了4-32bit,特别是那个NSS片选引脚超強劲,可以做各种时间插入灵活应对了市场上这类芯片的需求。
  •  DMA这块相比F4系列有了质的飞跃,支持了DMAMUX这个DMAMUX除了带来灵活的触发源选擇,还支持了各种触发事件和同步触发功能本章配套例子的触发周期控制就是利用了DMAMUX的同步触发功能。

5、  本章配套了中断和DMA两种更新方式的案例DMA实现方式与中断更新方式完全不同,因为DMA方式要使用硬件SPI1 NSS片选引脚驱动DAC856X而中断更新方式使用公共的总线驱动文件bsp_spi_bus.c,片选是通過通用IO方式控制支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI设备。大家在看例子的时候要注意

6、  对于本章教程配套例子的SPI DMA方式,这里特别注意一点定时器觸发一次,就会让SPI以DMA方式传输24bit数据

7、  DAC856X数据手册,模块原理图和接线图都已经放到本章教程配置例子的Doc文件里

注,这些知识翻译自TI的英攵技术手册

自动测试设备或仪器通常使用R2R MDAC。MDAC型制造商能够设计具有±1 LSB的高分辨率积分非线性(INL)和差分非线性(DNL)DAC通过使用合适的外蔀放大器,MDAC能够实现较短的建立时间(<0.3 ms)和大于10 MHz的带宽并且通过为MDAC的外部运算放大器提供不同电源电压和高输出电流可以增强DAC功能。

MDAC产苼的电流与用户设置的数字编码外部放大器以及RFB(在MDAC内部)将DAC的电流输出信号转换为可用的电压。

这类DAC的缺点是会有稳定性问题

通常茬工业应用中使用R2R backDAC。其它一些应用还包括仪器和数字控制校准使用这类DAC,每次新更新会将2R支路切换到参考电压高(VREF-H)或参考电压低(VREF-L)注意R-2R梯子的布置与MDAC相比是倒置的。这就是名字backDAC的由来这种架构很容易制造。

这类DAC的缺点是毛刺脉冲问题(注此贴有详细解释:):

String型DAC最适合便携式仪器,闭环伺服控制和过程控制下图显示了一个3bit String DAC的模型,数字输入代码101b被解码为5/8 VREFString DAC的输出级放大器隔离了来自输出负载嘚内部电阻元件。

String DAC是一种低功耗解决方案可确保单调性在整个输入代码中具有良好的DNL(差分非线性)性能范围。毛刺能量通常低于其它DAC類型

但是,INL(积分非线性)通常较大具体取决于电阻式片上匹配,另一方面控制回路中的DAC可减轻线性度影响。String DAC的噪声也相对较大洇为电阻串的阻抗很高,所以该值很高但String DAC功耗低且非常小的故障能量。

一些常见的DAC技术术语需要大家见到了大概了解是什么意思。

这個参数是专门用来定义温飘的ppm全称是parts per million,即百万分之一比如2ppm℃就是2 x 10^-6 ,反映到DAC8563上定义如下:

使用DAC进行设计时,您期望输出从一个值单调迻至下一个值但实际电路并非总是如此。在某些代码范围内出现过冲或下冲(量化为毛刺脉冲)并不少见。主要以下面两种形式呈现:

具体原因分析在这个帖子里面进行了讲解(内容较多就不整理到教程里面了):。

偏移误差为标称偏移点与实际偏移点之间的差此錯误以相同的数量影响所有代码,通常可以通过修正来补偿处理如果无法修正,则该误差称为零刻度误差

增益误差定义为传输时标称增益点与实际增益点之差。

差分非线性误差为实际步长宽度(对于ADC)或步长高度(对于DAC)与1 LSB的理想值之间的差值 因此,如果阶跃宽度或高度恰好为1 LSB则差分非线性误差为零。 如果DNL超过1 LSB转换器可能变得非单调。这意味着增加了输入的幅度但输出的大小可能变小

积分非线性误差是从一个传输点到相对应的理想传输曲线的最大偏差距离,不考虑偏置误差和增益误差 这个参数对最佳传输函数或端点传输函数囿一定参考意义。

绝对精度误差是包括偏移增益,积分线性等误差的总体误差

DAC的输出量可以为0到2.5V或者0到5V,通过外置运放实现了±10V输絀。原理图下载:

3、输出电压范围 : -10V ~ +10V 【客户可以自己更改为 0-10V输出范围使用烙铁切换2个焊点即可,无需更换元器件】

4、输出驱动能力:带運放驱动最大输出电流10mA,负载电阻>1K欧姆

1、输出和供电电压无关;模块内带正负12V升压电路

2、自适应单片机的电平(2.7 - 5V 均可以)

3、输出电压可抵达正负10V

4、上电时缺省输出0V (在软件未启动时)

5、引出正负12V电源排针方便客户使用

2、无论是用DAC8562还是DAC8563芯片,只要软件不启动本模块输出电壓缺省状态都是0V。

3、CLR脚悬浮时电压在1.9V左右,容易受到干扰导致输出被清零因此即使不用CLR控制功能,这个CLR脚也需要接固定电平(推荐接GND)CLR是边沿触发,仅在下降沿信号出现执行规定清零

V7板子上DAC856X模块的插座的原理图如下:

实际对应开发的位置如下:

驱动DAC856X需要对下面这些知识点有个认识。

  •   自带2.5V的内部参考基准典型的温飘是4ppm/℃。使用内部2.5V参考基准的情况下根据增益设置不同,DAC的输出量可以为0到2.5V或者0到5V
  •   鼡户可以根据需要外接运放实现常用的±5V,±10V或者±15V输出
  •   上电复位数值0V或者中间值。

DAC856X主要有下面两种封装形式:

异步清除输入下降沿囿效,触发后DAC8562输出最低电压值,DAC8563输出中间值用户写入操作的的第24个时钟下降沿将退出清除模式,激活清除模式将终止写操作

串行时鍾输入,每个时钟下降沿将数据写到的24bit的输入移位寄存器

同步模式下,数据更新发生在第24个SCLK周期的下降沿之后伴随着SYNC的下降沿。 这种哃步更新不需要LDAC而LDAC必须永久接地,或者将命令发送到设备时保持低电平异步模式下,LDAC是低电平触发用于同步DAC更新,可以编写多个单通道命令进行设置然后在LDAC引脚上产生一个下降沿将同步更新DAC输出寄存器。

时钟输入端支持50MHz。

低电平有效当SYNC变为低电平时,它使能输叺移位寄存器并且数据采样在随后的时钟下降沿。 DAC输出在第24个时钟下降沿之后更新 如果SYNC在第23个时钟沿之前变高,SYNC的上升沿将充当中断并且DAC756x,DAC816x和DAC856x器件将忽略写序列

双向电压参考引脚,如果使用内部电压基准此引脚是输出2.5V。

DAC856X的计算公式如下:

如果使用内部参考电压那么此数值是2.5V,如果使用外部参考电压由VREFIN引脚的输入决定。

增益设置禁止内部电压基准后,默认增益是1如果使能内部电压基准后,默认增益是2具体增益是1还是2,可以通过DAC856X的寄存器设置

这个时序里面有三个参数尤其重要,后面时序配置要用到

支持最高的串行时钟昰50MHz。

每传输24bit数据后SYNC要保持一段时间的高电平,DAC856X要求至少要80ns

SYNC低电平有效到SCLK第1个下降沿信号的时间,最小值13ns

DAC856X的寄存器配置看下面的图表即可,一目了然(X表示为0或者为1均可):

DAC856X的程序驱动框架设计如下:

有了这个框图程序设计就比较好理解了。

spi总线配置通过如下两个函數实现:

* 功能说明: 配置SPI总线 * 功能说明: 配置SPI总线参数,时钟分频时钟相位和时钟极性。 * _CLKPhase 时钟相位支持的参数如下: /* 提高执行规定效率,只有在SPI硬件参数发生变化时才执行规定HAL_Init */

关于这两个函数有以下两点要做个说明:

  •   函数bsp_InitSPIParam提供了时钟分频,时钟相位和时钟极性配置驱動不同外设芯片时,基本上调整这三个参数就够当SPI接口上接了多个不同类型的芯片时,通过此函数可以方便的切换配置

74.6.2 第2步:SPI总线的查询,中断和DMA方式设置

注:对于DAC8563请使用查询方式。

SPI驱动的查询中断和DMA方式主要通过函数bsp_spiTransfer实现数据传输:

* 选择DMA,中断或者查询方式 * 功能說明: 启动数据传输

通过开头宏定义可以方便的切换中断查询和DMA方式。其中查询和中断方式比较好理解而DMA方式要特别注意两点:

  •   通过本掱册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上因为本工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2
  •   由于程序里面开启了数據Cache,会造成DMA和CPU访问SRAM4数据不一致的问题特此将SRAM4空间关闭Cache。

首先回忆下STM32H7支持的4种时序配置

SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕獲传输的第1个数据

SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据

SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕獲传输的第1个数据

SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据

有了H7支持的时序配置,再来看下DAC856X的时序图:

首先DAC856X昰下降升沿做数据采集所以STM32H7的可选的配置就是:

对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序圖和DAC856X的数据手册两种情况下都可以正常运行。经过实际测试STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1

74.6.4 第4步:单SPI接ロ管理多个SPI设备的切换机制

单SPI接口管理多个SPI设备最麻烦的地方是不同设备的时钟分配,时钟极性和时钟相位并不相同对此的解决解决办法是在片选阶段配置切换,比如DAC856X的片选:

通过这种方式就有效的解决了单SPI接口管理多设备的问题因为给每个设备都配了一个独立的片选引脚,这样就可以为每个设备都配置这么一个片选配置

但是频繁配置也比较繁琐,所以函数bsp_InitSPIParam里面做了特别处理当前配置与之前配置相哃的情况下无需重复配置。

DAC856X的双通道数据更新通过下面的函数实现:

* 功能说明: 设置DAC输出并立即更新。

函数实现比较简单每次更新发送24bit數据即可。

DAC856X的DMA驱动方式略复杂跟中断更新方式完全不同,要使用硬件SPI1 NSS引脚驱动DAC8562的片选所有专门做了一个驱动文件来实现,程序驱动框架设计如下:

有了这个框图程序设计就比较好理解了。

spi总线配置通过如下两个函数实现:

/* 选择内部参考并复位2个DAC的增益=2 (复位时内部參考是禁止的) */ * 功能说明: 配置SPI总线参数,时钟分频时钟相位和时钟极性。 * _CLKPhase 时钟相位支持的参数如下: /* MSS, 插入到NSS有效边沿和第一个数据开始の间的额外延迟,单位SPI时钟周期个数 */ /* MIDI, 两个连续数据帧之间插入的最小时间延迟单位SPI时钟周期个数 */

这两个配置函数里面最重要的是置红的幾个配置选项,这里依次为大家做个说明:

对于SPI1来说里面的FIFO大小是16字节,那么SPI数据传输配置为24bit的话FIFO最多可以存储5个24bit,因此这个fifo阀值要設置为5

我们这里要使用SPI的硬件片选引脚SPI_NSS。

插入到NSS有效边沿和第一个数据开始之间的额外延迟单位SPI时钟周期个数。

根据本章4.5.4小节里面的t(5)偠求片选有效到SCLK第1个下降沿信号的时间,最小值13ns由于DAC856X的最高时钟是50MHz,即20ns的分辨率并且实际程序中,我们选择的是第2个边沿做数据采集所以这里配置为0即可,也就是无需插入时间

两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数

根据本章4.5.4小节里面的t(4)要求,每传输24bit数据后片选要保持一段时间的高电平,DAC856X要求至少要80ns也是说,如果我们以50MHz驱动DAC856X这里至少要配置为4个时钟周期,推荐值为5及其以上即可我们这里直接配置为10个时钟周期(配置为5也没问题的)。

这里特别注意一点定时器触发一次,就会让SPI以DMA方式传输24bit输出

TIM12的觸发配置如下:

* 功能说明: 配置TIM12,用于触发DMAMUX的请求发生器

这个函数支持的触发频率很宽对于DAC856X来说,如果样本点设置为100个的话此函数推荐嘚触发频率是100Hz到1MHz,具体可以支持到最高触发速度计算看本章4.7.7小节即可

这段程序里面最关键的就是置红的部分。作用是配置DMAMUX的同步触发功能触发周期由TIM12控制。

由于通用DMA1和DMA2仅支持8bit16bit和32bit数据传输,我们这里要传输24bit数据解决的关键就是配置DMA为传输宽度为32bit,并将传输的数据由24bit再補一个8bit的任意值组成32bit即可实际的传输会由SPI完成。

* 3表示通道1和2都输出 * 4表示通道1和2都输出并且附加一个控制命令,有效防止传输错误时恢複 * _ulFreq 触发频率,范围2KB- 1MHz注意这个参数是触发频率,并不是波形周期触发一次,SPI DMA /* 插入关键命令防止传输错误 */ /* 数据填充完毕后,插入关键命令数据输出过程中被8256误识别为命令处理*/

因为工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上:

由于程序里面开启了数据Cache会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache

注:与本章4.6.3小节内容是一样的。

艏先回忆下STM32H7支持的4种时序配置

SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据

SCK引脚在空闲状态处于低电平,SCK引脚的苐2个边沿捕获传输的第1个数据

SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据

SCK引脚在空闲状态处于低电平,SCK引脚的苐1个边沿捕获传输的第1个数据

有了H7支持的时序配置,再来看下DAC856X的时序图:

首先DAC856X是下降升沿做数据采集所以STM32H7的可选的配置就是:

对于这兩种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC856X的数据手册两种情况下都可以正常运行。经过实际測试STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1

这里特别注意一点,定时器触发一次就会让SPI以DMA方式传输24bit数据。

根据仩面的配置传输一帧(24bit)数据需要的时间:

认识到这些后,实际输出的波形周期也比较好算了比如我们设置10个样本点为一个周期,那麼触发速度为1MHz的时候那么波形周期就是100KHz。

DAC856X模块的输出电压范围是-10V到10V对应的编码值范围是0到65535,为了方便大家做互转专门做了两个函数:

* 功能说明: 将DAC值换算为电压值,单位0.1mV * 功能说明: 将电压值转换为DAC置

74.7.9 第9步:防止SPI DMA批量数据传输错误解决办法

使用SPI DMA批量数据传输过程中要防止┅些数据被DAC856X错误识别成关键命令,从而造成DAC856X工作异常其中最重要的一个关键命令就下面这个:

/* 选择内部参考并复位2个DAC的增益=2 (复位时,內部参考是禁止的) */
 
/* 插入关键命令防止传输错误 */ 
 
 
 
 
 
 
 
 /* 数据填充完毕后,插入关键命令数据输出过程中被8256误识别为命令处理*/
 
 

解决办法是在批量數据的末尾附一个命令,通过这种方式可以有效防止DAC856X工作异常

SPI总线驱动文件bsp_spi_bus.c主要实现了如下几个API供用户调用:

此函数主要用于SPI总线的初始化,在bsp.c文件调用一次即可

此函数用于SPI总线的配置。

  •   第1个参数SPI总线的分频设置支持的参数如下:
  •   第2个参数用于时钟相位配置,支持的參数如下:
  •   第3个参数是时钟极性配置支持的参数如下:

此函数用于启动SPI数据传输,支持查询中断和DMA方式传输。

主要用于DAC856X的初始化调鼡前务必先调用函数bsp_InitSPIBus初始化SPI外设。

此函数用于片选DAC8562

  •   第1个参数为0表示选中,为1表示取消选中

此函数用于向SPI总线发送24个bit数据。

此函数用于設置DAC输出并立即更新。

  •   第1个参数为0表示通道1为1表示通道2。
  •   第2个参数是DAC数值设置范围0到65535,0对应最小电压值65535对应最大电压值。

此函数鼡于将DAC值换算为电压值单位0.1mV。

  •   返回值返回电压值,单位0.1mV

此函数用于将电压值转换为DAC值。

DAC856X驱动文件bsp_spidam_dac8562.c涉及到的函数比较多我们主要介紹用到的如下几个函数:

主要用于DAC856X的初始化。

此函数用于SPI DMA方式数据发送

  •   第1个参数用于选择的通道:
  •   4表示通道1和2都输出,并且附加一个控淛命令有效防止传输错误时恢复。
  •   第2个参数表示通道1数据缓冲地址
  •   第3个参数表示通道2数据缓冲地址。
  •   第4个参数表示通道1数据大小
  •   第5個参数表示通道2数据大小。
  •   第6个参数表示触发频率推荐范围100Hz- 1MHz,注意这个参数是触发频率并不是波形周期。这里触发一次SPI DMA传输一次24bit数據。

此函数用于向SPI总线发送24个bit数据

  • 第1个参数为24bit数据。

此函数用于设置DAC输出并立即更新。

  • 第1个参数为0表示通道1为1表示通道2。
  •  第2个参数昰DAC数值设置范围0到65535,0对应最小电压值65535对应最大电压值。

第2步:根据使用的第几个SPISPI时钟,SPI引脚和DMA通道等修改bsp_spi_bus.c文件开头的宏定义

* 时钟,引脚DMA,中断等宏定义

第3步:根据芯片支持的时钟速度时钟相位和时钟极性配置函数DAC8562_SetCS。

第4步:根据使用的片选CLR和LDAC引脚,修改bsp_spi_dac8562.c文件开頭的宏定义

/* LDAC 使用扩展IO ,特别注意我们这里是用的扩展IO控制的 */

第5步:如果使用DMA方式的话,请不要使用TCM RAM因为通用DMA1和DMA2不支持。并为了防止DMA囷CPU同时访问DMA缓冲造成的数据一致性问题将这块空间关闭Cache处理,比如使用的SRAM4:

第6步:初始化SPI

/* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
 

第7步:DAC856X驱动主要用到HAL库的SPI驱动文件简单省事些可以添加所有HAL库C源文件进来。

第8步:应用方法看本章节配套例子即可

* 时鍾,引脚DMA,中断等宏定义
/* LDAC 使用扩展IO 特别注意,我们这里是用的扩展IO控制的 */

第4步:如果使用DMA方式的话请不要使用TCM RAM,因为通用DMA1和DMA2不支持并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理比如使用的SRAM4:

第5步:初始化SPI。

/* 针对不同的应用程序添加需偠的底层驱动模块初始化函数 */
 

 第6步:DAC856X驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来

 第7步:应用方法看本章节配套唎子即可

通过程序设计框架,让大家先对配套例程有一个全面的认识然后再理解细节,本次实验例程的设计框架如下:

  第1阶段上电启動阶段:

  • 这部分在第14章进行了详细说明。
  •  第1部分硬件初始化,主要是MPUCache,HAL库系统时钟,滴答定时器和LED
  •  第2部分,应用程序设计部分實现DAC856X的简易信号发生器功能。

注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明

  1. 双通道DAC,轨到轨输出16bit分辨率,支持50MHz的SPI时钟速喥
  2. 自带2.5V的内部参考基准,典型的温飘是4ppm/℃使用内部2.5V参考基准的情况下,根据增益设置不同DAC的输出量可以为0到2.5V或者0到5V。
  3. 无论是用DAC8562还是DAC8563芯片只要软件不启动,本模块输出电压缺省状态都是0V
  4. CLR脚悬浮时,电压在1.9V左右容易受到干扰导致输出被清零。因此即使不用CLR控制功能这个CLR脚也需要接固定电平(推荐接GND)。CLR是边沿触发仅在下降沿信号出现执行规定清零。
  1. 启动一个自动重装软件定时器每100ms翻转一次LED2。
  2. K1鍵按下双通道输出,通道1输出方波通道2输出正弦波。
  3. K2键按下双通道输出方波。
  4. K3键按下双通道输出正弦波。
  5. 摇杆上键按下通道1停圵方波,通道2停止输出
  6. 摇杆下键按下,双通道输出直流
  7. 摇杆OK键按下,重新初始化

上电后串口打印的信息:

波特率 115200,数据位 8奇偶校驗位无,停止位 1

  系统栈大小分配:

硬件外设的初始化是在 bsp.c 文件实现:

* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量只需要调用一次 - 设置NVIV优先级分组为4。 配置系统时钟到400MHz - 可用于代码执行规定时间测量MDK5.25及其以上版本才支持,IAR不支持 - 默认不开启,如果要使能此选项务必看V7开发板用户手册第xx章 bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前因为按钮检测是通过滴答定时器扫描 */ /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */

按键处理是在滴答定时器中断里面实现每10ms执行规定一次检测。

* 功能说奣: 该函数每隔10ms被Systick中断调用1次详见 bsp_timer.c的定时中断服务程序。一些处理时间要求 * 不严格的任务可以放在此函数比如:按键扫描、蜂鸣器鸣叫控制等。
  •   启动一个自动重装软件定时器每100ms翻转一次LED2。
  •   K1键按下双通道输出,通道1输出方波通道2输出正弦波。
  •   K2键按下双通道输出方波。
  •   K3键按下双通道输出正弦波。
  •   摇杆上键按下通道1停止方波,通道2停止输出
  •   摇杆下键按下,双通道输出直流
  •   摇杆OK键按下,重新初始囮
* 功能说明: c程序入口 * 返 回 值: 错误代码(无需处理) /* 生成正弦波数据 */ 3 表示通道1和2都输出 4 表示通道1和2都输出,并且附加一个控制命令有效防止傳输错误时恢复,即使插拔模块也不影响 定时器触发速度1MHz,触发1次是一组24bit数据的传输 /* 判断定时器超时时间 */ /* 按键滤波和检测由后台systick中断垺务程序实现,我们只需要调用bsp_GetKey读取键值即可 */ /* 生成正弦波数据 */ /* 上电默认双通道输出,触发速度1MHz */ /* 生成正弦波数据 */ /* 上电默认双通道输出触發速度1MHz */ /* 上电默认双通道输出,触发速度1MHz */ /* 其它的键值不处理 */

注:本章是配套了两个例子的这里我们以SPI DMA方式进行说明。

  1. 双通道DAC轨到轨输出,16bit分辨率支持50MHz的SPI时钟速度。
  2. 自带2.5V的内部参考基准典型的温飘是4ppm/℃,使用内部2.5V参考基准的情况下根据增益设置不同,DAC的输出量可以为0箌2.5V或者0到5V
  3. 无论是用DAC8562还是DAC8563芯片,只要软件不启动本模块输出电压缺省状态都是0V。
  4. CLR脚悬浮时电压在1.9V左右,容易受到干扰导致输出被清零因此即使不用CLR控制功能,这个CLR脚也需要接固定电平(推荐接GND)CLR是边沿触发,仅在下降沿信号出现执行规定清零
  1. 启动一个自动重装软件定时器,每100ms翻转一次LED2
  2. K1键按下,双通道输出通道1输出方波,通道2输出正弦波
  3. K2键按下,双通道输出方波
  4. K3键按下,双通道输出正弦波
  5. 摇杆上键按下,通道1停止方波通道2停止输出。
  6. 摇杆下键按下双通道输出直流。
  7. 摇杆OK键按下重新初始化。

上电后串口打印的信息:

波特率 115200数据位 8,奇偶校验位无停止位 1。

  系统栈大小分配:

硬件外设的初始化是在 bsp.c 文件实现:

* 功能说明: 初始化所有的硬件设备该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 - 设置NVIV优先级分组为4 配置系统时钟到400MHz - 可用于代码执行规定时间测量,MDK5.25忣其以上版本才支持IAR不支持。 - 默认不开启如果要使能此选项,务必看V7开发板用户手册第xx章 bsp_InitKey(); /* 按键初始化要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */ /* 针对不同的应用程序添加需要的底层驱动模块初始化函数 */

按键处理是在滴答定时器中断里面实现,每10ms执荇规定一次检测

* 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序一些处理时间要求 * 不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等
  •   启动一个自动重装软件定时器,每100ms翻转一次LED2
  •   K1键按下,双通道输出通道1输出方波,通道2输出正弦波
  •   K2鍵按下,双通道输出方波
  •   K3键按下,双通道输出正弦波
  •   摇杆上键按下,通道1停止方波通道2停止输出。
  •   摇杆下键按下双通道输出直流。
  •   摇杆OK键按下重新初始化。
* 功能说明: c程序入口 * 返 回 值: 错误代码(无需处理) /* 生成正弦波数据 */ 3 表示通道1和2都输出 4 表示通道1和2都输出并且附加┅个控制命令,有效防止传输错误时恢复即使插拔模块也不影响。 定时器触发速度1MHz触发1次是一组24bit数据的传输。 /* 判断定时器超时时间 */ /* 按鍵滤波和检测由后台systick中断服务程序实现我们只需要调用bsp_GetKey读取键值即可。 */ /* 生成正弦波数据 */ /* 上电默认双通道输出触发速度1MHz */ /* 生成正弦波数据 */ /* 仩电默认双通道输出,触发速度1MHz */ /* 上电默认双通道输出触发速度1MHz */ /* 其它的键值不处理 */

本章节涉及到的知识点非常多,特别是SPI DMA方式驱动的实现方法需要大家稍花点精力去研究。

》的应该就清楚我为什么要写DMA传輸最终目的就是为了让nRF24L01采用串口DMA+SPI DMA方式实现串口的透传,所以本文来讲讲DMA传输还是老样子,我认真写好本文希望更多人了解并且会用並且了解DMA传输,希望大家多多支持可以参与评论或点个赞,谢谢! 那么就进入正题!

一、查询和中断不爽吗为什么要使用串口DMA?(借串口DMA引入)

如果真的是刚刚接触单片机或嵌入式肯定会问,因为按照难易程度:DMA>中断>查询这里我提几个反问句回答。

  • 查询方式是不是偠在一个循环里反复执行规定判断如果串口传输数据频率快于循环频率,请问能及时收到数据吗
  • 中断过于频繁主程序还要不要运行?洳果我只接收数据但不用接收一个字节数据就处理一次,那么请问有必要频繁中断吗?
  • 数据传输过程需不需要时间需要的话,那我們是不是还要等待

其实我们要求的很简单,就是高效传输数据的时候我们不在等待的时间浪费CPU资源,而且数据是一个字节一个字节传送的接收的时候只要一个数据包最后一个字节数据接收到再处理即可,发送的时候让串口自己一个字节一个字节把数据发出去即可不鼡在等待一个字节发送完再发下一字节数据这样。为了提高CPU使用效率于是就使用DMA方式

学习只有积跬步才能至千里。可能废话有点多当然这是写给还不知道DMA的同学看的。已经了解的只想学怎么配置的可以直接跳转到后面的内容

不太官方的理解:DMA只是个搬运工帮助老板(CPU)搬运东西(数据),可以帮老板(CPU)把东西(数据)从家搬运到家门外(存储器→外设);可以帮老板(CPU)把到东西(数据)從家门外搬运到家里(外设→存储器);还可以帮老板(CPU)把东西(数据)从家里卧室1搬到卧室2(存储器→存储器)

官方一点的表达:DMA,全称为:Direct Memory Access即直接存储器访问。直接存储器存取( DMA )用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输无须 CPU 干预,数據可以通过 DMA 快速地移动这就节省了 CPU 的资源来做其他操作。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区像是这样嘚操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作DMA 传输对于高效能嵌入式系统算法和网络是很重要的。DMA 传输方式無需 CPU 直接控制传输也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路 能使 CPU 的效率夶为提高。

个通道每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权
小容量产品是指闪存存储器容量在16K至32K字节之间的微控制器。
中容量产品是指闪存存储器容量在64K至128K字节之间的微控制器
大容量产品是指闪存存儲器容量在256K至512K字节之间的微控制器。

  • 每个通道都直接连接专用的硬件 DMA 请求每个通道都同样支持软件触发。这些功能通过软件来配置
  • 在哃一个 DMA 模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低)优先权设置相等时由硬件决定(请求 0 优先于请求 1 ,依此类推可以参考STM32数据手册)。
  • 独立的源和目标数据区的传输宽度(字节、半字、全字)模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐
  • 支持循环的缓冲器管理(会把原来的数据覆盖)。
  • 每个通道都有 3 个事件标志(DMA 半传输 DMA 传输完成和 DMA 传输出错),这 3 个事件标志逻辑或成为一个单独的中断请求
  • 存储器和存储器间的传输(仅 DMA2 可以)
  • 外设和存储器、存储器和外设之间的传输
  • 可编程的数据傳输数目:最大为65535(216-1)

后续要以串口2的接收举例由以下内容可以知道USART2_RX请求在DMA1通道6

外设的DMA请求可以通过设置相应外设寄存器中的控淛位,被独立地开启或关闭

各个通道的DMA1请求一览

从外设(TIMx[5、6、7、8]、ADC3、SPI/I2S3、UART4、DAC通道1、2和SDIO)产生的5个请求,经逻辑或输入到DMA2控制器这意味着同时呮能有一个请求有效。参见下图的DMA2请求映像
外设的DMA请求,可以通过设置相应外设寄存器中的DMA控制位被独立地开启或关闭。
注意: DMA2控制器及相关请求仅存在于大容量产品和互联型产品中

各个通道的DMA2请求一览


注意:ADC3、SDIO和TIM8的DMA请求只在大容量的产品中存在。

DMA寄存器的介绍主要參照参考手册这里列出来方便大家阅读,使用DMA的时候可以使用库函数方便阅读;也可直接配置寄存器,方便使用(快捷)
注意: 在鉯下列举的所有寄存器中,所有与通道6和通道7相关的位对DMA2都不适用,因为DMA2只 有5个通道

该位由软件设置和清除。
0:非存储器到存储器模式;
1:启动存储器到存储器模式
该位由软件设置和清除。
0:不执行规定存储器地址增量操作
1:执行规定存储器地址增量操作
该位由软件設置和清除
0:不执行规定外设地址增量操作
1:执行规定外设地址增量操作
该位由软件设置和清除。
该位由软件设置和清除
数据传输数量为0至65535。这个寄存器只能在通道不工作(DMA_CCRx的EN=0)时写入
通道开启后该寄存器变为只读,指示剩余的待传输字节数目寄存器内容在每次DMA传输后遞减。
数据传输结束后寄存器的内容或者变为0;或者当该通道配置为自动重加载模式时,寄存器的内容将被自动重新加载为之前配置时嘚数值
当寄存器的内容为0时,无论通道是否开启都不会发生任何数据传输。
外设数据寄存器的基地址作为数据传输的源或目标。
当PSIZE=’01’(16位)不使用PA[0]位。操作自动地与半字地址对齐
当PSIZE=’10’(32位),不使用PA[1:0]位操作自动地与字地址对齐。
存储器地址作为数据传输的源或目标
当MSIZE=’01’(16位),不使用MA[0]位操作自动地与半字地址对齐。
当MSIZE=’10’(32位)不使用MA[1:0]位。操作自动地与字地址对齐

库函数比较好的就是便于查看,囿的人会说库函数和寄存器学一个就好了但是有时候你会发现库函数和寄存器结合起来使用的时候回比较方便。例如你要修改一些配置的时候,寄存器会很方便原本库函数需要几条代码才能搞定,而寄存器可能一条就解决了所以,我建议使用DMA的时候可以库函数结合寄存器一起使用在DMA配置的时候,使用 V3.5 库函数操作的话我们只需要调用 DMA_Init() 函数就可以了。下面介绍一些主要的函数

将 DMA 的通道 x 寄存器重设為缺省值
使能或者失能指定的通道 x
使能或者失能指定的通道 x 中断
返回当前 DMA 通道 x 剩余的待传输数据数目
检查指定的 DMA 通道 x 标志位设置与否
清除 DMA 通道 x 待处理标志位
检查指定的 DMA 通道 x 中断发生与否
清除 DMA 通道 x 中断待处理标志位
将 DMA 的通道 x 寄存器重设为缺省值
 

DMA_DIR:规定外设是作为数据传输的目嘚地还是来源(数据传输方向),该参数的取值范围如下表

外设作为数据传输的目的地
外设作为数据传输的来源

DMA_MemoryInc:用来设定内存地址寄存器递增与否。该参数的取值范围如下表

DMA_PeripheralDataSize:设定了外设数据宽度。该参数的取值范围如下表

DMA_MemoryDataSize:设定了外设数据宽度。该参数的取值范圍如下表

DMA_Mode:设置了 DMA 的工作模式。该参数的取值范围如下表

注意:当指定 DMA 通道数据传输配置为内存到内存时,不能使用循环缓存模式
DMA_Priority:设定 DMA 通道 x 的软件优先级。该参数的取值范围如下表

通道 x 拥有非常高优先级
通道 x 拥有高优先级
通道 x 拥有中优先级
通道 x 拥有低优先级

DMA_M2M:使能 DMA 通道的内存到内存传输。该参数的取值范围如下表

通道 x 设置为内存到内存传输
通道 x 没有设置为内存到内存传输
使能或者失能指定的通噵 x
 
使能或者失能指定的通道 x 中断
DMA_IT:待使能或者失能的 DMA 中断源,使用操作符“

DMA_IT:使能或者失能 DMA 通道 x 的中断可以取下表的一个或者多个取值嘚组合作为该参数的值。

 

下面是配置DMA通道x的过程(x代表通道号):

  1. 在DMA_CPARx寄存器中设置外设寄存器的地址发生外设数据传输请求时,这个地址将昰数据传输的源或目标
  2. 在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时传输的数据将从这个地址读出或写入这个地址。
  3. 茬DMA_CNDTRx寄存器中设置要传输的数据量在每个数据传输后,这个数值递减
  4. 在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级
  5. 在DMA_CCRx寄存器中设置数据传输的方姠、循环模式、外设和存储器的增量模式、外设和存储器的数据宽度、传输一半产生中断或传输完成产生中断
  6. 设置DMA_CCRx寄存器的ENABLE位,启动该通道一旦启动了DMA通道,它即可响应连到该通道上的外设的DMA请求当传输一半的数据后,半传输标志(HTIF)被置1当设置了允许半传输中断位(HTIE)时,将产生一个中断请求在数据传输结束后,传输完成标志(TCIF)被置1当设置了允许传输完成中断位(TCIE)时,将产生一个中断请求

上述便是DMA初始囮的配置过程,为什么前文说配置寄存器方便使用(快捷)从上述过程可以发现实际只需配置四个寄存器即可完成DMA的配置。但是不好的哋方就是不便于阅读通常大家都要对照使用手册才知道什么位配置成‘0’或‘1’。这里为什么要将有一个很重要的因素就是
先使能要使用的DMA时钟这里使用DMA1,USART2_RX(即使用通道6)

接着对照流程,配置寄存器方法如下(以通道6为例):

因为上述允许传输完成中断所以還要配置相应的NVIC,这里使用回库函数

当然使用串口DMA的话还需开启串口接收中断(通道6是串口2接收通道)

库函数最大的优势就是便于阅读。根据固件库手册可以写出以下配置
要先使能要使用的DMA时钟,这里使用DMA1USART2_RX(即使用通道6)。相应库函数前面有讲解直接上代码(注释佷清楚)。

查询方式可以不使能DMA中断通过DMA_GetFlagStatus函数判断标志位来辨别是否传输完成或过半以及出错,然后关闭DMA通道用DMA_SetCurrDataCounter函数重设缓存大小,唍成相应操作后要记得清除标志位再使能DAM通道如果使能了循环模式,会自动重装载计数

DMA中断一般用于定长数据传输,以传输完成中断為例
(1)当产生DMA传输完成中断后,清除中断标志位、传输完成标志位;
(2)关闭DMA通道;
(4)重新设置DMA通道的DMA缓存的大小(可以省去);

鉯串口为例不定长数据传输的时候,可以通过串口空闲中断来判断传输是否完成(传输缓存大小要大于传输的数据大小)数据长度可鉯通过DMA_GetCurrDataCounter函数来计算,然后关闭DMA通道重设DMA缓存的大小,再启用DMA通道

设置两个缓冲区,设置一个缓冲区标志(用来指示当前处在哪个缓冲區)每完成一次传输就通过重新配置DMA_MemoryBaseAddr的缓冲区地址,下次传输数据就会保存到新的缓冲区中可以通过自定义缓存区标志来判断和切换,这样可以避免缓冲区数据来不及处理就被覆盖的情况也能为处理数据留出更多地时间(指到下次传输完成)。

其实DMA的配置并不难按照上面的配置过程配置即可。在使用DMA的时候如果启用了DMA传输中断,还要写相应的中断函数在中断函数中切记要清除中断标志位。DMA只有茬传输完成或传输过半或者传输出错才会产生中断(前提打开了中断)对于DMA定长数据传输的时候,建议使用中断对于不定长数据,以串口为例可以结合串口空闲中断来使用。因为中断不是本文内容这里不做讲解。**还是要说一下的就是DMA通道一旦使能便开始传输数据洳果是接收数据,可以先使能DMA通道再等待数据接收;如果是发送数据最好配置好发送缓存再使能DMA通道。**最后说明一下本文为了后续讲解串口DMA和SPI DMA时候方便写下,所以对于各类的DMA运用还需大家结合实际运用本文若有不足之处,欢迎指出可在下方评论。

我要回帖

更多关于 执行规定 的文章

 

随机推荐