这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 利用STM32F103C8T6的PWM输出制作四路降压模块

共2条 1/1 1 跳转至

利用STM32F103C8T6的PWM输出制作四路降压模块

助工
2024-09-18 15:02:45     打赏

我在工作中会用到5V12V等常见的电压输出,由于使用开关电源做输出比较麻烦,所以设置一个四路的降压模块,现在将其分享出来给大家参考一下。

降压电路部分采用XL4015左右电压芯片,选用该芯片的考虑点是宽电压输入 8V-36V范围,这个范围基本上在工作中比较容易找到,效率也比较高,具体参数如下图:

图片1.png

设计的电路图如下:

图片2.png

由于单片机采用3.3V工作电压,所以固定一路输出为5V输出,该输出电压也可以为其他负载提供电压输出,在PCB布局时,将5V3.3V输出引出外部提供电压输出。

二:屏幕显示使用的IIC接口的OLED屏幕:资料如下:

采用i2C接口的1.3寸的12864 OLED模块显示,采用该屏幕主要是接线方便,对屏幕的刷新速度要求不高,只是用作显示电压使用;缺点是单色的,看起来并不是很友好。

图片3.png


软件控制部分:硬件设计资料:采用STM32的定时器4PWM输出功能,为了使输出的电压恒定,输出电压经过了两次RC电路,然后 进入运放LM358,这个运放工作时,不需要负电压就可以,而且价格便宜,图纸如下

图片4.png

电压采集部分:将XL4015输出端的电压经过电阻分压进入单片机的ADC采样口,设置分压电路时,需要注意下,输出电压最大时,不能超过单片IO口最高的承受电压的能力。这里需要将输出电压进入单片机内部进行采集,用于检测PWM输出是否正常。

图片5.png

实物测试图片如下:

实物测试图片如下:

主要软件代码如下:

利用ADC检测电压输出是否正常:

//单通道 DMA adc数据
volatile u16 adc_values;
volatile u16 adc_buf[400];

//-------------单通道 ADC读取----------------
void Adc_Init(void) {
    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72/6 = 12MHz
    //ADC1通道11的转换时间为T=(239.5+12.5) x 1/12=21us。
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure); 
    ADC_DeInit(ADC1);//复位ADC1
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC 独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单次扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//单次转换模式
    ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;//ADC1 和ADC2 工作在同步规则模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1 ;//进行规则转换的ADC通道数量
    ADC_Init(ADC1, &ADC_InitStructure);//初始化ADC1
    ADC_Cmd(ADC1, ENABLE);
    ADC_ResetCalibration(ADC1);//开启复位校准
    while(ADC_GetResetCalibrationStatus(ADC1));//等待复位校准成功
    ADC_StartCalibration(ADC1);//开始校验ADC1
    while(ADC_GetCalibrationStatus(ADC1));//等待校验成功
}
u16 get_ADC_Values(u8 ch, u8 rank) {
 
    //设置指定ADC规则通道,设置转换时间
    ADC_RegularChannelConfig(ADC1, ch, rank, ADC_SampleTime_239Cycles5);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); //开启软件转换
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); //等待转换结束
    return 	ADC_GetConversionValue(ADC1);
}
//返回ADC平均值
u16 get_ADC_Average(u8 ch, u8 rank, u8 count) {
    u32 temp_val = 0;
    u8 i;
 
    for(i = 0; i < count; i++) {
        temp_val += get_ADC_Values(ch, rank);
        delay_ms(5);
    }
    return temp_val / count; 
}
//-------------单通道 ADC DMA读取----------------
void ADC_DMA_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
	
    //使能DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);      
  
		//初始化IO 
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;        
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;  
    GPIO_Init(GPIOA,&GPIO_InitStructure);  
 
    //复位DMA1 通道1 ADC
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;//ADC内存基地址
    DMA_InitStructure.DMA_MemoryBaseAddr  = (u32)&adc_values;//变量基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设到内存
    DMA_InitStructure.DMA_BufferSize = 1;//数据宽度
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设内存地址不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;//变量内存地址不变
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//16字节 12位ADC
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//16字节
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//循环模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//关闭内存到内存传输
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA1 通道1
    DMA_Cmd(DMA1_Channel1, ENABLE);//使能DMA 通道1
 
    ADC_DeInit(ADC1);//复位ADC1
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE; //关闭扫描
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //开启连续转换 触发一次 持续转换
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发转换
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1;//转换一个通道
    ADC_Init(ADC1, &ADC_InitStructure);//初始化ADC
		//ADC时钟 不能大于14MHz
		RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72/6 = 12MHz 
		//设置指定ADC规则通道,设置转换时间
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
    //使能ADC DMA
		ADC_DMACmd(ADC1, ENABLE);
		//使能ADC
    ADC_Cmd(ADC1, ENABLE);
		
		ADC_ResetCalibration(ADC1);//复位校准寄存器  
    while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器复位完成  
  
    ADC_StartCalibration(ADC1);//ADC校准  
    while(ADC_GetCalibrationStatus(ADC1));//等待校准完成  
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//由于没有采用外部触发,所以使用软件触发ADC转换 
		
}
//返回ADC平均值
u16 get_ADC_DMA_Average(u8 count) {
    u32 temp_val = 0;
    u8 i;
 
    for(i = 0; i < count; i++) {
        temp_val += adc_values;
        delay_ms(5);
    }
    return temp_val / count;
 
}
//-------------多通道 ADC DMA读取----------------
void ADC_DMA_Multichannel_Init(uint16_t *ADC_Buff) {
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
	
    //使能DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);      
  
		//初始化IO 
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;        
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;  
    GPIO_Init(GPIOA,&GPIO_InitStructure);  
 
    //复位DMA1 通道1 ADC
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;//ADC内存基地址
    DMA_InitStructure.DMA_MemoryBaseAddr  = (u32)ADC_Buff;//变量基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设到内存
    DMA_InitStructure.DMA_BufferSize = 400;//数据宽度
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设内存地址不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//变量内存地址增长
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//16字节 12位ADC
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//16字节
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//循环模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//关闭内存到内存传输
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA1 通道1
    DMA_Cmd(DMA1_Channel1, ENABLE);//使能DMA 通道1
 
    ADC_DeInit(ADC1);//复位ADC1
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE; //开启扫描 多通道读取 
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //开启连续转换 触发一次 持续转换
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发转换
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 4;//转换3个通道
    ADC_Init(ADC1, &ADC_InitStructure);//初始化ADC
		
  //ADC时钟 不能大于14MHz
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72/6 = 12MHz 
  //设置指定ADC规则通道,设置转换时间
   ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_13Cycles5);
   ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_13Cycles5);
   ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_13Cycles5);
   ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_13Cycles5);
    //使能ADC DMA
		ADC_DMACmd(ADC1, ENABLE);
	//使能ADC
    ADC_Cmd(ADC1, ENABLE);
		
		ADC_ResetCalibration(ADC1);//复位校准寄存器  
    while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器复位完成  
  
    ADC_StartCalibration(ADC1);//ADC校准  
    while(ADC_GetCalibrationStatus(ADC1));//等待校准完成  
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//由于没有采用外部触发,所以使用软件触发ADC转换 
		
}

PWM输出控制:

//TIM4 CH1 PWM 输出设置
//PWM 输出初始化
//arr:自动重装值
//psc:时钟预分频数
void Tim4_PWM_Init(u16 arr,u16 psc)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能 TIMx 外设
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 PB 时钟
	TIM_DeInit(TIM4);
 
  //设置该引脚为复用输出功能,输出 TIM4 CH1 的 PWM 脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //TIM4_CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用功能输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIO
 
	TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载周期值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值 不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //初始化 TIMx
	
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //CH1 PWM2 模式
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
	
	
	TIM_OCInitStructure.TIM_Pulse = 1800; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //OC1 高电平有效
	TIM_OC1Init(TIM4, &TIM_OCInitStructure); //根据指定的参数初始化外设 TIMx
	TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); //CH1 预装载使能
	
	
	TIM_OCInitStructure.TIM_Pulse = 1800; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //OC2 高电平有效
	TIM_OC2Init(TIM4, &TIM_OCInitStructure); //根据指定的参数初始化外设 TIMx
	TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable); //CH2 预装载使能
	
	
	TIM_OCInitStructure.TIM_Pulse = 1800; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //OC3 高电平有效
	TIM_OC3Init(TIM4, &TIM_OCInitStructure); //根据指定的参数初始化外设 TIMx
	TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable); //CH3 预装载使能
	
	
	TIM_OCInitStructure.TIM_Pulse = 1800; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //OC4 高电平有效
	TIM_OC4Init(TIM4, &TIM_OCInitStructure); //根据指定的参数初始化外设 TIMx
	TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); //CH4 预装载使能
	
	
	TIM_ARRPreloadConfig(TIM4, ENABLE); //使能 TIMx 在 ARR 上的预装载寄存器
//	TIM_CtrlPWMOutputs(TIM4,ENABLE);  //MOE 主输出使能,高级定时器必须开启
	TIM_Cmd(TIM4, ENABLE); //使能 TIMx
}
void Set_Duty(uint8_t ch,uint16_t value)
{
	switch(ch)
	{
		case 1:
			TIM_SetCompare1(TIM4, value);
			break;
		case 2:
			TIM_SetCompare2(TIM4, value);
			break;
		case 3:
			TIM_SetCompare3(TIM4, value);
			break;
		case 4:
			TIM_SetCompare4(TIM4, value);
			break;
	}	
}   
void Auot_Vol(void)
{
	Set_Duty(3,(float)(Meun.CH2_Vol)*2.321+2207.35+7);
	Set_Duty(2,(float)(Meun.CH3_Vol)*2.321+2207.35+10);
	Set_Duty(1,(float)(Meun.CH4_Vol)*2.321+2207.35+7);
}

界面显示部分:

void MeunDisplay(void)
{
	char buffer[5];
	static char i=50;
	float adv[4]={0};
	int x;
	if(Meun.SecondM == 0 && Meun.FirstM == 0)
	{
		if(Meun.Refresh == 1)
		{
			OLED_Fill(0);//全屏填充
			Meun.Refresh = 0;
			Display_Refresh(0);
			for(x =0;x<100;x++)
			{
				adv[0] += adc_buf[0+x*4];
				adv[1] += adc_buf[1+x*4];
				adv[2] += adc_buf[2+x*4];
				adv[3] += adc_buf[3+x*4];
			}
			adv[0] = adv[0]/100;
			adv[1] = adv[1]/100;
			adv[2] = adv[2]/100;
			adv[3] = adv[3]/100;
			sprintf(buffer,"%4.1fV",(float)adv[3]/4096*3.3*2);
			OLED_ShowStr(24, 3, (uint8_t *)buffer, 1);
			sprintf(buffer,"%4.1fV",(float)adv[2]/4096*3.3*8.5);
			OLED_ShowStr(88, 3, (uint8_t *)buffer, 1);
			sprintf(buffer,"%4.1fV",(float)adv[1]/4096*3.3*8.5);
			OLED_ShowStr(24, 4, (uint8_t *)buffer, 1);
			sprintf(buffer,"%4.1fV",(float)adv[0]/4096*3.3*8.5);
			OLED_ShowStr(88, 4, (uint8_t *)buffer, 1);
			HoldReg[4] = Meun.CH2_Vol;
			HoldReg[5] = Meun.CH3_Vol;
			HoldReg[6] = Meun.CH4_Vol;
			USER_Save();//保存用户数据
		}
		
		if(i % 50 == 0)
		{
			for(x =0;x<100;x++)
			{
				adv[0] += adc_buf[0+x*4];
				adv[1] += adc_buf[1+x*4];
				adv[2] += adc_buf[2+x*4];
				adv[3] += adc_buf[3+x*4];
			}
			adv[0] = adv[0]/100;
			adv[1] = adv[1]/100;
			adv[2] = adv[2]/100;
			adv[3] = adv[3]/100;
			sprintf(buffer,"%4.1fV",(float)adv[3]/4096*3.3*2);
			OLED_ShowStr(24, 3, (uint8_t *)buffer, 1);
			HoldReg[0] = (float)adv[3]/4096*3.3*2*100;
			sprintf(buffer,"%4.1fV",(float)adv[2]/4096*3.3*8.5);
			OLED_ShowStr(88, 3, (uint8_t *)buffer, 1);
			HoldReg[1] = (float)adv[2]/4096*3.3*8.5*100;
			sprintf(buffer,"%4.1fV",(float)adv[1]/4096*3.3*8.5);
			OLED_ShowStr(24, 4, (uint8_t *)buffer, 1);
			HoldReg[2] = (float)adv[1]/4096*3.3*8.5*100;
			sprintf(buffer,"%4.1fV",(float)adv[0]/4096*3.3*8.5);
			OLED_ShowStr(88, 4, (uint8_t *)buffer, 1);
			HoldReg[3] = (float)adv[0]/4096*3.3*8.5*100;
			
			
			if(i == 100)
			{
				OLED_ShowStr(96, 6, (uint8_t *)"*", 2);
				i = 0;
			}
			else if(i == 50)
			{
				OLED_ShowStr(96, 6, (uint8_t *)"  ", 2);
			}
				
		}
		i ++;
	}
	else if(Meun.SecondM == 0)
	{
		if(Meun.Refresh == 1)
		{
			OLED_Fill(0);//全屏填充
			switch(Meun.FirstM)
			{
				case 1:
					Display_Refresh(1);
					break;
				case 2:
					Display_Refresh(2);
					break;
				case 3:
					Display_Refresh(3);
					break;
			}
			Meun.Refresh = 0;
		}
	}
	else
	{
		if(Meun.Refresh == 1)
		{
			OLED_Fill(0);//全屏填充
			Display_Refresh(4);
			Meun.Refresh = 0;
			EDIT_Flag = 1;
		}
		switch(Meun.FirstM)
		{
			case 1:
				sprintf(buffer,"%4.1fV",(float)Meun.CH2_Vol/10);
				OLED_ShowStr(40, 3, (uint8_t *)buffer, 2);
				break;
			case 2:
				sprintf(buffer,"%4.1fV",(float)Meun.CH3_Vol/10);
				OLED_ShowStr(40, 3, (uint8_t *)buffer, 2);
				break;
			case 3:
				sprintf(buffer,"%4.1fV",(float)Meun.CH4_Vol/10);
				OLED_ShowStr(40, 3, (uint8_t *)buffer, 2);
				break;
		}
	}
}

为了节约点成本,数据存储在单片机内部的flash,没有使用存储芯片。

在实际使用过程中发现电压输出并不恒定,用万用表测试可以明显的看出输出电压在0.2V上下跳动,但是这样并不影响使用,其中OUT1固定为5V输出,其他三路为可调电压输出。现在STM32价格貌似是处于下降的趋势,感兴趣的芯查查的小伙伴们可以在下方留下联系方式,方便自己焊接使用。






关键词: STM32F103C8T6     降压     PWM输出     AD    

专家
2024-09-18 19:44:10     打赏
2楼

高手出马,就是不一样!感觉挺实用的设计。


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]