前言
架构做得好,高产似母猪。
开工第一天,弄完ADC,接着弄DAC。
什么是DAC?
前面我们讲过了ADC是把模拟量转成数字量,那么DAC就是反过来,即把数字量转成模拟量。
DAC一般需要配置的内容包括:
- IO配置(时钟,模拟输入)
- DAC参数配置(触发源,附加噪声?,数据宽度)
- 中断和DMA(使能)配置
DAC的配置比较简单,GD32L233跟GD32F450几乎一样:
代码语言:javascript复制void dac1_init(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_DAC);
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_4);
dac_deinit();
/* software trigger */
dac_trigger_enable();
dac_trigger_source_config(DAC_TRIGGER_SOFTWARE);
/* no noise wave */
dac_wave_mode_config(DAC_WAVE_DISABLE);
/* noise wave - triangle */
//dac_wave_mode_config(DAC_WAVE_MODE_TRIANGLE);
//dac_triangle_noise_config(DAC_TRIANGLE_AMPLITUDE_4095);
/* noise wave - lfsr */
//dac_wave_mode_config(DAC_WAVE_MODE_LFSR);
//dac_lfsr_noise_config(DAC_LFSR_BITS11_0);
dac_output_buffer_enable();
/* enable DAC and set data */
dac_enable();
dac_software_trigger_enable();
dac_data_set(DAC_ALIGN_12B_R, 0);
}
DAC波形发生器
如何通过DAC产生任意频率的周期性波形?
对于一个周期性波形,实际上它是一个时间的函数,即:y=f(t)
抽象一把,我们要解决2个问题:
- x轴时间如何采样?
- y轴数据如何计算?
数学推导 - x轴
假设频率为f,周期为T(T=1/f),采样点数为M:
那么△t = T/M,当给定频率f时,M=1/(△t*f)
在软件逻辑中,△t是定时器周期,为已知变量。
【注:请复习下奈奎斯特定理】
数学推导 - y轴 方波
如下数学推导中,MAX为幅值。
代码语言:javascript复制y = 0 if p < M/2 else MAX
1HZ方波
数学推导 - y轴 三角波
代码语言:javascript复制y = p*MAX/(M/2) if p < M/2 else (M-p)*MAX/(M/2)
1HZ三角波
数学推导 - y轴 正弦波
正弦波可以用数学库中的sin函数来计算,但是比较费事,可以通过角度查表的方式来计算。
另外,由于sin函数包含负值,需要将数据向上平移到正值( 1)。
代码语言:javascript复制rad = 360*p/M
y = (sin_table(rad) 1) * MAX/2
0-90°的sin函数表如下所示:
代码语言:javascript复制const float sinus_I_quarter[91] =
{
0.0000, 0.0175, 0.0349, 0.0523, 0.0698, 0.0872, 0.1045, 0.1219, 0.1392, 0.1564, // 00 .. 09
0.1736, 0.1908, 0.2079, 0.2250, 0.2419, 0.2588, 0.2756, 0.2924, 0.3090, 0.3256, // 10 .. 19
0.3420, 0.3584, 0.3746, 0.3907, 0.4067, 0.4226, 0.4384, 0.4540, 0.4695, 0.4848, // 20 .. 29
0.5000, 0.5150, 0.5299, 0.5446, 0.5592, 0.5736, 0.5878, 0.6018, 0.6157, 0.6293, // 30 .. 39
0.6428, 0.6561, 0.6691, 0.6820, 0.6947, 0.7071, 0.7193, 0.7314, 0.7431, 0.7547, // 40 .. 49
0.7660, 0.7771, 0.7880, 0.7986, 0.8090, 0.8192, 0.8290, 0.8387, 0.8480, 0.8572, // 50 .. 59
0.8660, 0.8746, 0.8829, 0.8910, 0.8988, 0.9063, 0.9135, 0.9205, 0.9272, 0.9336, // 60 .. 69
0.9397, 0.9455, 0.9511, 0.9563, 0.9613, 0.9659, 0.9703, 0.9744, 0.9781, 0.9816, // 70 .. 79
0.9848, 0.9877, 0.9903, 0.9925, 0.9945, 0.9962, 0.9976, 0.9986, 0.9994, 0.9998, // 80 .. 89
1.0000 // 90
};
由于sin函数的对称性,91°~360°可以通过数学公式推导得到:
代码语言:javascript复制#define CIRCLE_QUARTER_1 1
#define CIRCLE_QUARTER_2 2
#define CIRCLE_QUARTER_3 3
#define CIRCLE_QUARTER_4 4
float sinus_lookup (unsigned int angle)
{
float sin_value;
unsigned int circle_quarter;
// correct angles outside the accepted angle range into 0 .. 359
if (angle > 359u)
angle = angle % 360u;
circle_quarter = 1 (angle / 90u);
switch (circle_quarter)
{
case CIRCLE_QUARTER_1: // 00 .. 89
sin_value = sinus_I_quarter[angle];
break;
case CIRCLE_QUARTER_2: // 90 .. 179
sin_value = sinus_I_quarter[180 - angle];
break;
case CIRCLE_QUARTER_3: // 180 .. 269
sin_value = -sinus_I_quarter[angle - 180];
break;
case CIRCLE_QUARTER_4: // 270 .. 359
sin_value = -sinus_I_quarter[360 - angle];
break;
}
return sin_value;
}
1HZ正弦波
100HZ正弦波
程序设计
波形程序
我们通过一个0.1ms的定时器作为△t,在定时中断函数中执行绘图函数。
代码语言:javascript复制void plot_sin(uint32_t f, uint32_t delta_f)
{
/* 定时周期为T=1/delta_f, f=1/(pMax*T) */
static uint32_t point = 0;
uint32_t pMAX = delta_f/f;
uint32_t value = 0;
if (point > pMAX) point = 0;
value = (uint32_t)((sinus_lookup(360*point/pMAX) 1)*10000)*2047/10000;
dac_software_trigger_enable(DAC0);
dac_data_set(DAC0, DAC_ALIGN_12B_R, value);
}
void plot_triangle(uint32_t f, uint32_t delta_f)
{
/* 定时周期为T=1/delta_f, f=1/(pMax*T) */
static uint32_t point = 0;
uint32_t pMAX = delta_f/f;
uint32_t pMAX2 = pMAX/2;
uint32_t value = 0;
if ( point > pMAX) point = 0;
if (point < pMAX2)
{
value = point * 4095 / pMAX2;
}
else
{
value = (pMAX - point) * 4095 / pMAX2;
}
dac_software_trigger_enable(DAC0);
dac_data_set(DAC0, DAC_ALIGN_12B_R, value);
}
void plot_square(uint32_t f, uint32_t delta_f)
{
/* 定时周期为T=1/delta_f, f=1/(pMax*T) */
static uint32_t point = 0;
uint32_t pMAX = delta_f/f;
uint32_t pMAX2 = pMAX/2;
uint32_t value = 0;
if ( point > pMAX) point = 0;
if (point < pMAX2)
{
value = 0;
}
else
{
value = 0xFFF;
}
dac_software_trigger_enable(DAC0);
dac_data_set(DAC0, DAC_ALIGN_12B_R, value);
}
GD32的定时器配置和中断:
代码语言:javascript复制void timer3_init(void)
{
timer_deinit(TIMER2);
rcu_periph_clock_enable(RCU_TIMER2);
#ifdef DAC_WAVE_TEST
timerx_init(TIMER2, 639, 9); // 100KHz 0.1ms
#endif
timer_interrupt_enable(TIMER2, TIMER_INT_UP);
nvic_irq_enable(TIMER2_IRQn, 3);
}
代码语言:javascript复制void TIMER2_IRQHandler(void)
{
#ifdef DAC_WAVE_TEST
//plot_sin(100, 10000);
plot_triangle(1, 10000);
//plot_square(1, 10000);
#endif
timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);
}
测试结果
10HZ的正弦波:
10HZ的正弦波
示波器波形(PA4管脚):