2021暑假一起练--基于STM32F072制作双通道可调直流电压
基于STM32F072微控制器,支持双通道示波器、单通道波形发生器、单通道脉冲发生器、双路可调直流电压源,240*240 LCD显示,2个按键和一个拨轮开关,适合教学实验使用以及低于100KHz频率以内的测试测量。
标签
嵌入式系统
测试
电源变换及管理
lxb
更新2021-09-15
1219

系统构成如下:

FmgiPxPdJfMj8h8gewRY01jT9Ty8

      STM32F072CB-主流ARM Cortex-M0 USB系列MCU,具有128 KB Flash、48 MHz CPU、USB、CAN和CEC功能(https://www.eetree.cn/doc/detail/2301

     我选择的项目如下:(本想完成示波器,奈何硬件spi的lcd屏玩不转,刷新一直有问题)

项目3 制作双通道可调直流电压

  1. 通过STM32F072的内部定时器产生PWM,进而生成可调的直流电压,输出电压的变化范围为-4V到+4V
  2. 双路直流电压可以设置为独立模式调节,也可以设置成为跟踪模式调节,也就是DC1调节为2.5V的时候,DC2自动为-2.5V,当DC1调节为1.8V的时候,DC2自动为-1.8V
  3. 在LCD屏幕上显示两路DC当前的值,以及调节菜单

      

这里首先感谢strongerHuang微信公众号,里面有stm32f0系列的较为完整的教程。(CSDN相关博客汇总:https://blog.csdn.net/ybhuangfugui/category_6247312.html?spm=1001.2014.3001.5482

首先是cubeMX配置相应stm32管脚:

FodV90u_UmYwUJ7UpzxHdpSeOsYg

GPIO配置:

Fh4j_VvvsTkEKnw-8M8YddhEiG4_

ADC配置:

Fj1ria7wOIaYdSzqIHgYIbJjpVtf

TIM15配置:

FoK1WM537G54xJlUsK2ViN10v35D

SPI配置:

Fp6A-P8Cyk-wIqk2RW1r1J1Xy0GG

时钟配置(按照第一次直播时做的):

FoPyT3UTXnH8rWmmN8gSOdU66WjD

其它配置:

工程IDE用的是STM32CubeIDE

FhsNQLCTsgEcEaALeqQ7hWeKlmXQ

都是HAL库,直播提到可以使用LL库与DMA会更快更有效率,但奈何我不会,以后会慢慢学的。

之后生成代码即可,主体就有了。

记得配置这个:

Ft9M4dXhKMfayCsWvKEsV9U5mnCF

其实其中某些配置可以在CubeMX中完成,以下文章仅供参考:

1. GPIO配置详细过程

2. TIM基本延时配置详细过程

3. EXIT中断配置详细过程

4. ADC采集电压配置详细过程

5. TIM输出PWM配置详细过程

TIM的比较输出功能,输出可调PWM波形。直接调用函数接口“TIM2_CH1_PWM(uint32_t Freq, uint16_t Dutycycle)”传入频率和占空比就能输出指定的波形。

因为pwm的Counter Period设置为999,所以pwm的取值范围是0~999。使用万用表协助测量,在pwm值为530时,输出为43mV,接近于0。DC输出电压+4v~-4v对应的PWM取值范围为【210,850】,与该同学略有偏差,可能为仪器原因。

基本就差不多了。

下面是硬件配置SPI驱动lcd屏(但我个人做的其实并不好):

CubeMX,HAL库使用硬件SPI

STM32驱动1.3寸IPS的LCD

STM32的硬件SPI驱动LCD例子

以上是完成该项目时参考的部分博客,如要完成其它项目以上是不够的。

 

补充一下项目的原理:

      简单来说就是将波形图上平直的直流电压线,斩波成一段一段的矩形波,即通过改变PWM波的占空比和频率,以实现平均电压小于原来的直流电压。

上原理图:

FoamRMxhb6Y0zYySLHgC95EVm8uw

通过图可以看出想达到+-4V的输出,得通过运放来将PWM_DC抬高到4V以上(直播中有讲解)

然后这一部分还可以参考这篇文章:使用PWM得到精密的输出电压

 

至于如何改变PWM波的占空比和频率,请参考这篇文章:练习STM32动态更改PWM波频率和占空比

我提炼一下:

这部分操作原理上是:当以定时器为驱动时,定时器的计数频率就是PWM波的频率,然后根据TIMx_CCRx设置的值和定时器计数器当前的数值TIMx_CNT比较大小,根据比较结果输出高低电平。比较结果和高低电平之间的关系就是我们设置的PWM对齐方式。

具体操作上:一、调节占空比:只要根据设置的TIMx_ARR寄存器的值和所需要的占空比设置TIMx_CCRx寄存器的值即可。 二、调节频率:①更改预分频器的值,改变计数器的频率;②计数器频率一定时,改变TIMx_ARR的值。

 

对设备的一些感像:

1.杜邦线让仪器在测试时略有些不大方便,且线有些杂乱,不过官方的固件还是很棒的

2.经过直播的学习和实践,对示波器的简易原理以及如何提高采样准确率有了一定的了解,不过遗憾的是最终没能做出示波器来

3.将这个练熟后尝试入手一个adalm2000

以后学习的重点:

1.TT库

2.DMA

3.硬件SPI

下面贴代码:

main.c:

#include "main.h"
#include "adc.h"
#include "spi.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "lcd.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/* 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 */
uint16_t pwm_dc[2]={PWMZERO,PWMZERO};
uint8_t lockdc=0;
uint8_t chose=0;
uint32_t lasttick=0;
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
//	uint16_t flag=0;
	uint16_t adc_dc[2]={0}; 
  /* 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();
  MX_SPI1_Init();
  MX_ADC_Init();
  MX_TIM15_Init();
  /* USER CODE BEGIN 2 */
	LCD_Init();
	HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_2);

	drawWin();
	//findZeroPwm(pwm_dc);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		__HAL_TIM_SET_COMPARE(&htim15, TIM_CHANNEL_1, pwm_dc[0]);
		__HAL_TIM_SET_COMPARE(&htim15, TIM_CHANNEL_2, pwm_dc[1]);
		readADC(adc_dc);
		dispDC(adc_dc);
		//LCD_ShowIntNum(0,50,key,1,RED,BROWN,32);
		//HAL_Delay(100);	
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.HSI14CalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
  RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

void drawWin(void){
	LCD_Fill(0,0,120,240,BLACK);
	LCD_Fill(120,0,240,240,BLUE);
	LCD_ShowString(0,0,"DC2",WHITE,BLACK,24,0);
	LCD_ShowString(120,0,"DC1",WHITE,BLUE,24,0);
}

void readADC(uint16_t adc_dc[]){	
	uint8_t i=0,flag=1,readtimes=0;	
	uint16_t val0=0,val1=0;
	adc_dc[0]=0;
	adc_dc[1]=0;
	for(i=0;i<10;i++){
		flag=1;
		HAL_ADC_Start(&hadc);
		HAL_ADC_PollForConversion(&hadc,0x30);
		if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc),HAL_ADC_STATE_REG_EOC)){
			val0=HAL_ADC_GetValue(&hadc);
		}else{
			flag=0;
		}
		HAL_ADC_Start(&hadc);
		HAL_ADC_PollForConversion(&hadc,0x30);
		if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc),HAL_ADC_STATE_REG_EOC)){
			val1=HAL_ADC_GetValue(&hadc);
		}else{
			flag=0;
		}
		if(flag==1){
			adc_dc[0]+=val0;
			adc_dc[1]+=val1;
			readtimes++;
		}		
		HAL_ADC_Stop(&hadc);		
  }
	//printf("%d\t%d\t%d\n",readtimes,adc_dc[0],adc_dc[1]);ֵ
  adc_dc[0]=adc_dc[0]/readtimes;
  adc_dc[1]=adc_dc[1]/readtimes;
	
}


void dispDC(uint16_t adc_dc[]){
	uint8_t y=6,x1=100,x2=220,x3=48,x4=168;	
	if(chose){
		LCD_Fill(x2,y,x2+15,y+15,GREEN);
		LCD_Fill(x1,y,x1+15,y+15,BLACK);
	}else{
		LCD_Fill(x2,y,x2+15,y+15,BLUE);
		LCD_Fill(x1,y,x1+15,y+15,GREEN);
	}
	if(lockdc){
		LCD_ShowString(x3,y,"diff",WHITE,BLACK,16,0);
		LCD_ShowString(x4,y,"diff",RED,BLUE,16,0);
	}else{
		LCD_ShowString(x3,y,"    ",BLACK,BLACK,16,0);
		LCD_ShowString(x4,y,"    ",BLUE,BLUE,16,0);
	}
	

	float dc1,dc2;
	char strdc[10]={0};
	dc1=RESVAL*adc_dc[0]*3.3f/4096;
	dc2=RESVAL*adc_dc[1]*3.3f/4096;
	//printf("[%d , %d , %1.4f]\t[%d , %d , %1.4f]\n",pwm_dc[0],adc_dc[0],dc1,pwm_dc[1],adc_dc[1],dc2);
	if(pwm_dc[0]<PWMZERO){
		sprintf(strdc,"%3.2fV ",(float)(PWMZERO-pwm_dc[0])*PWMSETUP);								 // @suppress("Float formatting support")
		LCD_ShowString(5,150,strdc,WHITE,BLACK,32,0); //DC1
	}else{
		sprintf(strdc,"%3.2fV ",(float)(PWMZERO-pwm_dc[0])*PWMSETUP);
		//sprintf(strdc,"%3.2f V",dc1);								 // @suppress("Float formatting support")
		LCD_ShowString(5,150,strdc,WHITE,BLACK,32,0);			//DC1
	}
	if(pwm_dc[1]<PWMZERO){
		sprintf(strdc,"%3.2fV ",(float)(PWMZERO-pwm_dc[1])*PWMSETUP);								 // @suppress("Float formatting support")
		LCD_ShowString(125,150,strdc,RED,BLUE,32,0);		//DC1
	}else{
		sprintf(strdc,"%3.2fV ",(float)(PWMZERO-pwm_dc[1])*PWMSETUP);
		//sprintf(strdc,"%3.2f V",dc2); // @suppress("Float formatting support")
		LCD_ShowString(125,150,strdc,RED,BLUE,32,0);//DC2
	}
	/*
	sprintf(strdc,"%04d",adc_dc[0]);
	LCD_ShowString(0,85,strdc,GREY,BLACK,32,0);			//DC1
	sprintf(strdc,"%04d",adc_dc[1]);
	LCD_ShowString(120,85,strdc,RED,BLUE,32,0);//DC2
    */
	sprintf(strdc,"%03dpwm",pwm_dc[0]);
	LCD_ShowString(0,50,strdc,GRAY,BLACK,16,0);			//DC1
	sprintf(strdc,"%03dpwm",pwm_dc[1]);
	LCD_ShowString(120,50,strdc,RED,BLUE,16,0);//DC2
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
	if(HAL_GetTick()-lasttick<=15){
		return ;
	}else{
		lasttick=HAL_GetTick();
	}
	if(GPIO_Pin == KEY_1_Pin){/* KEY 1  */
		if(lockdc==0){
			lockdc=1;
			pwm_dc[0]=PWMZERO;
			pwm_dc[1]=PWMZERO;
		}
	}
	if(GPIO_Pin == KEY_2_Pin){/* KEY 1  */
		if(lockdc==1){
			lockdc=0;
			pwm_dc[0]=PWMZERO;
			pwm_dc[1]=PWMZERO;
		}
	} 
  if(GPIO_Pin == KEY_R_Pin){/* KEY L */ 
		if(lockdc==0){
			pwm_dc[chose]+=PWMSTEP;
		}else{
			pwm_dc[chose]+=PWMSTEP;
			pwm_dc[(chose+1)%2]=PWMZERO+PWMZERO-pwm_dc[chose];
		}
		if(pwm_dc[0]>PWMMAX) pwm_dc[0]=PWMMAX;
		if(pwm_dc[0]<PWMMIN) pwm_dc[0]=PWMMIN;
		if(pwm_dc[1]>PWMMAX) pwm_dc[1]=PWMMAX;
		if(pwm_dc[1]<PWMMIN) pwm_dc[1]=PWMMIN;	
  } 
  if(GPIO_Pin == KEY_O_Pin){/* KEY O */
  	chose=(chose+1)%2;
  }
  if(GPIO_Pin == KEY_L_Pin){/* KEY R */ 
  	if(lockdc==0){
  		pwm_dc[chose]-=PWMSTEP;
  	}else{
  		pwm_dc[chose]-=PWMSTEP;
		pwm_dc[(chose+1)%2]=PWMZERO+PWMZERO-pwm_dc[chose];
  	}
  	if(pwm_dc[0]>PWMMAX) pwm_dc[0]=PWMMAX;
		if(pwm_dc[0]<PWMMIN) pwm_dc[0]=PWMMIN;
		if(pwm_dc[1]>PWMMAX) pwm_dc[1]=PWMMAX;
		if(pwm_dc[1]<PWMMIN) pwm_dc[1]=PWMMIN;	
  } 
   
}
/* 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 */
  __disable_irq();
  while (1)
  {
  }
  /* 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,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif

 

相应参数设置:

#define PWMSTEP 10
#define RESVAL 1.524f			ֵ
#define PWMMIN 210
#define PWMMAX 850
#define PWMZERO 530
#define PWMSETUP 0.0129f

 

效果演示:(存在+40mV左右的误差)

跟随模式DC2_0.77V输出:

Fu5KFkAaaBVh7OsLCOb2U7g74FgM

跟随模式DC1_-0.77V输出:

Fhoo4teG4FMfwM34PLCiqsgr8TfN

正常DC1_4V输出:

FlP5kf2Bb1FcKBydlIfNLSv_PU3Z

正常DC1_3V输出:

FuMC5N9T9AvvrCsYijNGjxq-PhAx

正常DC2_-1.68V输出

FlTJvviJEIYfuYCXbKnplSSeYPuh

 

附件下载
project.hex
固件
stm32DC.rar
工程文件
团队介绍
哈尔滨工业大学电信学院大三学生
团队成员
lxb
哈尔滨工业大学电信学院信息对抗专业
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号