之前发帖:
【瑞萨RA4系列开发板体验】1. 新建工程+按键控制LED
【瑞萨RA4系列开发板体验】2. KEIL环境搭建+STLINK调试+FreeRTOS使用
【瑞萨RA4系列开发板体验】3. KEIL下UART实现printf与scanf重定向
【瑞萨RA4系列开发板体验】4. PWM驱动LED
【瑞萨RA4系列开发板体验】5. 硬件IIC驱动OLED显示汉字
【瑞萨RA4系列开发板体验】6. ADC测量摇杆模块偏移量
【瑞萨RA4系列开发板体验】7. 用DAC输出正弦波以及余弦波
【瑞萨RA4系列开发板体验】8. 超声波测距模块在RA4M2上的应用
前言
前面已经开了一篇帖子来描述DAC如何使用了,为什么又要写一篇呢,那是因为我发现DAC结合示波器能玩出一些新花样,将示波器设置为XY模式之后就可以用来显示李萨如图像,通过sin函数以及cos函数的变形就可以显示出不同图案的数据,如果有精力和时间的话,显示动画,打游戏都是不是问题。
其实本文已经在其他平台发布过了,只不过是针对示波器的试用,使用了该单片的DAC输出波形,让示波器显示一个跳动的爱心。因为是基于RA4M2来制作的,所以也将流程在此处发布一下,也算是对RA4M2的一个试用方向。
XY模式简单介绍
示波器都可以设置为XY模式,就是一路信号作为X轴,一路信号作为Y轴,两路信号相交的位置就显示点,根据该模式的特性,可以显示图形。
试验环境
信号源:瑞萨RA4M2单片机两路DAC,精度为12bit,最大输出值为4096,最大输出电压为3.3V;
DS100:两路示波器信号输入,CHA作为X轴,CHB作为Y轴。
试验
输出正弦信号
详见我之前关于DAC那篇文章描述。
、首先是测试单片机的DAC模块是否能够正常输出正弦信号,这是后面试验的基础,设置时基模式为ROLLO机会YT。
然后编写一个测试代码:如下:
其中,sin(xxx) * 2048 + 2048的目的是将DAC输出信号编程一个正数。
让DAC0输出正弦波,DAC1输出余弦波。
其中time_cnt就是0~2π,每次的增长周期为0.05,没1毫秒增长一次,这样可以让波形看起来更新连续,如果需要增加进行精度,可以修改为更小的值。
实现原理:
因为DAC不能输出负电压,所以需要将sin计算出来的负数部分进行偏移,实现原理见下方代码,sin最小值为-1,所以将DAC输出值偏移2048,然后加上剩余值2048,当sin计算值-1的时候,DAC则输出0,当sin计算值为1的时候,DAC输出4096。
dac_value[0] = (uint16_t)(sin(time_cnt) * (float)2048) + 2048;
dac_value[1] = (uint16_t)(sin(time_cnt + 0.5*PI_MATH) * (float)2048) + 2048;
Adc_OutputVal(DAC_DAC0, dac_value[0]);
Adc_OutputVal(DAC_DAC1, dac_value[1]);
测试李萨如图像
1输出一个圆
使用单片机输出两路DAC正弦信号,使用了C语言标准库的sin函数,两路正弦波信号有相位差,相位差为0的时候显示的是一条斜线,随着相位差增加,会变为一个椭圆,当相位差为π/2的时候就会显示成一个圆形。
2输出变动的李萨如图像
调整DAC0与DAC1输出正弦信号的相位差就可以改变李萨如图像为椭圆或者斜线,见下视频。
代码中只需要修改DAC1输出的信号的相位差即可,其中change应该随着时间变化,变化的周期自行编写,最好是一个完整的sin信号输出周期之后在改变,不然图形显示不完整。
dac_value[1] = (uint16_t)(sin(time_cnt + change*PI_MATH) * (float)2048) + 2048;
视频上传了bilibili:
3输出一个有大小可变的圆。
设置DAC0与DAC1的输出正弦信号的相位差为π/2,然后修改输出信号的幅值就可以修改圆形的半径,其中半径R_circle为周期可变的。
将上述中的偏移值2048设置为一个可变值,让它随着时间变化,则可以改变椭圆的半径,如下:
dac_value[0] = (uint16_t)(sin(time_cnt) * (float)R_circle) + R_circle;
dac_value[1] = (uint16_t)(sin(time_cnt + 0.5*PI_MATH) * (float)R_circle) + R_circle;
Adc_OutputVal(DAC_DAC0, dac_value[0]);
Adc_OutputVal(DAC_DAC1, dac_value[1]);
if (time_cnt > PI_DOUBLE)
{
time_cnt = 0.0f;
R_circle -= 20;
if (R_circle <= 100)
R_circle = 2048;
}
else
{
time_cnt += 0.05f;
}
视频上传了bilibili:
输出爱心
知道了上面的原理之后,我们可以用sin以及cos输出一个爱心,计算公式如下:
x = 2sin(t)+sin(2t);
y = -(2cos(t)+cos(2t));
同上述原理,DAC不能输出负数,y轴最小值会输出-3,所以我们需要将DAC的输出值分成6份,用于抵消负数,实现代码如下:
需要注意的是,测试中需要自己调整示波器的刷新频率以及单片机输出信号的切换频率,让两者保持在一个平衡点之后,刷新出来的爱心跳动才会稳定,不然会有一个余辉影响视觉效果。
演示视频见文章头部
完整代码实现
代码写的比较乱,但是逻辑相对简单,还是比较容易理解。
#define POLT_MODE 1
#define PI_MATH (3.14)
#define PI_DOUBLE (PI_MATH * 2)
uint16_t dac_value[2];
double time_cnt;
double change = 0;
uint16_t change_up_cnt = 0;
uint16_t R_circle = 2048;
uint16_t love_R = 682;
uint8_t love_flg = 0;
uint8_t love_chane_t = 0;
double my_cos(double time_)
{
return sin(time_ + 0.5*PI_MATH);
}
void hal_entry(void)
{
Uart_Init();
I2c_Init();
OLED_Init();
Adc_Init();
Dac_Init();
OLED_ShowHzStringRow(8, 0, (const char*)"正点原子示波器", 1);
OLED_ShowString(8, 16, (const uint8_t*)"elecfans|hehung", 16, 1);
OLED_ShowString(16, 32, (const uint8_t*)"XY Mode Test", 16, 1);
OLED_Refresh_Gram();
while (1)
{
#if (POLT_MODE == 0)
dac_value[0] = (uint16_t)(sin(time_cnt) * (float)2048) + 2048;
dac_value[1] = (uint16_t)(sin(time_cnt + change*PI_MATH) * (float)2048) + 2048;
Adc_OutputVal(DAC_DAC0, dac_value[0]);
Adc_OutputVal(DAC_DAC1, dac_value[1]);
if (time_cnt > PI_DOUBLE)
{
time_cnt = 0.0f;
change_up_cnt = 0U;
change += 0.01f;
if (change > PI_DOUBLE)
{
change = 0.0f;
}
}
else
{
time_cnt += 0.05f;
}
#elif (POLT_MODE == 1)
double x = (2 * sin(time_cnt) + sin(2 * time_cnt));
double y = (2 * cos(time_cnt) + cos(2 * time_cnt));
dac_value[0] = 4096-((uint16_t)((x * (float)love_R) + (3*love_R)));
dac_value[1] = 4096-((uint16_t)((y * (float)love_R) + (3*love_R)));
Adc_OutputVal(DAC_DAC0, dac_value[0]);
Adc_OutputVal(DAC_DAC1, dac_value[1]);
if (time_cnt > PI_DOUBLE)
{
time_cnt = 0.0f;
love_chane_t ++;
if (love_chane_t > 10)
{
love_chane_t = 0;
love_R = (love_flg == 0) ? (love_R - 40) : (love_R + 40);
love_flg ^= 1;
}
}
else
{
time_cnt += 0.05f;
}
#else
dac_value[0] = (uint16_t)(sin(time_cnt) * (float)R_circle) + R_circle;
dac_value[1] = (uint16_t)(sin(time_cnt + 0.5*PI_MATH) * (float)R_circle) + R_circle;
Adc_OutputVal(DAC_DAC0, dac_value[0]);
Adc_OutputVal(DAC_DAC1, dac_value[1]);
if (time_cnt > PI_DOUBLE)
{
time_cnt = 0.0f;
R_circle -= 20;
if (R_circle <= 100)
R_circle = 2048;
}
else
{
time_cnt += 0.05f;
}
#endif
R_BSP_SoftwareDelay(210, BSP_DELAY_UNITS_MICROSECONDS);
}
#if BSP_TZ_SECURE_BUILD
R_BSP_NonSecureEnter();
#endif
}
总结
从这个试验可以看出,RA4M2的DAC能够满足基本的电压输出需求,能够满足快速变换电压的输出请求,而且DAC使用起来也十分的方便。