本次硬體選用的是正點原子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);
}
}//主循環