系统构成如下:
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 制作双通道可调直流电压
-
通过STM32F072的内部定时器产生PWM,进而生成可调的直流电压,输出电压的变化范围为-4V到+4V
-
双路直流电压可以设置为独立模式调节,也可以设置成为跟踪模式调节,也就是DC1调节为2.5V的时候,DC2自动为-2.5V,当DC1调节为1.8V的时候,DC2自动为-1.8V
-
在LCD屏幕上显示两路DC当前的值,以及调节菜单
这里首先感谢strongerHuang微信公众号,里面有stm32f0系列的较为完整的教程。(CSDN相关博客汇总:https://blog.csdn.net/ybhuangfugui/category_6247312.html?spm=1001.2014.3001.5482)
首先是cubeMX配置相应stm32管脚:
GPIO配置:
ADC配置:
TIM15配置:
SPI配置:
时钟配置(按照第一次直播时做的):
其它配置:
工程IDE用的是STM32CubeIDE
都是HAL库,直播提到可以使用LL库与DMA会更快更有效率,但奈何我不会,以后会慢慢学的。
之后生成代码即可,主体就有了。
记得配置这个:
其实其中某些配置可以在CubeMX中完成,以下文章仅供参考:
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屏(但我个人做的其实并不好):
以上是完成该项目时参考的部分博客,如要完成其它项目以上是不够的。
补充一下项目的原理:
简单来说就是将波形图上平直的直流电压线,斩波成一段一段的矩形波,即通过改变PWM波的占空比和频率,以实现平均电压小于原来的直流电压。
上原理图:
通过图可以看出想达到+-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输出:
跟随模式DC1_-0.77V输出:
正常DC1_4V输出:
正常DC1_3V输出:
正常DC2_-1.68V输出