|**银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801** ||| |技术支持邮件|Gingko@vip.163.com ||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-11-18 | gingko | 初次建立 | ===== STM32CubeMX教程十二——DDS输出正弦波 ===== 1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR {{ :icore3l:icore3l_cube_12_1.png |}} 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置在搜索栏的下面,提供的各种查找方式,可以选择芯片内核,型号等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32F429IGHx。 {{ :icore3l:icore3l_cube_12_2.png |}} 3.配置RCC,使用外部时钟源 {{ :icore3l:icore3l_cube_12_3.png |}} 4. 调试选择Serial Wire,时基源选择SysTick {{ :icore3l:icore3l_cube_12_4.png |}} {{ :icore3l:icore3l_cube_12_5.png |}} 5.将PI3,PI4,PH14设置为GPIO_Output {{ :icore3l:icore3l_cube_12_6.png |}} 引脚模式配置 {{ :icore3l:icore3l_cube_12_7.png |}} 6.配置DAC {{ :icore3l:icore3l_cube_12_8.png |}} 7.配置定时器 {{ :icore3l:icore3l_cube_12_9.png |}} 定时器中断使能 {{ :icore3l:icore3l_cube_12_10.png |}} 8.配置SD卡 {{ :icore3l:icore3l_cube_12_11.png |}} SD卡引脚配置 {{ :icore3l:icore3l_cube_12_12.png |}} 9.配置Middleware(中间件),即fatfs {{ :icore3l:icore3l_cube_12_13.png |}} 10.时钟源设置,选择外部高速时钟源,配置为最大主频 {{ :icore3l:icore3l_cube_12_14.png |}} 11.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可IDE我们使用的是MDK V5.27 {{ :icore3l:icore3l_cube_12_15.png |}} 12.点击Code Generator,进行进一步配置 {{ :icore3l:icore3l_cube_12_16.png |}} * **Copy all used libraries into the project folder** * **将HAL库的所有.C和.H都复制到所建工程中** * 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便 * 缺点:体积大,编译时间很长 * **Copy only the necessary library files** * **只复制所需要的.C和.H(推荐)** * 优点:体积相对小,编译时间短,并且工程可复制拷贝 * 缺点:新增外设时需要重新用STM32CubeMX导入 * **Add necessary library files as reference in the toolchain project configuration file** * **不复制文件,直接从软件包存放位置导入.C和.H** * 优点:体积小,比较节约硬盘空间 * 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径 * 自行选择方式即可 13.然后点击GENERATE CODE创建工程 {{ :icore3l:icore3l_cube_12_17.png |}} 创建成功,打开工程。 \\ \\ \\ \\ ===== 实验十二:DDS实验——输出正弦波 ===== ==== 一、实验目的与意义 ==== - 了解STM32 DAC结构 - 了解STM32 DAC特征 - 掌握DDS原理 - 掌握STM32 HAL库中DAC属性的配置方法 - 掌握KEIL MDK 集成开发环境使用方法 ==== 二、实验设备及平台 ==== - iCore3L 双核心板 - JLINK(或相同功能)仿真器 - Micro USB线缆 - Keil MDK 开发平台 - STM32CubeMX开发平台 - 装有WIN XP(及更高版本)系统的计算机 ==== 三、实验原理 ==== === 1.DAC简介 === * STM32F429IGH6的DAC模块(数字/模拟转换模块)是12位数字输入,电压输出型的DAC。DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压Vref+(通ADC共用)以获得更精确的转换结果。 STM32F429IGH6的DAC模块主要特性: * 两个 DAC 接口,各自对应一个输出通道 * 8位或10位单调输出 * 12 位模式下数据采用左对齐或右对齐 * 同步更新功能 * 噪声波和三角波生成 * DAC 双通道单独或同时转换 * 每条通道的 DMA 功能,包括 DMA 下溢错误检测 * 通过外部触发信号进行转换 * DAC 输出通道缓冲/非缓冲模式 * 输出参考电压VREF+ DAC框图: {{ :icore3l:icore3l_arm_hal_12_1.png |}} DAC 包含: * 多达两条输出通道 * DAC_OUTx 可与输出引脚断开连接并用作普通 GPIO * DAC_OUTx 可使用与片上外设(如比较器和 OPAMP)的内部引脚连接。 * DAC 输出通道(缓冲/非缓冲) * 使用 LSI 时钟源在停止模式下运行以实现静态转换的采样和保持模块及其寄存器 * DAC 包含多达两条独立的输出通道。每条输出通道均可连接到片上外设,如 COMP、 OPAMP和 ADC。在这种情况下, DAC 输出通道可与 DAC_OUTx 输出引脚断开连接,相应的 GPIO可用于其他用途。 * DAC 输出可缓冲、也可以不缓冲。采样和保持模块及其关联寄存器可在停止模式下使用 LSI时钟源运行。 * 图中VDDA和VSSA为DAC模块模拟部分的供电,而VREF+则是DAC模块的参考电压。(对应PA4引脚)。 从图中可以看出,DAC输出是受DORx(x=1/2,下同)寄存器直接控制的,但是我们不能直接往DORx寄存器写入数据,而是通过DHRx间接的传给DORx寄存器,实现对DAC输出的控制。前面我们提到,STM32F429IGH6的DAC支持8/12位模式,8位模式的时候是固定的右对齐的,而12位模式又可以设置左对齐/右对齐。单DAC通道x,总共有3种情况: - 8位数据右对齐:用户将数据写入DAC_DHR8Rx[7:0]位(实际存入DHRx[11:4]位)。 - 12位数据左对齐:用户将数据写入DAC_DHR12Lx[15:4]位(实际存入DHRx[11:0]位)。 - 12位数据右对齐:用户将数据写入DAC_DHR12Rx[11:0]位(实际存入DHRx[11:0]位)。 * 如果没有选中硬件触发(寄存器DAC_CR1的TENx位置‘0’),存入寄存器DAC_DHRx的数据会在1个APB1时钟周期后自动传至寄存器DAC_DORx。如果选中硬件触发(寄存器DAC_CR1的TENx位置’1’),数据传输在触发发生以后3个APB1时钟周期后完成。一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间tSETTLING之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。 关闭触发 (TEN = 0) 时的转换时序图: {{ :icore3l:icore3l_arm_hal_12_2.png |}} DAC输入/输出引脚: {{ :icore3l:icore3l_arm_hal_12_3.png |}} DAC内部输入/输出信号: {{ :icore3l:icore3l_arm_hal_12_4.png |}} === 2.DAC输出电压公式 === * 经过线性转换后,数字输入会转换为 0 到 VREF+ 之间的输出电压。 * 各 DAC 通道引脚的模拟输出电压通过以下公式确定: * **DAC_output= V_REF × DOR/4096** === 3.DAC部分寄存器介绍 === * 我们介绍一下要实现 DAC 的通道 1 输出,需要用到的一些寄存器。首先是 DAC控制寄存器 DAC_CR,该寄存器的各位描述如图所示: {{ :icore3l:icore3l_arm_hal_12_5.png |}} * DAC_CR 的低 16 位用于控制通道 1,而高 16 位用于控制通道 2,我们这里仅列出比较本章需要设置的一些位: * EN1 位:用于 DAC 通道 1 的使能,我们需要用到 DAC 通道 1 的输出,该位必须设置为 1。 * TEN1 位:用于 DAC 通道 1 的触发使能,我们设置该位为 0,不使用硬件触发。 写入 DHR1的值会在 1 个 APB1 周期后传送到 DOR1,然后输出到 PA4 口上。 * TSEL[3:0]位,用于选择 DAC 通道 1 的触发方式,本章设置为 0,使用软件触发。 * WAVE[1:0]位,用于控制 DAC 通道 1 的噪声/波形输出功能,默认设置为 0,不使能噪声/波形输出。 * DMAEN1 位,用于控制 DAC 通道 1 的 DMA 使能,本章不使能,设置该位为 0 即可。CEN1 位,用于控制 DAC 通道 1 的输出缓冲校准使能,本章不使用校准功能(默认有一个出场校准值,我们使用默认的校准值即可),设置该位为 0 即可。 * 然后,我们介绍 DAC 模式控制寄存器( DAC1_MCR),该寄存器各位描述如图所示: {{ :icore3l:icore3l_arm_hal_12_6.png |}} * 位 2:0 MODE1[2:0]: DAC 通道 1 模式 (DAC Channel 1 mode) * 仅当 DAC 已禁止且不处于校准模式时( DACx_CR 寄存器中的位 EN1 = 0 且 * 位 CEN1 = 0),才可写入这些位。如果 EN1=1 或 CEN1 =1,则会忽略写操作。 * 这些位可由软件置 1 和清零,用于选择 DAC 通道 1 模式。 * ** – DAC 通道 1 处于正常模式** * 000: DAC 通道 1 连接到外部引脚且使能了缓冲器 * 001: DAC 通道 1 连接到外部引脚以及片上外设且使能了缓冲器 * 010: DAC 通道 1 连接到外部引脚且禁止了缓冲器 * 011: DAC 通道 1 连接到片上外设且禁止了缓冲器 * **– DAC 通道 1 处于采样和保持模式** * 100: DAC 通道 1 连接到外部引脚且使能了缓冲器 * 101: DAC 通道 1 连接到外部引脚以及片上外设且使能了缓冲器 * 110: DAC 通道 1 连接到外部引脚以及片上外设且禁止了缓冲器 * 111: DAC 通道 1 连接到片上外设且禁止了缓冲器 * ** 注: 仅可在 EN1 = 0 时修改该寄存器。** === 4、DDS原理 === == 4.1 什么是频率 == 频率是指单位时间内某事件重复的次数。在电子学中,信号的频率是指单位时间内信号的周期数,单位是赫兹(Hertz,简称Hz)。很多年前有一个著名的德国物理学家海因里希· 鲁道夫·赫兹,他首先证明了电磁波的存在,为了纪念他,频率的单位就用他的名字命名。频率是一个非常常用、也是一个非常重要的国际单位;日常生活中,我们收听的收音机、观看的电视机、交流市电、移动蜂窝电话等信号的传输过程,都利用了信号的频率特性。频率与信号的周期互为倒数关系,所以频率也可以表示为: {{ :icore3l:icore3l_arm_hal_12_7.jpg |}} == 4.2 怎么得到任意频率的信号 == * 既然信号的频率特性那么重要,我们怎么样才能得到自己想要的频率的信号呢?一般来说,我们通过下面三种方法得到想要的频率信号。 * ** 晶体或者晶体振荡器**:晶体振荡器是利用石英晶体的压电效应,来产生我们想要的频率,他有精度高、稳定性强、温漂小等特点。晶体或者晶振是电路中常用的元器件,它能给我们的电路提供稳定的时钟源,但它也有它的局限性,晶体一旦切割完毕,他的固有频率也就固定了,所以晶体或者晶振不适用于要求频率时刻变化的场合。 * ** RLC 振荡器**:利用RC 振荡或者 LC 振荡的原理,通过改变其中的R、L或者C达到改变振荡频率的目的。这种振荡器电路简单、起振容易,所以也很常用。不过他频率稳定性一般,不适合在频率精度要求较高的场合使用。 * ** 频率合成技术**:频率合成技术可以理解为我们通过某种方法,对固定的频率进行运算,来完成频率的变换。频率合成方法一般有锁相环(PLL)法和直接数字合成技术。PLL 是一种闭环系统,他通过对输入频率进行倍频、分频等过程,完成频率的变换。这种技术一般用于输出频点不多的场合(例如MCU中频率的倍频过程),它得天独厚的优势是可以产生比输入频率更高的频率。直接数字合成技术为本节讨论的重点,以下内容均讨论直接数字合成技术。 == 4.3 直接数字合成技术简介 == * 直接数字合成(Direct Digital Synthesizer, DDS)是1971年由J.Tierncy等人提出的,它通过基于相位累加的变换方式,对输入频率进行变换,来达到任意频率输出的目的。它具有输出频率稳定、分辨率高、切换速度快等优点,配合波形查找表及相关路,它能产生任意的波形,这也是它最大的优势。 == 4.4 DDS原理 == * 一个典型的DDS系统包括相位累加器、幅度变换及DA转换电路。它基于同一个系统时钟驱动的。在某一个时钟时刻,相位累加器产生一个特定的相位角度,通过相位角度-幅度变换,查找到波形表中电压值,然后送给DAC,来重现这一时刻的模拟电压,这就完成了DDS工作的一个步进。通过不断的时钟驱动,我们就能得到连续的模拟波形。 {{ :icore3l:icore3l_arm_hal_12_8.png |}} ** 4.4.1 相位累加器** * 相位累加器(ACCUMULATOR)是DDS的核心,它由一个加法器和一个D触发器组成。相位累加器由多位组成,典型的应用中,一般取 16~48位。相位累加器工作过程中,时钟每动作一次,累加器便累加一次调谐字(TUNING WORD);所以相位累加器输出一个以时间为序列的数字字,它线性增长,直到达到最大值2n(假设该累加器为n位),如果大于最大值,则舍弃溢出的高位,仍然保留n位。 * 为了形象的描述相位累加器的工作过程,我们可以把相位累加器看做一个圆周,如图三所示。其中,n表示相位累加器的位数,2n为相位累加器的模数,也就是圆周等分点数,每次的步进值为调谐字(TUNING WORD)。如果把相位累加器旋转一周作为一个周期的话,则最终频率输出可以用方程式(2)来描述。 {{ :icore3l:icore3l_arm_hal_12_9.jpg |}} * 如果把调谐字(TUNING WORD)设置为最小值1,带入方程式(2)则可得到最小输出频率,也就是频率分辨率。通过公式(3)我们可以看出,相位累加器位数越高,则频率分辨率越高。在100MHz的采样时钟下,48位的相位累加器可以使得频率分辨率优于 1μHz。 {{ :icore3l:icore3l_arm_hal_12_10.jpg |}} ** 4.4.2 角度-幅度转换** * 相位累加器的输出为线性的,如果我们需要输出任意的波形,我们则需要角度-幅度转换。一般地可以通过查找表的方法来实现。相位累加器的输出作为查找表的地址,数据线作为内容输出,则完成了角度-幅度转换。图四示意图为单周期正弦波查找表,n位的相位累加器输出(线性变化)作为查找表的地址线,数据输出端则为按正弦规律变化的数字序列。 {{ :icore3l:icore3l_arm_hal_12_11.png |}} ** 4.4.3 D/A 转换** * 相位累加器的输出经过波形查找表后,得到按预置波形变化的数字序列,还需要进行数字-模拟转换,才能得到我们需要的模拟波形,这个过程为 D/A转换,如图五所示。DAC 的驱动时钟与相位累加器的时钟同源,所以保证DDS 每个模块工作“步调”一致。 {{ :icore3l:icore3l_arm_hal_12_12.png |}} ==== 四、实验程序 ==== 1.主函数 int main(void) {   HAL_Init();   SystemClock_Config(); MX_GPIO_Init(); MX_DAC1_Init(); MX_SDMMC1_SD_Init(); MX_TIM3_Init(); MX_FATFS_Init(); res = f_mount(&fatfs,"0:",1); if(res != RES_OK) { while(1) { LED_ON; HAL_Delay(300); LED_OFF; HAL_Delay(300); } } wave.waveform(WAVE_SINE,0); wave.set_frequency(1000); //计算频率为1K的波表数据 i = 0; while(1) { wave.accumulator = wave.accumulator + wave.fword; if(wave.accumulator > 4294967295)break; temp = wave.accumulator >> 18; f = wave.buffer[temp]; g = f / 16384.0; g = g * 2.0 +0.05; g = g * 4096 / 2.5; wave.buffer[i ++] = g; } counter = i - 1; HAL_TIM_Base_Start_IT(&htim3); HAL_DAC_Start(&hdac1, DAC_CHANNEL_1); LED_ON; while (1) { } } 2.set_frequency函数 static int set_frequency(int freq) { //32位相位累加器 400K 采样率 wave.accumulator = 0; wave.fword = (int)(freq * 4294.967296 * 2.5); return 0; } 3.HAL_DAC_SetValue函数 HAL_DAC_SetValue(DAC_HandleTypeDef *hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data) //hdac:指向DAC_HandleTypeDef结构的指针 //Channel:选定DAC通道 //Alignment:数据对方方式 //Data:放入寄存器中的值 4.DAC初始化函数 void MX_DAC1_Init(void) {   DAC_ChannelConfTypeDef sConfig = {0};   hdac1.Instance = DAC1;   if (HAL_DAC_Init(&hdac1) != HAL_OK)   {     Error_Handler();   }   sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;//关闭   sConfig.DAC_Trigger = DAC_TRIGGER_NONE;//关闭DAC触发   sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;//使用输出缓存,提高驱动力   sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;//关闭DAC芯片连接   sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;//缓冲器偏移校准采用出厂修整   if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)   {     Error_Handler();   } } ==== 五、实验步骤 ==== - 把仿真器与iCore3L的SWD调试口相连(直接相连或者通过转接器相连); - 将iCore3L与扩展底板相连,为扩展板供电; - 打开Keil MDK 开发环境,并打开本实验工程; - 烧写程序到iCore3L上; - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 ==== 六、实验现象 ==== 用示波器测量iCore3L底板的DAC引脚(PA4)可以看到2Vpp 1KHz的正弦波,如下图。 {{ :icore3l:icore3l_arm_hal_12_13.jpg |}}