[APM32F4] TMR+DMA输出模拟信号应用

[复制链接]
 楼主| 宫影空明人不往 发表于 2025-2-9 22:10 | 显示全部楼层 |阅读模式
本帖最后由 宫影空明人不往 于 2025-2-9 22:10 编辑

前言
当芯片可以使用的通讯模块都被占用了,我们该如何去发送相应的通讯信号呢?这时候可以选择使用定时器和DMA来输出特性波形的模拟信号。在现代嵌入式系统和通信领域,定时器(TMR)和直接内存访问(DMA)是两个非常重要的硬件模块。它们可以协同工作,生成特定的波形,模拟其他信号通信协议。
使用场景
1. 模拟通信协议
在许多嵌入式系统中,可能需要模拟某些通信协议,如UART、SPI、I2C等。使用TMR和DMA可以精确地生成这些协议所需的波形,从而在没有硬件支持的情况下实现通信功能。
2. 信号发生器
在测试和调试过程中,可能需要生成特定的信号波形来测试设备的响应。TMR和DMA可以用于生成这些信号,提供高精度和高灵活性的信号输出。
3. 音频信号处理
在音频处理应用中,TMR和DMA可以用于生成特定的音频波形,如正弦波、方波等,用于音频信号的合成和处理。
4. 电机控制
在电机控制应用中,TMR和DMA可以用于生成PWM信号,控制电机的转速和方向。通过精确控制PWM信号的占空比和频率,可以实现对电机的精确控制。
好处
1.高精度
2.低CPU开销
3.灵活性
4.实时性
实现
下面我们将通过一个具体的例子,展示如何使用TMR和DMA模拟UART发送一段特定的字符串。
实现原理
下面的例子利用DMA的突发传输,在定时器产生更新DMA请求时,可以将定时器的多个连续的寄存器进行改变。这里选择将定时器的计数器和比较器的值刷新,从而改变下一段高低电平的时间长度,从而生成特定的信号。
硬件配置
这里我们使用APM32F407开发板,模块使用是定时器8来产生相应的串口信号,DMA2来将数据从内存传输到GPIO端口。
软件实现
1.配置定时器8
    F407的主频是168MHz,定时器8的主频为系统时钟168MHz,这里的话将定时器8分频设置为167。那么定时器8计数器数值加1的波长为1us。同时将定时器8的计数值和比较器值分别设置成999和1000,计数方式上升,极性高电平。那么TMR8将一直输出高电平,直到DMA将其刷新。

  1.     TMR_TimeBaseStruct.clockDivision = TMR_CLOCK_DIV_1;

  2.     TMR_TimeBaseStruct.countMode = TMR_COUNTER_MODE_UP;

  3.     TMR_TimeBaseStruct.division = 167;

  4.     TMR_TimeBaseStruct.period = 999;

  5.     TMR_TimeBaseStruct.repetitionCounter = 0;

  6.     TMR_ConfigTimeBase(TMR8, &TMR_TimeBaseStruct);

  7. ...

  8.     TMR_ConfigDMA(TMR8, TMR_DMA_BASE_AUTORLD, TMR_DMA_BURSTLENGTH_3TRANSFERS);

  9.     TMR_EnableDMASoure(TMR8, TMR_DMA_SOURCE_UPDATE);


2.配置DMA2
    根据F4的用户手册,将DMA配置成定时器8的更新触发,目标寄存器为定时器8的DMADDR寄存器。

  1.     DMA_Config_T dmaConfig;

  2.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_DMA2);



  3.     dmaConfig.bufferSize = sizeof(ConfigBuf);

  4.     dmaConfig.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;

  5.     dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;

  6.     dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE;

  7.     dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;

  8.     dmaConfig.loopMode = DMA_MODE_CIRCULAR;

  9.     dmaConfig.priority = DMA_PRIORITY_HIGH;

  10.     dmaConfig.dir = DMA_DIR_MEMORYTOPERIPHERAL;

  11.     dmaConfig.memoryBaseAddr = (uint32_t)ConfigBuf;

  12.     dmaConfig.peripheralBaseAddr = (uint32_t)&TMR8->DMADDR;



  13.     dmaConfig.channel = DMA_CHANNEL_7;

  14.     dmaConfig.fifoMode = DMA_FIFOMODE_ENABLE;

  15.     dmaConfig.fifoThreshold = DMA_FIFOTHRESHOLD_FULL;

  16.     dmaConfig.peripheralBurst = DMA_PERIPHERALBURST_SINGLE;

  17.     dmaConfig.memoryBurst = DMA_MEMORYBURST_SINGLE;

  18.     DMA_Config(DMA2_Stream1, &dmaConfig);

  19.     DMA_Enable(DMA2_Stream1);


3.配置数组
    根据需要输出的信号,编写相应的数组。根据定时器的配置,计数器加1时间为1us。那么第一次更新后,DMA将54,0,24分别赋值到定时器8的计数值,重复计数值,比较值。那么输出的信号为为24us的高电平,32us的低电平。定时器更新后产生DMA更新信号,下一组的信号则为8us的高电平,16us的电平。后面则以此类推。这里的话将8us作为一个串口单位,则对于串口波特率为125000。

  1. uint16_t ConfigBuf[120] = {56,0,24,24,0,8 ,16,0,8 ,24,0,16,

  2.                            16,0,8, 24,0,8,24,0,16 ,40,0,16,

  3.                            24,0,16, 24,0,16, 40,0,16,

  4.                            24,0,16, 24,0,16, 24,0,16,

  5.                            40,0,32, 24,0,16, 64,0,16,

  6.                            24,0,8, 24,0,16,

  7.                            32,0,24, 16,0,8, 16,0,8, 24,0,16,

  8.                            40,0,32, 24,0,16, 32,0,16,

  9.                            24,0,8, 32,0,24, 40,0,16,

  10.                            24,0,16, 24,0,16, 40,0,16,

  11.                            24,0,8, 24,0,16,24,0,16,

  12.                            40,0,8, 24,0,8, 999, 0 ,1000};


结果
将定时器8的输出IO引脚接到串口的Rx引脚上,波特率选择125000,8bit,无校验位。复位开发板后,可以看见串口正常接收。 U0$EU]%TV%8U~MKQ9$DR0.png

结论
使用TMR和DMA输出特定波形模拟其他信号通信,具有高精度、低CPU开销、灵活性和实时性等优点。通过合理配置TMR和DMA,可以实现各种复杂的信号生成和处理任务,满足不同应用场景的需求。本文通过一个具体的例子,展示了如何使用TMR和DMA模拟125000波特率的UART信号发送,为类似的应用提供了参考。


天体书记 发表于 2025-5-31 18:50 | 显示全部楼层
这个用于DAC输出是不是更好啊!
话说模拟串口输出,楼主也是个狠人
您需要登录后才可以回帖 登录 | 注册

本版积分规则

12

主题

14

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部