这里写目录标题
- 一、DAC简介(了解)
- 二、DAC工作原理(掌握)
- 三、DAC输出实验(熟悉)
- 四、DAC输出三角波实验(熟悉)
- 五、DAC输出正弦波实验(熟悉)
- [六、PWM DAC实验(熟悉)](#六、PWM DAC实验(熟悉))
-
- [6.1, PWM DAC应用背景(了解)](#6.1, PWM DAC应用背景(了解))
- [6.2, PWM DAC技术实现原理(了解)](#6.2, PWM DAC技术实现原理(了解))
-
- [6.2.1,什么是PWM DAC技术?](#6.2.1,什么是PWM DAC技术?)
- 6.2.2,用分段函数表示PWM波
- 6.2.3,将PWM波分段函数进行傅里叶级数展开
- [6.2.4,PWM DAC的分辨率](#6.2.4,PWM DAC的分辨率)
- 6.2.5,8位分辨率下对RC滤波器的设计要求
- [6.2.6,PWM DAC二阶低通滤波器原理图](#6.2.6,PWM DAC二阶低通滤波器原理图)
- [6.3,编程实战: PWM DAC实验(掌握)](#6.3,编程实战: PWM DAC实验(掌握))
一、DAC简介(了解)
1.1,什么是DAC?
1.2,DAC的特性参数
1.3,STM32各系列DAC的主要特性
二、DAC工作原理(掌握)
2.1,DAC框图简介(F1)
2.2,参考电压/模拟部分电压
2.3,DAC数据格式
2.4,触发源
2.5,DMA请求
2.6,DAC输出电压
三、DAC输出实验(熟悉)
3.1,实验简要(了解)
3.2,DAC寄存器介绍(了解)
3.3,DAC输出实验配置步骤(掌握)
相关HAL库函数介绍
关键结构体介绍(F1为例)
3.4,编程实战:DAC输出实验(掌握)
adc3.c
c
#include "./BSP/ADC/adc3.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc3_handle; /* ADC句柄 */
/**
* @brief ADC3初始化函数
* @note 本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
* 我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期
* 设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us
* @param 无
* @retval 无
*/
void adc3_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
ADC3_CHY_GPIO_CLK_ENABLE(); /* IO口时钟使能 */
ADC3_CHY_CLK_ENABLE(); /* ADC时钟使能 */
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; /* ADC外设时钟 */
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; /* 分频因子6时钟为72M/6=12MHz */
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); /* 设置ADC时钟 */
/* 设置AD采集通道对应IO引脚工作模式 */
gpio_init_struct.Pin = ADC3_CHY_GPIO_PIN; /* ADC通道对应的IO引脚 */
gpio_init_struct.Mode = GPIO_MODE_ANALOG; /* 模拟 */
HAL_GPIO_Init(ADC3_CHY_GPIO_PORT, &gpio_init_struct);
g_adc3_handle.Instance = ADC_ADCX; /* 选择哪个ADC */
g_adc3_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* 数据对齐方式:右对齐 */
g_adc3_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; /* 非扫描模式,仅用到一个通道 */
g_adc3_handle.Init.ContinuousConvMode = DISABLE; /* 关闭连续转换模式 */
g_adc3_handle.Init.NbrOfConversion = 1; /* 1个转换在规则序列中 也就是只转换规则序列1 */
g_adc3_handle.Init.DiscontinuousConvMode = DISABLE; /* 禁止规则通道组间断模式 */
g_adc3_handle.Init.NbrOfDiscConversion = 0; /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */
g_adc3_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */
HAL_ADC_Init(&g_adc3_handle); /* 初始化 */
HAL_ADCEx_Calibration_Start(&g_adc3_handle); /* 校准ADC */
}
/**
* @brief 设置ADC通道采样时间
* @param adcx : adc句柄指针,ADC_HandleTypeDef
* @param ch : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17
* @param stime: 采样时间 0~7, 对应关系为:
* @arg ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期 ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期
* @arg ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期 ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期
* @arg ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期 ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期
* @arg ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期 ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期
* @param rank: 多通道采集时需要设置的采集编号,
假设你定义channle1的rank=1,channle2 的rank=2,
那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。
单通道DMA设置为 ADC_REGULAR_RANK_1
* @arg 编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16
* @retval 无
*/
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
ADC_ChannelConfTypeDef adc_ch_conf;
adc_ch_conf.Channel = ch; /* 通道 */
adc_ch_conf.Rank = rank; /* 序列 */
adc_ch_conf.SamplingTime = stime; /* 采样时间 */
HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf); /* 通道配置 */
}
/**
* @brief 获得ADC转换后的结果
* @param ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17
* @retval 无
*/
uint32_t adc3_get_result(uint32_t ch)
{
adc3_channel_set(&g_adc3_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5); /* 设置通道,序列和采样时间 */
HAL_ADC_Start(&g_adc3_handle); /* 开启ADC */
HAL_ADC_PollForConversion(&g_adc3_handle, 10); /* 轮询转换 */
return (uint16_t)HAL_ADC_GetValue(&g_adc3_handle); /* 返回最近一次ADC1规则组的转换结果 */
}
/**
* @brief 获取通道ch的转换值,取times次,然后平均
* @param ch : 通道号, 0~17
* @param times : 获取次数
* @retval 通道ch的times次转换结果平均值
*/
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{
uint32_t temp_val = 0;
uint8_t t;
for (t = 0; t < times; t++) /* 获取times次数据 */
{
temp_val += adc3_get_result(ch);
delay_ms(5);
}
return temp_val / times; /* 返回平均值 */
}
adc3.h
c
#ifndef __ADC_H
#define __ADC_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* ADC及引脚 定义 */
#define ADC3_CHY_GPIO_PORT GPIOA
#define ADC3_CHY_GPIO_PIN GPIO_PIN_1
#define ADC3_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define ADC_ADCX ADC3
#define ADC3_CHY ADC_CHANNEL_1 /* 通道Y, 0 <= Y <= 17 */
#define ADC3_CHY_CLK_ENABLE() do{ __HAL_RCC_ADC3_CLK_ENABLE(); }while(0) /* ADC1 时钟使能 */
/******************************************************************************************/
void adc3_init(void); /* ADC3初始化 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime); /* ADC3通道设置 */
uint32_t adc3_get_result(uint32_t ch); /* 获得某个通道值 */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times); /* 得到某个通道给定次数采样的平均值 */
#endif
dac.c
c
#include "./BSP/DAC/dac.h"
#include "./SYSTEM/delay/delay.h"
DAC_HandleTypeDef g_dac_handle; /* DAC句柄 */
/**
* @brief DAC初始化函数
* @note 本函数支持DAC1_OUT1/2通道初始化
* DAC的输入时钟来自APB1, 时钟频率=36Mhz=27.8ns
* DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 4us (F103数据手册有写)
* 因此DAC输出的最高速度约为:250Khz, 以10个点为一个周期, 最大能输出25Khz左右的波形
*
* @param outx: 要初始化的通道. 1,通道1; 2,通道2
* @retval 无
*/
void dac_init(uint8_t outx)
{
GPIO_InitTypeDef gpio_init_struct;
DAC_ChannelConfTypeDef dac_ch_conf;
__HAL_RCC_DAC_CLK_ENABLE(); /* 使能DAC1的时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5; /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
gpio_init_struct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
g_dac_handle.Instance = DAC;
HAL_DAC_Init(&g_dac_handle); /* 初始化DAC */
dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE; /* 不使用触发功能 */
dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; /* DAC1输出缓冲关闭 */
switch(outx)
{
case 1:
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1); /* 配置DAC通道1 */
HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_1); /* 开启DAC通道1 */
break;
case 2:
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_2); /* 配置DAC通道2 */
HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_2); /* 开启DAC通道2 */
break;
default:break;
}
}
/**
* @brief 设置通道1/2输出电压
* @param outx: 1,通道1; 2,通道2
* @param vol : 0~3300,代表0~3.3V
* @retval 无
*/
void dac_set_voltage(uint8_t outx, uint16_t vol)
{
double temp = vol;
temp /= 1000;
temp = temp * 4096 / 3.3;
if (temp >= 4096)temp = 4095; /* 如果值大于等于4096, 则取4095 */
if (outx == 1) /* 通道1 */
{
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
else /* 通道2 */
{
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
}
dac.h
c
#ifndef __DAC_H
#define __DAC_H
#include "./SYSTEM/sys/sys.h"
void dac_init(uint8_t outx); /* DAC通道1初始化 */
void dac_set_voltage(uint8_t outx, uint16_t vol); /* 设置通道1/2输出电压 */
#endif
main.c
c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DAC/dac.h"
#include "./BSP/ADC/adc3.h"
extern DAC_HandleTypeDef g_dac_handle;
int main(void)
{
uint16_t adcx;
float temp;
uint8_t t = 0;
uint16_t dacval = 0;
uint8_t key;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
usmart_dev.init(72); /* 初始化USMART */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
adc3_init(); /* 初始化ADC3 */
dac_init(1); /* 初始化DAC1_OUT1通道 */
lcd_show_string(30, 50, 200, 16, 16, "STM32F103", RED);
lcd_show_string(30, 70, 200, 16, 16, "DAC TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "WK_UP:+ KEY1:-", RED);
lcd_show_string(30, 130, 200, 16, 16, "DAC VAL:", BLUE);
lcd_show_string(30, 150, 200, 16, 16, "DAC VOL:0.000V", BLUE);
lcd_show_string(30, 170, 200, 16, 16, "ADC VOL:0.000V", BLUE);
while (1)
{
t++;
key = key_scan(0); /* 按键扫描 */
if (key == WKUP_PRES)
{
if (dacval < 4000)dacval += 200;
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dacval);/* 输出增大200 */
}
else if (key == KEY1_PRES)
{
if (dacval > 200)dacval -= 200;
else dacval = 0;
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dacval); /* 输出减少200 */
}
if (t == 10 || key == KEY1_PRES || key == WKUP_PRES) /* WKUP/KEY1按下了,或者定时时间到了 */
{
adcx = HAL_DAC_GetValue(&g_dac_handle, DAC_CHANNEL_1); /* 读取前面设置DAC1_OUT1的值 */
lcd_show_xnum(94, 130, adcx, 4, 16, 0, BLUE); /* 显示DAC寄存器值 */
temp = (float)adcx * (3.3 / 4096); /* 得到DAC电压值 */
adcx = temp;
lcd_show_xnum(94, 150, temp, 1, 16, 0, BLUE); /* 显示电压值整数部分 */
temp -= adcx;
temp *= 1000;
lcd_show_xnum(110, 150, temp, 3, 16, 0X80, BLUE); /* 显示电压值的小数部分 */
adcx = adc3_get_result_average(ADC3_CHY, 20); /* 得到ADC3通道1的转换结果 */
temp = (float)adcx * (3.3 / 4096); /* 得到ADC电压值(adc是16bit的) */
adcx = temp;
lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE); /* 显示电压值整数部分 */
temp -= adcx;
temp *= 1000;
lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE); /* 显示电压值的小数部分 */
LED0_TOGGLE(); /* LED0闪烁 */
t = 0;
}
delay_ms(10);
}
}
四、DAC输出三角波实验(熟悉)
4.1,实验简要(了解)
4.2,编程实战:DAC输出三角波实验(掌握)
dac.c
c
#include "./BSP/DAC/dac.h"
#include "./SYSTEM/delay/delay.h"
DAC_HandleTypeDef g_dac_handle; /* DAC句柄 */
/**
* @brief DAC初始化函数
* @note 本函数支持DAC1_OUT1/2通道初始化
* DAC的输入时钟来自APB1, 时钟频率=36Mhz=27.8ns
* DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 4us (F103数据手册有写)
* 因此DAC输出的最高速度约为:250Khz, 以10个点为一个周期, 最大能输出25Khz左右的波形
*
* @param outx: 要初始化的通道. 1,通道1; 2,通道2
* @retval 无
*/
void dac_init(uint8_t outx)
{
GPIO_InitTypeDef gpio_init_struct;
DAC_ChannelConfTypeDef dac_ch_conf;
__HAL_RCC_DAC_CLK_ENABLE(); /* 使能DAC1的时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5; /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
gpio_init_struct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
g_dac_handle.Instance = DAC;
HAL_DAC_Init(&g_dac_handle); /* 初始化DAC */
dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE; /* 不使用触发功能 */
dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; /* DAC1输出缓冲关闭 */
switch(outx)
{
case 1:
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1); /* 配置DAC通道1 */
HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_1); /* 开启DAC通道1 */
break;
case 2:
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_2); /* 配置DAC通道2 */
HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_2); /* 开启DAC通道2 */
break;
default:break;
}
}
/**
* @brief 设置通道1/2输出电压
* @param outx: 1,通道1; 2,通道2
* @param vol : 0~3300,代表0~3.3V
* @retval 无
*/
void dac_set_voltage(uint8_t outx, uint16_t vol)
{
double temp = vol;
temp /= 1000;
temp = temp * 4096 / 3.3;
if (temp >= 4096)temp = 4095; /* 如果值大于等于4096, 则取4095 */
if (outx == 1) /* 通道1 */
{
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
else /* 通道2 */
{
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
}
/***************************************DAC输出三角波实验代码*****************************************/
/**
* @brief 设置DAC_OUT1输出三角波
* @note 输出频率 ≈ 1000 / (dt * samples) Khz, 不过在dt较小的时候,比如小于5us时, 由于delay_us
* 本身就不准了(调用函数,计算等都需要时间,延时很小的时候,这些时间会影响到延时), 频率会偏小.
*
* @param maxval : 最大值(0 < maxval < 4096), (maxval + 1)必须大于等于samples/2
* @param dt : 每个采样点的延时时间(单位: us)
* @param samples: 采样点的个数, samples必须小于等于(maxval + 1) * 2 , 且maxval不能等于0
* @param n : 输出波形个数,0~65535
*
* @retval 无
*/
void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n)
{
uint16_t i, j;
float incval; /* 递增量 */
float Curval; /* 当前值 */
if((maxval + 1) <= samples)return ; /* 数据不合法 */
incval = (maxval + 1) / (samples / 2); /* 计算递增量 */
for(j = 0; j < n; j++)
{
Curval = 0;
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval); /* 先输出0 */
for(i = 0; i < (samples / 2); i++) /* 输出上升沿 */
{
Curval += incval; /* 新的输出值 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
delay_us(dt);
}
for(i = 0; i < (samples / 2); i++) /* 输出下降沿 */
{
Curval -= incval; /* 新的输出值 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
delay_us(dt);
}
}
}
dac.h
c
#ifndef __DAC_H
#define __DAC_H
#include "./SYSTEM/sys/sys.h"
void dac_init(uint8_t outx); /* DAC通道1初始化 */
void dac_set_voltage(uint8_t outx, uint16_t vol); /* 设置通道1/2输出电压 */
void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n); /* 输出三角波 */
#endif
main.c
c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DAC/dac.h"
int main(void)
{
uint8_t t = 0;
uint8_t key;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
usmart_dev.init(72); /* 初始化USMART */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
dac_init(1); /* 初始化DAC1_OUT1通道 */
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "DAC Triangular WAVE TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY0:Wave1 KEY1:Wave2", RED);
lcd_show_string(30, 130, 200, 16, 16, "DAC None", BLUE); /* 提示无输出 */
while (1)
{
t++;
key = key_scan(0); /* 按键扫描 */
if (key == KEY0_PRES) /* 高采样率 , 约1Khz波形 */
{
lcd_show_string(30, 130, 200, 16, 16, "DAC Wave1 ", BLUE);
dac_triangular_wave(4095, 5, 2000, 100); /* 幅值4095, 采样点间隔5us, 200个采样点, 100个波形 */
lcd_show_string(30, 130, 200, 16, 16, "DAC None ", BLUE);
}
else if (key == KEY1_PRES) /* 低采样率 , 约1Khz波形 */
{
lcd_show_string(30, 130, 200, 16, 16, "DAC Wave2 ", BLUE);
dac_triangular_wave(4095, 500, 20, 100); /* 幅值4095, 采样点间隔500us, 20个采样点, 100个波形 */
lcd_show_string(30, 130, 200, 16, 16, "DAC None ", BLUE);
}
if (t == 10) /* 定时时间到了 */
{
LED0_TOGGLE(); /* LED0闪烁 */
t = 0;
}
delay_ms(10);
}
}
五、DAC输出正弦波实验(熟悉)
5.1,实验简要(了解)
5.2,DAC输出正弦波实验配置步骤(掌握)
5.3,产生正弦波序列函数介绍(熟悉)
5.4,编程实战:DAC输出正弦波实验(掌握)
dac3.c
c
#include "./BSP/ADC/adc3.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc3_handle; /* ADC句柄 */
/**
* @brief ADC3初始化函数
* @note 本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
* 我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期
* 设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us
* @param 无
* @retval 无
*/
void adc3_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
ADC3_CHY_GPIO_CLK_ENABLE(); /* IO口时钟使能 */
ADC3_CHY_CLK_ENABLE(); /* ADC时钟使能 */
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; /* ADC外设时钟 */
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; /* 分频因子6时钟为72M/6=12MHz */
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); /* 设置ADC时钟 */
/* 设置AD采集通道对应IO引脚工作模式 */
gpio_init_struct.Pin = ADC3_CHY_GPIO_PIN; /* ADC通道对应的IO引脚 */
gpio_init_struct.Mode = GPIO_MODE_ANALOG; /* 模拟 */
HAL_GPIO_Init(ADC3_CHY_GPIO_PORT, &gpio_init_struct);
g_adc3_handle.Instance = ADC_ADCX; /* 选择哪个ADC */
g_adc3_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* 数据对齐方式:右对齐 */
g_adc3_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; /* 非扫描模式,仅用到一个通道 */
g_adc3_handle.Init.ContinuousConvMode = DISABLE; /* 关闭连续转换模式 */
g_adc3_handle.Init.NbrOfConversion = 1; /* 1个转换在规则序列中 也就是只转换规则序列1 */
g_adc3_handle.Init.DiscontinuousConvMode = DISABLE; /* 禁止规则通道组间断模式 */
g_adc3_handle.Init.NbrOfDiscConversion = 0; /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */
g_adc3_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */
HAL_ADC_Init(&g_adc3_handle); /* 初始化 */
HAL_ADCEx_Calibration_Start(&g_adc3_handle); /* 校准ADC */
}
/**
* @brief 设置ADC通道采样时间
* @param adcx : adc句柄指针,ADC_HandleTypeDef
* @param ch : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17
* @param stime: 采样时间 0~7, 对应关系为:
* @arg ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期 ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期
* @arg ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期 ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期
* @arg ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期 ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期
* @arg ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期 ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期
* @param rank: 多通道采集时需要设置的采集编号,
假设你定义channle1的rank=1,channle2 的rank=2,
那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。
单通道DMA设置为 ADC_REGULAR_RANK_1
* @arg 编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16
* @retval 无
*/
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
ADC_ChannelConfTypeDef adc_ch_conf;
adc_ch_conf.Channel = ch; /* 通道 */
adc_ch_conf.Rank = rank; /* 序列 */
adc_ch_conf.SamplingTime = stime; /* 采样时间 */
HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf); /* 通道配置 */
}
/**
* @brief 获得ADC转换后的结果
* @param ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17
* @retval 无
*/
uint32_t adc3_get_result(uint32_t ch)
{
adc3_channel_set(&g_adc3_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5); /* 设置通道,序列和采样时间 */
HAL_ADC_Start(&g_adc3_handle); /* 开启ADC */
HAL_ADC_PollForConversion(&g_adc3_handle, 10); /* 轮询转换 */
return (uint16_t)HAL_ADC_GetValue(&g_adc3_handle); /* 返回最近一次ADC1规则组的转换结果 */
}
/**
* @brief 获取通道ch的转换值,取times次,然后平均
* @param ch : 通道号, 0~17
* @param times : 获取次数
* @retval 通道ch的times次转换结果平均值
*/
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{
uint32_t temp_val = 0;
uint8_t t;
for (t = 0; t < times; t++) /* 获取times次数据 */
{
temp_val += adc3_get_result(ch);
delay_ms(5);
}
return temp_val / times; /* 返回平均值 */
}
dac3.h
c
#ifndef __ADC_H
#define __ADC_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* ADC及引脚 定义 */
#define ADC3_CHY_GPIO_PORT GPIOA
#define ADC3_CHY_GPIO_PIN GPIO_PIN_1
#define ADC3_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define ADC_ADCX ADC3
#define ADC3_CHY ADC_CHANNEL_1 /* 通道Y, 0 <= Y <= 17 */
#define ADC3_CHY_CLK_ENABLE() do{ __HAL_RCC_ADC3_CLK_ENABLE(); }while(0) /* ADC1 时钟使能 */
/******************************************************************************************/
void adc3_init(void); /* ADC3初始化 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime); /* ADC3通道设置 */
uint32_t adc3_get_result(uint32_t ch); /* 获得某个通道值 */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times); /* 得到某个通道给定次数采样的平均值 */
#endif
dac.c
c
#include "./BSP/DAC/dac.h"
#include "./SYSTEM/delay/delay.h"
DAC_HandleTypeDef g_dac_handle; /* DAC句柄 */
/**
* @brief DAC初始化函数
* @note 本函数支持DAC1_OUT1/2通道初始化
* DAC的输入时钟来自APB1, 时钟频率=36Mhz=27.8ns
* DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 4us (F103数据手册有写)
* 因此DAC输出的最高速度约为:250Khz, 以10个点为一个周期, 最大能输出25Khz左右的波形
*
* @param outx: 要初始化的通道. 1,通道1; 2,通道2
* @retval 无
*/
void dac_init(uint8_t outx)
{
GPIO_InitTypeDef gpio_init_struct;
DAC_ChannelConfTypeDef dac_ch_conf;
__HAL_RCC_DAC_CLK_ENABLE(); /* 使能DAC1的时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5; /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
gpio_init_struct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
g_dac_handle.Instance = DAC;
HAL_DAC_Init(&g_dac_handle); /* 初始化DAC */
dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE; /* 不使用触发功能 */
dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; /* DAC1输出缓冲关闭 */
switch(outx)
{
case 1:
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1); /* 配置DAC通道1 */
HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_1); /* 开启DAC通道1 */
break;
case 2:
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_2); /* 配置DAC通道2 */
HAL_DAC_Start(&g_dac_handle,DAC_CHANNEL_2); /* 开启DAC通道2 */
break;
default:break;
}
}
/**
* @brief 设置通道1/2输出电压
* @param outx: 1,通道1; 2,通道2
* @param vol : 0~3300,代表0~3.3V
* @retval 无
*/
void dac_set_voltage(uint8_t outx, uint16_t vol)
{
double temp = vol;
temp /= 1000;
temp = temp * 4096 / 3.3;
if (temp >= 4096)temp = 4095; /* 如果值大于等于4096, 则取4095 */
if (outx == 1) /* 通道1 */
{
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
else /* 通道2 */
{
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
}
/***************************************DAC输出三角波实验代码*****************************************/
/**
* @brief 设置DAC_OUT1输出三角波
* @note 输出频率 ≈ 1000 / (dt * samples) Khz, 不过在dt较小的时候,比如小于5us时, 由于delay_us
* 本身就不准了(调用函数,计算等都需要时间,延时很小的时候,这些时间会影响到延时), 频率会偏小.
*
* @param maxval : 最大值(0 < maxval < 4096), (maxval + 1)必须大于等于samples/2
* @param dt : 每个采样点的延时时间(单位: us)
* @param samples: 采样点的个数, samples必须小于等于(maxval + 1) * 2 , 且maxval不能等于0
* @param n : 输出波形个数,0~65535
*
* @retval 无
*/
void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n)
{
uint16_t i, j;
float incval; /* 递增量 */
float Curval; /* 当前值 */
if((maxval + 1) <= samples)return ; /* 数据不合法 */
incval = (maxval + 1) / (samples / 2); /* 计算递增量 */
for(j = 0; j < n; j++)
{
Curval = 0;
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval); /* 先输出0 */
for(i = 0; i < (samples / 2); i++) /* 输出上升沿 */
{
Curval += incval; /* 新的输出值 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
delay_us(dt);
}
for(i = 0; i < (samples / 2); i++) /* 输出下降沿 */
{
Curval -= incval; /* 新的输出值 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
delay_us(dt);
}
}
}
/***************************************DAC输出正弦波实验代码*****************************************/
DMA_HandleTypeDef g_dma_dac_handle; /* 定义要搬运DAC数据的DMA句柄 */
DAC_HandleTypeDef g_dac_dma_handle; /* 定义DAC(DMA输出)句柄 */
extern uint16_t g_dac_sin_buf[4096]; /* 发送数据缓冲区 */
/**
* @brief DAC DMA输出波形初始化函数
* @note 本函数支持DAC1_OUT1/2通道初始化
* DAC的输入时钟来自APB1, 时钟频率=36Mhz=27.8ns
* DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 4us (F103数据手册有写)
* 因此DAC输出的最高速度约为:250Khz, 以10个点为一个周期, 最大能输出25Khz左右的波形
*
* @param outx: 要初始化的通道. 1,通道1; 2,通道2
* @param par : 外设地址
* @param mar : 存储器地址
* @retval 无
*/
void dac_dma_wave_init(uint8_t outx, uint32_t par, uint32_t mar)
{
GPIO_InitTypeDef gpio_init_struct;
DAC_ChannelConfTypeDef dac_ch_conf={0};
DMA_Channel_TypeDef *dmax_chy;
if (outx == 1)
{
dmax_chy = DMA2_Channel3; /* OUT1对应DMA2_Channel3 */
}
else
{
dmax_chy = DMA2_Channel4; /* OUT2对应DMA2_Channel4 */
}
__HAL_RCC_GPIOA_CLK_ENABLE(); /* DAC通道引脚端口时钟使能 */
__HAL_RCC_DAC_CLK_ENABLE(); /* 使能DAC1的时钟 */
__HAL_RCC_DMA2_CLK_ENABLE(); /* DMA2时钟使能 */
gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5; /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
gpio_init_struct.Mode = GPIO_MODE_ANALOG; /* 模拟 */
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
/* 初始化DMA */
g_dma_dac_handle.Instance = dmax_chy; /* 设置DMA通道 */
g_dma_dac_handle.Init.Direction = DMA_MEMORY_TO_PERIPH; /* 从存储器到外设模式 */
g_dma_dac_handle.Init.PeriphInc = DMA_PINC_DISABLE; /* 外设非增量模式 */
g_dma_dac_handle.Init.MemInc = DMA_MINC_ENABLE; /* 存储器增量模式 */
g_dma_dac_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据长度:16位 */
g_dma_dac_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; /* 存储器数据长度:16位 */
g_dma_dac_handle.Init.Mode = DMA_CIRCULAR; /* 外设流控模式 */
g_dma_dac_handle.Init.Priority = DMA_PRIORITY_MEDIUM; /* 中等优先级 */
HAL_DMA_Init(&g_dma_dac_handle); /* 初始化DMA */
HAL_DMA_Start(&g_dma_dac_handle, mar, par, 0); /* 配置DMA传输参数 */
__HAL_LINKDMA(&g_dac_dma_handle, DMA_Handle1, g_dma_dac_handle); /* DMA句柄与DAC句柄关联 */
/* 初始化DAC */
g_dac_dma_handle.Instance = DAC;
HAL_DAC_Init(&g_dac_dma_handle); /* 初始化DAC */
/* 配置DAC通道 */
dac_ch_conf.DAC_Trigger = DAC_TRIGGER_T7_TRGO; /* 不使用触发功能 */
dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; /* DAC1输出缓冲关闭 */
switch(outx)
{
case 1:
HAL_DAC_ConfigChannel(&g_dac_dma_handle, &dac_ch_conf, DAC_CHANNEL_1); /* 配置DAC通道1 */
break;
case 2:
HAL_DAC_ConfigChannel(&g_dac_dma_handle, &dac_ch_conf, DAC_CHANNEL_2); /* 配置DAC通道2 */
break;
default:break;
}
}
/**
* @brief DAC DMA使能波形输出
* @note TIM7的输入时钟频率(f)来自APB1, f = 36M * 2 = 72Mhz.
* DAC触发频率 ftrgo = f / ((psc + 1) * (arr + 1))
* 波形频率 = ftrgo / ndtr;
*
* @param ndtr : DMA通道单次传输数据量
* @param arr : TIM7的自动重装载值
* @param psc : TIM7的分频系数
* @retval 无
*/
void dac_dma_wave_enable(uint16_t cndtr, uint16_t arr, uint16_t psc)
{
TIM_HandleTypeDef tim7_handle= {0};
TIM_MasterConfigTypeDef tim_master_config= {0};
__HAL_RCC_TIM7_CLK_ENABLE(); /* TIM7时钟使能 */
tim7_handle.Instance = TIM7; /* 选择定时器7 */
tim7_handle.Init.Prescaler = psc; /* 预分频 */
tim7_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数器 */
tim7_handle.Init.Period = arr; /* 自动装载值 */
HAL_TIM_Base_Init(&tim7_handle); /* 初始化定时器7 */
tim_master_config.MasterOutputTrigger = TIM_TRGO_UPDATE; /* 定时器更新事件用于触发 */
tim_master_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&tim7_handle, &tim_master_config); /* 配置定时器7的更新事件触发DAC转换 */
HAL_TIM_Base_Start(&tim7_handle); /* 启动定时器7 */
HAL_DAC_Stop_DMA(&g_dac_dma_handle, DAC_CHANNEL_1); /* 先停止之前的传输 */
HAL_DAC_Start_DMA(&g_dac_dma_handle, DAC_CHANNEL_1, (uint32_t *)g_dac_sin_buf, cndtr, DAC_ALIGN_12B_R);
}
dac.h
c
#ifndef __DAC_H
#define __DAC_H
#include "./SYSTEM/sys/sys.h"
void dac_init(uint8_t outx); /* DAC通道1初始化 */
void dac_set_voltage(uint8_t outx, uint16_t vol); /* 设置通道1/2输出电压 */
void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n); /* 输出三角波 */
void dac_dma_wave_init(uint8_t outx, uint32_t par, uint32_t mar); /* DAC DMA输出波形初始化函数 */
void dac_dma_wave_enable(uint16_t cndtr, uint16_t arr, uint16_t psc); /* DAC DMA输出波形使能 */
#endif
main.c
c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DAC/dac.h"
#include "./BSP/ADC/adc3.h"
#include "math.h"
extern ADC_HandleTypeDef g_adc3_handle; /* ADC句柄 */
uint16_t g_dac_sin_buf[4096]; /* 发送数据缓冲区 */
/**
* @brief 产生正弦波函序列
* @note 需保证: maxval > samples/2
*
* @param maxval : 最大值(0 < maxval < 2048)
* @param samples: 采样点的个数
*
* @retval 无
*/
void dac_creat_sin_buf(uint16_t maxval, uint16_t samples)
{
uint8_t i;
float inc = (2 * 3.1415962) / samples; /* 计算增量(一个周期DAC_SIN_BUF个点)*/
float outdata = 0;
for (i = 0; i < samples; i++)
{
outdata = maxval * (1 + sin(inc * i)); /* 计算以dots个点为周期的每个点的值,放大maxval倍,并偏移到正数区域 */
if (outdata > 4095)
outdata = 4095; /* 上限限定 */
//printf("%f\r\n",outdata);
g_dac_sin_buf[i] = outdata;
}
}
/**
* @brief 通过USMART设置正弦波输出参数,方便修改输出频率.
* @param arr : TIM7的自动重装载值
* @param psc : TIM7的分频系数
* @retval 无
*/
void dac_dma_sin_set(uint16_t arr, uint16_t psc)
{
dac_dma_wave_enable(100, arr, psc);
}
int main(void)
{
uint16_t adcx;
float temp;
uint8_t t = 0;
uint8_t key;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
usmart_dev.init(72); /* 初始化USMART */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
adc3_init(); /* 初始化ADC */
adc3_channel_set(&g_adc3_handle, ADC3_CHY, ADC_CHANNEL_0, ADC_SAMPLETIME_1CYCLE_5);
/* 初始化DAC通道1 DMA波形输出 */
dac_dma_wave_init(1, (uint32_t)&DAC1->DHR12R1, (uint32_t)g_dac_sin_buf);
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "DAC DMA Sine WAVE TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY0:3Khz KEY1:30Khz", RED);
lcd_show_string(30, 130, 200, 16, 16, "DAC VAL:", BLUE);
lcd_show_string(30, 150, 200, 16, 16, "DAC VOL:0.000V", BLUE);
lcd_show_string(30, 170, 200, 16, 16, "ADC VOL:0.000V", BLUE);
dac_creat_sin_buf(2048, 100);
dac_dma_wave_enable(100, 10 - 1, 72 - 1); /* 100Khz触发频率, 100个点, 得到1Khz的正弦波 */
while (1)
{
t++;
key = key_scan(0); /* 按键扫描 */
if (key == KEY0_PRES) /* 高采样率 , 约1Khz波形 */
{
dac_creat_sin_buf(2048, 100);
dac_dma_wave_enable(100, 10 - 1, 24 - 1); /* 300Khz触发频率, 100个点, 得到最高3KHz的正弦波. */
}
else if (key == KEY1_PRES) /* 低采样率 , 约1Khz波形 */
{
dac_creat_sin_buf(2048, 10);
dac_dma_wave_enable(10, 10 - 1, 24 - 1); /* 300Khz触发频率, 10个点, 可以得到最高30KHz的正弦波. */
}
adcx = DAC1->DHR12R1; /* 获取DAC1_OUT1的输出状态 */
lcd_show_xnum(94, 130, adcx, 4, 16, 0, BLUE); /* 显示DAC寄存器值 */
temp = (float)adcx * (3.3 / 4096); /* 得到DAC电压值 */
adcx = temp;
lcd_show_xnum(94, 150, temp, 1, 16, 0, BLUE); /* 显示电压值整数部分 */
temp -= adcx;
temp *= 1000;
lcd_show_xnum(110, 150, temp, 3, 16, 0X80, BLUE); /* 显示电压值的小数部分 */
adcx = adc3_get_result_average(ADC3_CHY, 10); /* 得到ADC3通道1的转换结果 */
temp = (float)adcx * (3.3 / 4096); /* 得到ADC电压值(adc是12bit的) */
adcx = temp;
lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE); /* 显示电压值整数部分 */
temp -= adcx;
temp *= 1000;
lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE); /* 显示电压值的小数部分 */
if (t == 40) /* 定时时间到了 */
{
LED0_TOGGLE(); /* LED0闪烁 */
t = 0;
}
delay_ms(5);
}
}
六、PWM DAC实验(熟悉)
6.1, PWM DAC应用背景(了解)
6.2, PWM DAC技术实现原理(了解)
6.2.1,什么是PWM DAC技术?
6.2.2,用分段函数表示PWM波
6.2.3,将PWM波分段函数进行傅里叶级数展开
6.2.4,PWM DAC的分辨率
6.2.5,8位分辨率下对RC滤波器的设计要求
6.2.6,PWM DAC二阶低通滤波器原理图
6.3,编程实战: PWM DAC实验(掌握)
adc3.c
c
#include "./BSP/ADC/adc3.h"
#include "./SYSTEM/delay/delay.h"
ADC_HandleTypeDef g_adc3_handle; /* ADC句柄 */
/**
* @brief ADC3初始化函数
* @note 本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
* 我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期
* 设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us
* @param 无
* @retval 无
*/
void adc3_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
ADC3_CHY_GPIO_CLK_ENABLE(); /* IO口时钟使能 */
ADC3_CHY_CLK_ENABLE(); /* ADC时钟使能 */
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; /* ADC外设时钟 */
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; /* 分频因子6时钟为72M/6=12MHz */
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); /* 设置ADC时钟 */
/* 设置AD采集通道对应IO引脚工作模式 */
gpio_init_struct.Pin = ADC3_CHY_GPIO_PIN; /* ADC通道对应的IO引脚 */
gpio_init_struct.Mode = GPIO_MODE_ANALOG; /* 模拟 */
HAL_GPIO_Init(ADC3_CHY_GPIO_PORT, &gpio_init_struct);
g_adc3_handle.Instance = ADC_ADCX; /* 选择哪个ADC */
g_adc3_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* 数据对齐方式:右对齐 */
g_adc3_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; /* 非扫描模式,仅用到一个通道 */
g_adc3_handle.Init.ContinuousConvMode = DISABLE; /* 关闭连续转换模式 */
g_adc3_handle.Init.NbrOfConversion = 1; /* 1个转换在规则序列中 也就是只转换规则序列1 */
g_adc3_handle.Init.DiscontinuousConvMode = DISABLE; /* 禁止规则通道组间断模式 */
g_adc3_handle.Init.NbrOfDiscConversion = 0; /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */
g_adc3_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */
HAL_ADC_Init(&g_adc3_handle); /* 初始化 */
HAL_ADCEx_Calibration_Start(&g_adc3_handle); /* 校准ADC */
}
/**
* @brief 设置ADC通道采样时间
* @param adcx : adc句柄指针,ADC_HandleTypeDef
* @param ch : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17
* @param stime: 采样时间 0~7, 对应关系为:
* @arg ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期 ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期
* @arg ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期 ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期
* @arg ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期 ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期
* @arg ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期 ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期
* @param rank: 多通道采集时需要设置的采集编号,
假设你定义channle1的rank=1,channle2 的rank=2,
那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。
单通道DMA设置为 ADC_REGULAR_RANK_1
* @arg 编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16
* @retval 无
*/
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
ADC_ChannelConfTypeDef adc_ch_conf;
adc_ch_conf.Channel = ch; /* 通道 */
adc_ch_conf.Rank = rank; /* 序列 */
adc_ch_conf.SamplingTime = stime; /* 采样时间 */
HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf); /* 通道配置 */
}
/**
* @brief 获得ADC转换后的结果
* @param ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17
* @retval 无
*/
uint32_t adc3_get_result(uint32_t ch)
{
adc3_channel_set(&g_adc3_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5); /* 设置通道,序列和采样时间 */
HAL_ADC_Start(&g_adc3_handle); /* 开启ADC */
HAL_ADC_PollForConversion(&g_adc3_handle, 10); /* 轮询转换 */
return (uint16_t)HAL_ADC_GetValue(&g_adc3_handle); /* 返回最近一次ADC1规则组的转换结果 */
}
/**
* @brief 获取通道ch的转换值,取times次,然后平均
* @param ch : 通道号, 0~17
* @param times : 获取次数
* @retval 通道ch的times次转换结果平均值
*/
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{
uint32_t temp_val = 0;
uint8_t t;
for (t = 0; t < times; t++) /* 获取times次数据 */
{
temp_val += adc3_get_result(ch);
delay_ms(5);
}
return temp_val / times; /* 返回平均值 */
}
adc3.h
c
#ifndef __ADC_H
#define __ADC_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* ADC及引脚 定义 */
#define ADC3_CHY_GPIO_PORT GPIOA
#define ADC3_CHY_GPIO_PIN GPIO_PIN_1
#define ADC3_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define ADC_ADCX ADC3
#define ADC3_CHY ADC_CHANNEL_1 /* 通道Y, 0 <= Y <= 17 */
#define ADC3_CHY_CLK_ENABLE() do{ __HAL_RCC_ADC3_CLK_ENABLE(); }while(0) /* ADC1 时钟使能 */
/******************************************************************************************/
void adc3_init(void); /* ADC3初始化 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime); /* ADC3通道设置 */
uint32_t adc3_get_result(uint32_t ch); /* 获得某个通道值 */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times); /* 得到某个通道给定次数采样的平均值 */
#endif
pwmdac.c
c
#include "./BSP/PWMDAC/pwmdac.h"
TIM_HandleTypeDef g_tim1_handle; /* 定时器句柄 */
/**
* @brief PWM DAC初始化, 实际上就是初始化定时器
* @note
* 定时器的时钟来自APB1 / APB2, 当APB1 / APB2 分频时, 定时器频率自动翻倍
* 所以, 一般情况下, 我们所有定时器的频率, 都是72Mhz 等于系统时钟频率
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft = 定时器工作频率, 单位: Mhz
*
* @param arr: 自动重装值。
* @param psc: 时钟预分频数
* @retval 无
*/
void pwmdac_init(uint16_t arr, uint16_t psc)
{
TIM_OC_InitTypeDef timx_oc_pwmdac = {0};
PWMDAC_TIMX_CLK_ENABLE(); /* PWM DAC 定时器时钟使能 */
g_tim1_handle.Instance = TIM1; /* 定时器1 */
g_tim1_handle.Init.Prescaler = psc; /* 定时器分频 */
g_tim1_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_tim1_handle.Init.Period = arr; /* 自动重装载值 */
g_tim1_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; /* 使能TIMx_ARR进行缓冲 */
HAL_TIM_PWM_Init(&g_tim1_handle); /* 初始化PWM */
timx_oc_pwmdac.OCMode = TIM_OCMODE_PWM1; /* CH1/2 PWM模式1 */
timx_oc_pwmdac.Pulse = 0; /* 设置比较值,此值用来确定占空比 */
timx_oc_pwmdac.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出比较极性为高 */
HAL_TIM_PWM_ConfigChannel(&g_tim1_handle, &timx_oc_pwmdac, PWMDAC_TIMX_CHY); /* 配置TIM1通道1 */
HAL_TIM_PWM_Start(&g_tim1_handle, TIM_CHANNEL_1); /* 开启定时器1通道1 */
}
/**
* @brief 定时器底层驱动,时钟使能,引脚配置
* @note
* 此函数会被HAL_TIM_PWM_Init()调用
* @param htim:定时器句柄
* @retval 无
*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef gpio_init_struct;
if (htim->Instance == TIM1)
{
__HAL_RCC_TIM1_CLK_ENABLE(); /* 使能定时器1 */
__HAL_AFIO_REMAP_TIM1_PARTIAL(); /* TIM1通道引脚部分重映射使能 */
PWMDAC_GPIO_CLK_ENABLE(); /* GPIO 时钟使能 */
gpio_init_struct.Pin = PWMDAC_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(PWMDAC_GPIO_PORT, &gpio_init_struct); /* TIMX PWM CHY 引脚模式设置 */
}
}
/**
* @brief 设置PWM DAC输出电压
* @param vol : 0~3300,代表0~3.3V
* @retval 无
*/
void pwmdac_set_voltage(uint16_t vol)
{
float temp = vol;
temp /= 100; /* 缩小100倍, 得到实际电压值 */
temp = temp * 256 / 3.3; /* 将电压转换成PWM占空比 */
__HAL_TIM_SET_COMPARE(&g_tim1_handle, PWMDAC_TIMX_CHY, temp); /* 设置新的占空比 */
}
pwmdac.h
c
#ifndef __PWMDAC_H
#define __PWMDAC_H
#include "./SYSTEM/sys/sys.h"
/******************************************************************************************/
/* PWM DAC 引脚 和 定时器 定义 */
/* PWMDAC 默认是使用 PA8, 对应的定时器为 TIM1_CH1, 如果你要修改成其他IO输出, 则相应
* 的定时器及通道也要进行修改. 请根据实际情况进行修改.
*/
#define PWMDAC_GPIO_PORT GPIOA
#define PWMDAC_GPIO_PIN GPIO_PIN_8
#define PWMDAC_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define PWMDAC_TIMX TIM1
#define PWMDAC_TIMX_CHY TIM_CHANNEL_1 /* 通道Y, 1<= Y <=4 */
#define PWMDAC_TIMX_CCRX PWMDAC_TIMX->CCR1 /* 通道Y的输出比较寄存器 */
#define PWMDAC_TIMX_CLK_ENABLE() do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0) /* TIM1 时钟使能 */
/******************************************************************************************/
void pwmdac_init(uint16_t arr, uint16_t psc); /* PWM DAC初始化 */
void pwmdac_set_voltage(uint16_t vol); /* PWM DAC设置输出电压 */
#endif