最近,在做课程设计了一款数字恒流源电路,其要求如下:
a)利用DA转换芯片输出200-1000mA电流信号
b)可设置并显示输出电流给定值
c)系统工作符合一般恒流源要求
d)可步进输出电流,步进值10mA
e)保护电路
一、设计原理 (硬件部分)
通过STM32F103C8T6驱动MCP4725模块,产生模拟电压,模拟电压输入由LM324和S8050三极管构成的压控型恒流源,再通过采样电阻与恒流源电路串联进行差分运放电压采样,实现对电流的检测。将电流的检测结果送入STM32F103C8T6,进行PID运算,实现200-1000mA恒流源控制。
其Multism电路图如下:
压控恒流:如原理图,输入的电压为DAC4725输出的模拟电压Vin,该恒流源系统由LM324AD和三极管以及外部电阻构成,三极管采用8050能够实现集电极最大电流1.5A输出。图中RL2为负载电阻,R2为采样电阻,流过负载电阻的电为Vin/R2,LM324采用负反馈电路从而工作在线性区,由此有Vin=Up=Un,则R2的电流为Vin/R2。三极管集电极电流近似等于发射极电流,当流过的电流增大,R2的电流也会随之增大,进而电压增大,导致Un增大,(Up-Un)减小,从而运放输出减小,三极管电流减小,负载电流也会减小,R2电流随之减小,正是因为负反馈从而达到恒流目的。
差分运放:如图,电阻R1,R5分别接入到正向输入端和反向输入端,采集的电压信号分别从两端输入,输出端接入R6到反向输入端,同时正向输入端与地之间接入平衡电阻R8,其中R1=R5,反馈电阻为平衡电阻R8,从而构成对称电路。运放输出端为R6/(R8*(V1-V2
绘制原理框图,描述各功能单元之间的逻辑关系
1.软件部分
软件部分的代码使用的HAL 库编写的,原理如下:用iic驱动mcp4725模块,输出模拟电压,然后用c8t6自带的DA采集,采集电压,通过位置式pid计算,实现横流控制,
这里用的是HAL库,iic1和iic2分别驱动mcp4725芯片和oled屏幕;这里的mcp4725是一款集成的芯片,其实就是一个DA转换器,它用单片机输出的数字电压,产生一个模拟电压
这里是所需要的引脚配置,包括两个和IIC和一个串口(用于调试);KEY-1和KEY-2是外部中断,用来调节输出的电压 ADC1是用电电压采集的,目的是形成负反馈,pid闭环控制]
这里是两个IIC的配置,就是常规配置 标准模式100000HZ]
[这里是ADC 单通道 我选择的是IN0 加dma方式](
这里是两个按键的外部中断,目的是用于调节电压,控制输出电流
这里一定要注意打开外部中断](
这里是实物图 其中pid需要自己调试
所有的工程文件在这,包括multisim仿真和代码
链接:https://pan.baidu.com/s/18zVFmSFzKUN2d6Jb-nQy8g?pwd=7777
提取码:7777
–来自百度网盘超级会员V4的分享
代码如下(示例):
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C2_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
OLED_Init();
OLED_Clear();
PID_init();
HAL_ADCEx_Calibration_Start(&hadc1); // f1系列需要ADC校准,f4不需要
HAL_ADC_Start_DMA(&hadc1,(uint32_t )&ADC_DMA_Value,ADC_Channel_MAX); // 启动ADC的DMA转换
/ USER CODE END 2 /
/ Infinite loop /
/ USER CODE BEGIN WHILE /
while (1)
{
/ USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// OLED_FullyClear ();
sprintf(i1_1, "Set i:%.1f mA", i1);
sprintf(i2_2, "Acutal i:%.1f mA", i2);
sprintf(i3_3, "Acutal V:%.1f V", i3);
OLED_ShowString(4,2.,i1_1,1);//这个是oled驱动里面的,是显示位置的一个函数,
OLED_ShowString(4,4,i2_2,1);//这个是oled驱动里面的,是显示位置的一个函数,
OLED_ShowString(4,8,i3_3,1);//这个是oled驱动里面的,是显示位置的一个函数
ADC_Value = ADC_DMA_Value[0];
//printf("实际电压%0.1f \n",ADC_Value*5.0/4096.0);//测试时,串口调试
DAC_Value = DAC_Value + PID_realize((i1/1000)*RL, ADC_Value*5.0/4096.0);
DAC_Value2 = DAC_Value*1.5;//硬件山的设计,采样的电压和实际的电压为1.5倍的关系
i3=ADC_Value*5.0/4096.0;
// /软件保护电路,如果计算出的输出电压过大,就关闭输出*******/
// if( DAC_Value2 >5.0)
// { DAC_Value2 =0;
// }
i2= (( ADC_Value*5.0/4096.0)*1000)/RL;//实际采集的电流
/*************************** //mcp4725的输出函数,当为5v接入时输出关系为 输入*1.2,当接入3.3v倍数为0.8 输入的单位为mV
比如输入3000 ,接入5v就输出3000*1.2=3600mV ;; ************************************/
// MCP4725_Out(&hi2c1,( DAC_Value2/1.2)*1000,1);
MCP4725_Out(&hi2c1,3000,1);
HAL_Delay(50);
//printf(“输出电压%0.1d \n”, DAC_Value);
}
/* USER CODE END 3 */
}
## 2.读入数据
代码如下(示例):
#include "pid.h"
#include "stdio.h"
extern float i1 ;
pid_p pid;
//pid位置式
void PID_init()
{
// printf("PID_init begin \n");
pid.SetVoltage= 0.0; // 设定的预期电压值
pid.ActualVoltage= 0.0; // adc实际电压值
pid.err= 0.0; // 当前次实际与理想的偏差
pid.err_last=0.0; // 上一次的偏差
pid.voltage= 0.0; // 控制电压值
pid.integral= 0.0; // 积分值
pid.Kp= 0.5; // 比例系数
pid.Ki= 0.0; // 积分系数
pid.Kd= 0.0; // 微分系数
// printf("PID_init end \n");
}
float PID_realize( float v, float v_r)
{
printf("设定值%0.1f \n", i1);
pid.SetVoltage = v; // 固定电压值传入
pid.ActualVoltage = v_r; // 实际电压传入 = ADC_Value * 3.3f/ 4096
pid.err = pid.SetVoltage - pid.ActualVoltage; //计算偏差
pid.integral += pid.err; //积分求和
pid.result = pid.Kp * pid.err + pid.Ki * pid.integral + pid.Kd * ( pid.err - pid.err_last);//位置式公式
pid.err_last = pid.err; //留住上一次误差
//pid.ActualVoltage=pid.result*1.0; //实际角度应该由角度传感器+ADC返回
return pid.result;
}