DAC digital to analog converter
A brief introduction to DAC
DAC is a device that converts digital quantities into analog quantities. Simply put, we can program it to output a specific voltage value (the voltage value is an analog quantity).
That is to say, stm32G4 integrates a DAC module inside, and then through programming, we can output a voltage on a specific pin of stm32. Or output a changing voltage waveform (such as sine wave, triangle wave, etc.).
You can open the Pin Definition of the stm32G4 data sheet to find out which pin of the stm32 has the DAC function.
You can see that this development board has a DAC output, which is DAC1, and this output has two channels, corresponding to the PA4 and PA5 pins respectively.
The DAC features of the stm32G4 chip are:
- Supports left-aligned or right-aligned data, the data is 12 bits
- Support two channels output at the same time
- Supports output noise waveforms and triangle waveforms
- Support DMA output
- Can be triggered externally
- Supports buffered output and unbuffered output modes
programming
The purpose is to program so that PA4 and PA5 can output voltage, and this voltage can be controlled by the program.
- [Template] As a project for STM32CUBEMX generated code;
- Configure I0 of DAC output;PA4→DAC1_OUT1;PA5→DAC1_OUT2
- DAC output mode: output to external pin;
- Port dac.c and dac.h to [Programming Project]
4.1main.c contains #include "dac.h"
4.2 Add stm32g4xx hal dac,c and stm32g4xx_hal_dac_ex.c to the project; 4.3stm32g4xx hal_confh start the DAC module
4.4 In the main Call MX_DAC1_Init() in the function
4.5 to complete the HAL DAC SetValue function and HAL_DAC_Start function testing;
First, we need to set PA4 and PA5 to DAC output mode in the template project:
after setting these two pins, they are still yellow because we have not configured their output mode yet. Find DAC1 in the "Analog" option on the right, and then configure the output mode of the two pins: there
are three modes in total:
the first is to connect the voltage generated by the DAC to the outside through the pin, which is what we need.
The second is to output the DAC voltage as a voltage reference to other peripherals.
The third type is to output to external pins and then output to on-chip and on-chip peripherals.
We just need to set it to the first type. The complete configuration is as shown below:
Then click to generate code. Then transplant the dac.c and dac.h files into our programming files.
The most important points when transplanting a program are to check whether the library functions have been added, whether the clock is properly matched, whether the main function includes the header file, and whether the corresponding config has comments removed.
After completing the transplantation process
Since the modes we use are all 12-bit data, the variables defined when defining variables for storing dac must be 2-byte data, which is 16-bit data. (Because 1 byte only has 8 bits of data, which is not enough)
The final main.c is as follows:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "led.h"
#include "key.h"
#include "i2c.h"
#include "dac.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//Led执行程序
__IO uint32_t ledTick =0,keyTick=0;
u8 led_ctrl=0xff;
void LED_Process(void)
{
if(uwTick-ledTick<500)return;
ledTick=uwTick;
LED_Control(led_ctrl);
led_ctrl=~led_ctrl;
}
void KEY_Process(void)
{
if(uwTick-keyTick<10)return;
keyTick=uwTick;
Key_Read();
// if(Trg&0x01)
// {
// LED_Control(0x01);
// }
if(Trg)
{
LED_Control(Trg);
}
}
void LCD_Process(void)
{
u8 display_buf[20];
//[问题]长数据对端数据的覆盖问题
sprintf((char*)display_buf,"%d",4000);
LCD_DisplayStringLine(Line0,display_buf);
sprintf((char*)display_buf,"%d",10);
LCD_DisplayStringLine(Line0,display_buf);
//解决方案:加空格,针对字符串
LCD_DisplayStringLine(Line2,"hello");
LCD_DisplayStringLine(Line2,"h ");
//解决方案:格式化输出,针对数据
sprintf((char*)display_buf,"%5d",5000);//默认5位,显示右对齐
LCD_DisplayStringLine(Line3,display_buf);
sprintf((char*)display_buf,"%5d",10);
LCD_DisplayStringLine(Line3,display_buf);
sprintf((char*)display_buf,"%-5d",10);//左对齐
LCD_DisplayStringLine(Line4,display_buf);
sprintf((char*)display_buf,"%05d",500);//前面补0
LCD_DisplayStringLine(Line5,display_buf);
sprintf((char*)display_buf,"%5.2f",3.1415926);//显示小鼠,总长是5位,小数点算一位
LCD_DisplayStringLine(Line6,display_buf);
sprintf((char*)display_buf,"%x",15);
LCD_DisplayStringLine(Line7,display_buf);//%x显示16进制,%o显示8进制
sprintf((char*)display_buf,"%c",'a');
LCD_DisplayStringLine(Line8,display_buf);//%s字符串,%c是字符
sprintf((char*)display_buf,"%d %%",10);
LCD_DisplayStringLine(Line9,display_buf);//输出百分号:%
}
u8 val_24c02=0;
u16 dac_ch1_val,dac_ch2_val;
void DAC_Process(void)
{
dac_ch1_val=(1.1f/3.3f*4095);//输出1.1V
dac_ch2_val=(2.1f/3.3f*4095);//输出2.2V
HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_ch1_val);//0->0V;4095->3.3V
HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,DAC_ALIGN_12B_R,dac_ch2_val);//0->0V;4095->3.3V
HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
LED_Control(0x00);
MX_DAC1_Init();
//LCD_Process();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
LCD_Process();
I2CInit();
EEPROM_Write(0x10,0x55);
val_24c02=EEPROM_Read(0x10);
u8 display_buf[20];
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//LED_Process();
sprintf((char*)display_buf,"EEPROM:%d",val_24c02);
LCD_DisplayStringLine(Line1,display_buf);//输出百分号:%
KEY_Process();
DAC_Process();
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {
0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {
0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
One thing to note is:
DAC is best assigned first and then started (this is the following order)
HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_ch1_val);//0->0V;4095->3.3V
HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
It is best to start the ADC first and then assign values.