
电流电压表
简介
采用CW芯片,使用ADC和spi,实现电流电压的显示输出。
简介:采用CW芯片,使用ADC和spi,实现电流电压的显示输出。开源协议
:GPL 3.0
(未经作者授权,禁止转载)描述
项目简介
本项目是基于CW32的电流电压表。
项目功能
本设计是基于CW32单片机设计的电流电压表检测;采用0.96寸OLED屏幕显示检测的电流电压;采用分压电路扩大检测的电压电流范围;按键切换模式;点灯科技用于指示状态。
项目参数
- 本设计采用CW32主控芯片,带有内部基准,ADC采集准确;
- 本设计采用0.96寸OLED显示,实时刷新显示电流电压;
- 采用分压电路,扩大测量范围;
原理解析(硬件说明)
本项目由以下部分组成,电源部分、LED指示、主控部分、ADC采集、OLED显示部分,本项目主要是通过ADC采集后并进行处理,显示在OELD屏幕上。
1--主控电路:
采用CW32芯片,引出串口,方便调试
2--电源电路:
采用TYPE-C-16P接口作为供电接口,加入了防反接。
3--OLED显示电路:
使用0.96寸OLED显示屏,教程多,方便显示移植。
4--ADC采集电路
采用分压电路,扩大范围。
5--基准电路
提供基准,使得测量更准。
6--校准电路
因测量电路存在误差,采用校准电路,后面可以进行校准。
软件代码
1--使用了RT-Thread 操作系统,移植了极小内核版本,对接了finsh。
void rt_hw_console_output(const char *str)
{
rt_size_t i = 0, size = 0;
size= rt_strlen(str);
for(i=0 ; i < size ;i++)
{
if (*str == '\n')
{
USART_ClearFlag(CW_UART1, USART_FLAG_TC);
USART_SendData_8bit(CW_UART1, '\r');
while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TC)== RESET);
}
USART_ClearFlag(CW_UART1, USART_FLAG_TC);
USART_SendData_8bit(CW_UART1, *(str++));
while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TC)== RESET);
}
}
int rt_hw_console_getchar(void)
{
int ch = -1;
while (USART_GetFlagStatus(CW_UART1, USART_FLAG_RC) == RESET);
USART_ClearFlag(CW_UART1, USART_FLAG_RC);
if (USART_GetFlagStatus(CW_UART1, USART_FLAG_PE | USART_FLAG_FE))
{
USART_ClearFlag(CW_UART1, USART_FLAG_PE | USART_FLAG_FE);
rt_thread_mdelay(10);
}
else
{
ch= USART_ReceiveData_8bit(CW_UART1);
}
return ch;
}
2--OELD显示
采用了硬件SPI,也移植U8G2,移植OLED可以参考立创移植手册。
uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,
void *arg_ptr)
{
uint8_t *data = (uint8_t *)arg_ptr;
switch (msg)
{
case U8X8_MSG_BYTE_SEND:
{
for(int i = 0; i < arg_int; i++)
spi_read_write_byte((u8)*(data + i));
break;
}
case U8X8_MSG_BYTE_INIT:
break;
case U8X8_MSG_BYTE_SET_DC:
if ( arg_int ) GPIO_WritePin(OLED_GPIO_PORT,OLED_DC_PIN, GPIO_Pin_SET);
else GPIO_WritePin(OLED_GPIO_PORT,OLED_DC_PIN, GPIO_Pin_RESET);
break;
case U8X8_MSG_BYTE_START_TRANSFER:
GPIO_WritePin(OLED_GPIO_PORT,OLED_CS_PIN, GPIO_Pin_RESET);
break;
case U8X8_MSG_BYTE_END_TRANSFER:
GPIO_WritePin(OLED_GPIO_PORT,OLED_CS_PIN, GPIO_Pin_SET);
break;
default:
return 0;
}
return 1;
}
3--ADC采集
ADC采集后,进行滤波处理后输出。
void ADC_init(void)
{
ADC_InitTypeDef ADC_InitStructure; //ADC配置结构体
ADC_SerialChTypeDef ADC_SerialChStructure; //ADC序列通道结构体
GPIO_InitTypeDef GPIO_Init_Struct;
__RCC_GPIOB_CLK_ENABLE(); //打开ADC对应引脚时钟
__RCC_ADC_CLK_ENABLE(); // 打开ADC时钟
PB00_ANALOG_ENABLE(); //使能模拟引脚
PB01_ANALOG_ENABLE(); //使能模拟引脚
PB10_ANALOG_ENABLE(); //使能模拟引脚
PB11_ANALOG_ENABLE(); //使能模拟引脚
ADC_StructInit(&ADC_InitStructure); // ADC默认值初始化
ADC_InitStructure.ADC_ClkDiv = ADC_Clk_Div128; //ADC工作时钟配置 PCLK/4 = 6/4 = 1.5Mhz
// ADC_SerialChStructure.ADC_InitStruct.ADC_AccEn = ADC_AccDisable; //转换结果累加不使能
// ADC_SerialChStructure.ADC_InitStruct.ADC_Align = ADC_AlignLeft; //ADC转换结果右对齐
// ADC_SerialChStructure.ADC_InitStruct.ADC_DMAEn = ADC_DmaDisable; //关闭DMA传输
// ADC_SerialChStructure.ADC_InitStruct.ADC_SampleTime = ADC_SampTime5Clk; //5个ADC时钟周期
// ADC_SerialChStructure.ADC_InitStruct.ADC_InBufEn = ADC_BufEnable; //开启跟随器
// ADC_SerialChStructure.ADC_InitStruct.ADC_TsEn = ADC_TsEnable; //内置温度传感器禁用
// ADC_SerialChStructure.ADC_InitStruct.ADC_OpMode = ADC_SerialChScanMode; //序列扫描模式:4个通道同时转换一次
/*信号电压较低时,可以降低参考电压来提高分辨率。 改变参考电压后,同样二进制表示的电压值就会不一样,
最大的二进制(全1)表示的就是你的参考电压,在计算实际电压时,就需要将参考电压考虑进去。*/
ADC_InitStructure.ADC_VrefSel = ADC_Vref_BGR1p5; //参考电压设置为1.5V
ADC_InitStructure.ADC_SampleTime = ADC_SampTime10Clk; //由于电压信号为慢速信号,ADC采样时间为十个ADC采样周期以确保准确
ADC_SerialChStructure.ADC_Sqr0Chmux = ADC_SqrCh8; //通道4输入PB00
ADC_SerialChStructure.ADC_Sqr1Chmux = ADC_SqrCh9; //通道13输入
ADC_SerialChStructure.ADC_Sqr2Chmux = ADC_SqrCh11; //通道11输入
ADC_SerialChStructure.ADC_Sqr3Chmux = ADC_SqrCh12; //通道12输入
ADC_SerialChStructure.ADC_SqrEns = ADC_SqrEns03; //Sqr为序列配置寄存器,这里只用到了序列0的通道,所以配置成0表示只转换Sqr0序列
ADC_SerialChStructure.ADC_InitStruct = ADC_InitStructure; //ADC初始化
ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure); //ADC序列连续转换模式配置
ADC_ClrAccResult();
ADC_ClearITPendingAll(); //清除ADC所有中断状态
ADC_Enable(); // ADC使能
ADC_SoftwareStartConvCmd(ENABLE); //ADC转换软件启动命令
}
uint32_t Mean_Value_Filter(uint16_t *value, uint32_t size) //均值滤波
{
uint32_t sum = 0;
uint16_t max = 0;
uint16_t min = 0xffff;
int i;
for(i = 0; i < size; i++) //遍历数组找到最大值和最小值
{
sum += value[i];
if(value[i] > max)
{
max = value[i];
}
if(value[i] < min)
{
min = value[i];
}
}
sum -= max + min; //减去最大和最小值后求平均
sum = sum / (size - 2);
return sum;
}
4--保存标定
进行标定后,把相应值锁存在flash,防止重新上电后值改变,这里电压和电流的K值用了函数。写入时注意先擦除再写入,否则写入错误
void flash_erase(void)
{
uint8_t Flag;
// erase
FLASH_UnlockPages(START_ADDR, END_ADDR); // 解锁最后一个页面
Flag = FLASH_ErasePages(START_ADDR, END_ADDR); // 擦除最后一个页面
FLASH_LockAllPages();
}
void flash_write(uint16_t offset,uint16_t *data,uint16_t lenght)
{
uint8_t Flag;
// write
FLASH_UnlockPages(START_ADDR, END_ADDR); // 解锁最后一个页面
Flag = FLASH_WirteHalfWords(START_ADDR+offset*2, data, lenght);
FLASH_LockAllPages();
}
void ComputeKV()
{
flash_read(0,k_data,4);
flash_erase();
k_data[0]=cal_adc.span_v[1]-cal_adc.span_v[0];
k_data[0]= (k_data[0]/(V_15-V_05));//电压
k_data[2] = 8;
flash_write(0,k_data,4);
}
void ComputeKI()
{
flash_read(0,k_data,4);
flash_erase();
k_data[1]=cal_adc.span_m[1]-cal_adc.span_m[0];
k_data[1]= (k_data[1]/(M_15-M_05));//电流
k_data[3]=8;
flash_write(0,k_data,4);
}
功能说明
1.主界面显示的电流测量范围是03A,电压范围是03V,VMAX的范围是0~30V,与硬件电路的测量测量范围一一对一;
2.按键说明:
(1)K1主界面切换;
(2)K2点击界面显示标定模式,进入标定;
(3)K3电流/电压标定,点击切换;
(4)K4第一次点击进入标定第一个点,再次点击,记录标定值,结束;
(5)K4第一次点击进入标定第二个点,再次点击,记录标定值,结束;
(6)两个点标定后,点击K6,计算出K值,标定结束;
(7)每个按键对应一个指示灯。
实物展示
测量5V电压,3V电路显示3V,满量程,30V量程显示有误差为5.1V
后续优化
- OLED设计成多级界面;
- 使用按键进行切换(留有6个按键,无需更改硬件);
附件清单
1.CWF030是源码;
2.C5138758是OED屏幕规格书,里面有驱动流程;
3.PCtoLCD2002是常用的取模软件。
设计图

BOM


评论