本次硬件选用的是正点原子STM32精英版开发板、维特智能的mpu6050(JY62)。通过将JY62读取到的角度数据用lcd显示再DAC并用ADC进行验证。
一、DAC配置
1)开启 PA 口时钟,设置 PA4 为模拟输入。
STM32F103ZET6 的 DAC 通道 1 在 PA4 上,所以,我们先要使能 PORTA 的时钟,然后设置 PA4 为模拟输入。DAC 本是输出,但是为什么端口要设置为模拟输入模式呢?因为一但使能 DACx 通道之后,相应的 GPIO 引脚(PA4 或者 PA5)会自动与 DAC 的模拟输出相连,设置为输入,是为了避免额外的干扰。
使能 GPIOA 时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能 PORTA 时钟
设置 PA1 为模拟输入只需要设置初始化参数即可:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
2)使能 DAC1 时钟。
同其他外设一样,要想使用,必须先开启相应的时钟。STM32 的 DAC 模块时钟是由 APB1提供的,所以我们调用函数 RCC_APB1PeriphClockCmd()设置 DAC 模块的时钟使能。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能 DAC 通道时钟
3)初始化 DAC,设置 DAC 的工作模式。
该部分设置全部通过 DAC_CR 设置实现,包括:DAC 通道 1 使能、DAC 通道 1 输出缓存关闭、不使用触发、不使用波形发生器等设置。这里 DMA 初始化是通过函数 DAC_Init 完成的:
void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct)
跟前面一样,首先我们来看看参数设置结构体类型 DAC_InitTypeDef 的定义:
typedef struct
{
uint32_t DAC_Trigger;
uint32_t DAC_WaveGeneration;
uint32_t DAC_LFSRUnmask_TriangleAmplitude;
uint32_t DAC_OutputBuffer;
}DAC_InitTypeDef;
这个结构体的定义还是比较简单的,只有四个成员变量。
第一个参数 DAC_Trigger 用来设置是否使用触发功能,前面已经讲解过这个的含义,这里我们不是用触发功能,所以值为DAC_Trigger_None。
第二个参数 DAC_WaveGeneratio 用来设置是否使用波形发生,这里我们前面同样讲解过不使用。所以值为DAC_WaveGeneration_None。
第三个参数 DAC_LFSRUnmask_TriangleAmplitude 用来设置屏蔽/幅值选择器,这个变量只在使用波形发生器的时候才有用,这里我们设置为 0 即可,值为 DAC_LFSRUnmask_Bit0。
第四个参数 DAC_OutputBuffer 是用来设置输出缓存控制位,前面讲解过,我们不使用输出缓存,所以值为 DAC_OutputBuffer_Disable。到此四个参数设置完毕。
5)设置 DAC 的输出值。
通过前面 4 个步骤的设置,DAC 就可以开始工作了,我们使用 12 位右对齐数据格式,所以我们通过设置 DHR12R1,就可以在 DAC 输出引脚(PA4)得到不同的电压值了。
void Dac1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitType;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高
DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值
}
二、程序分享
因为程序中ADC用的很简单,而且程序也有很多注释,所以不做过多解释,直接分享给大家一起交流。
硬件接线:
USB-TTL工具 STM32Core JY61
VCC ----- VCC ---- VCC
TX ----- RX1(管脚10)
RX ----- TX1(管脚9)
GND ----- GND ---- GND
RX2 (管脚3) ---- TX
TX2 (管脚2) ---- RX
------------------------------------
*/
#include <string.h>
#include <stdio.h>
#include "Main.h"
#include "UART1.h"
#include "UART2.h"
#include "delay.h"
#include "JY61.h"
#include "DIO.h"
#include "dac.h"
#include "adc.h"
#include "lcd.h"
struct SAcc stcAcc;
struct SGyro stcGyro;
struct SAngle stcAngle;
//用串口2给JY模块发送指令
void sendcmd(char cmd[])
{
char i;
for(i=0;i<3;i++)
UART2_Put_Char(cmd[i]);
}
//CopeSerialData为串口2中断调用函数,串口每收到一个数据,调用一次这个函数。
void CopeSerial2Data(unsigned char ucData)
{
static unsigned char ucRxBuffer[250];
static unsigned char ucRxCnt = 0;
LED_REVERSE(); //接收到数据,LED灯闪烁一下
ucRxBuffer[ucRxCnt++]=ucData; //将收到的数据存入缓冲区中
if (ucRxBuffer[0]!=0x55) //数据头不对,则重新开始寻找0x55数据头
{
ucRxCnt=0;
return;
}
if (ucRxCnt<11) {return;}//数据不满11个,则返回
else
{
switch(ucRxBuffer[1])//判断数据是哪种数据,然后将其拷贝到对应的结构体中,有些数据包需要通过上位机打开对应的输出后,才能接收到这个数据包的数据
{
//memcpy为编译器自带的内存拷贝函数,需引用"string.h",将接收缓冲区的字符拷贝到数据结构体里面,从而实现数据的解析。
case 0x51: memcpy(&stcAcc,&ucRxBuffer[2],8);break;
case 0x52: memcpy(&stcGyro,&ucRxBuffer[2],8);break;
case 0x53: memcpy(&stcAngle,&ucRxBuffer[2],8);break;
}
ucRxCnt=0;//清空缓存区
}
}
void CopeSerial1Data(unsigned char ucData)
{
UART2_Put_Char(ucData);//转发串口1收到的数据给串口2(JY模块)
}
//定义
float ang1,ang2,ang3;
float vol;
u16 adcx;
float temp;
u8 x=0;
int main(void)
{
unsigned char i = 0;
SysTick_init(72,10);//设置时钟频率
Initial_UART1(9600);//接PC的串口
Initial_UART2(115200);//接JY61模块的串口
LED_ON();
delay_ms(1000);delay_ms(1000);//等等JY61初始化完成
//功能现象,10秒钟左右会进行一次加速度校准,Z轴角度归零,XYZ角度会缓慢回到0度状态
while(1)
{
delay_ms(1000);
i++;
if(i>9)
{
i = 0;
printf("正在进行加速度校准\r\n");
sendcmd(ACCCMD);//等待模块内部自动校准好,模块内部会自动计算需要一定的时间
printf("加速度校准完成\r\n");
delay_ms(100);
printf("进行Z轴角度清零\r\n");
sendcmd(YAWCMD);
printf("Z轴角度清零完成\r\n");
}
printf("-----------------------------------\r\n");
//输出加速度
//串口接受到的数据已经拷贝到对应的结构体的变量中了,根据说明书的协议,以加速度为例 stcAcc.a[0]/32768*16就是X轴的加速度,
printf("Acc:%.3f %.3f %.3f\r\n",(float)stcAcc.a[0]/32768*16,(float)stcAcc.a[1]/32768*16,(float)stcAcc.a[2]/32768*16);
delay_ms(10);
//输出角速度
printf("Gyro:%.3f %.3f %.3f\r\n",(float)stcGyro.w[0]/32768*2000,(float)stcGyro.w[1]/32768*2000,(float)stcGyro.w[2]/32768*2000);
delay_ms(10);
//输出角度
printf("Angle:%.3f %.3f %.3f\r\n",(float)stcAngle.Angle[0]/32768*180,(float)stcAngle.Angle[1]/32768*180,(float)stcAngle.Angle[2]/32768*180);
delay_ms(10);
//运算
ang1=(float)stcAngle.Angle[0]/32768*180;
ang2=(float)stcAngle.Angle[1]/32768*180;
ang3=(float)stcAngle.Angle[2]/32768*180;
printf("Angs:%.3f %.3f %.3f\r\n",ang1,ang2,ang3);
LCD_Init(); //LCD初始化
Adc_Init(); //ADC初始化
Dac1_Init(); //DAC通道1初始化
DAC_SetChannel1Data(DAC_Align_12b_R,0);
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(60,50,200,16,16,"STM32");
LCD_ShowString(60,70,200,16,16,"DAC TEST");
LCD_ShowString(60,90,200,16,16,"2021/7/30");
//显示提示信息
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(60,110,200,16,16,"DAC VAL:");
LCD_ShowString(60,130,200,16,16,"DAC VOL:0.000V");
LCD_ShowString(60,150,200,16,16,"ADC VOL:0.000V");
LCD_ShowString(60,170,200,16,16,"Angx:");
LCD_ShowString(60,190,200,16,16,"Angy:");
LCD_ShowString(60,210,200,16,16,"Angz:");
LCD_ShowxNum(100,170,ang1,4,16,0);
LCD_ShowxNum(100,190,ang2,4,16,0);
LCD_ShowxNum(100,210,ang3,4,16,0);
//DAC部分
DAC_SetChannel1Data(DAC_Align_12b_R,ang1);
adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
LCD_ShowxNum(124,110,adcx,4,16,0); //显示DAC寄存器值
temp=(float)adcx*(3.3/4096); //得到DAC电压值
adcx=temp;
LCD_ShowxNum(124,130,temp,1,16,0); //显示电压值整数部分
temp-=adcx;
temp*=1000;
LCD_ShowxNum(140,130,temp,3,16,0X80); //显示电压值的小数部分
adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值
temp=(float)adcx*(3.3/4096); //得到ADC电压值
adcx=temp;
LCD_ShowxNum(124,150,temp,1,16,0); //显示电压值整数部分
temp-=adcx;
temp*=1000;
LCD_ShowxNum(140,150,temp,3,16,0X80); //显示电压值的小数部分
delay_ms(10);
}
}//主循环