天天看點

程式源碼分享-STM32将mpu6050的資料DAC并用LCD顯示一、DAC配置二、程式分享

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





           

繼續閱讀