今天和大家分享一下我们参加电赛的经历,分享一下经验。
本人是在队伍中担任队长同时负责软件部分也就是编写单片机代码(stm32),平时我们就练习仪器题如2019年的纸张计数装置等,但今年这类题:电感电容测量装置(C 题)需要使用IT公司的MSP430来完成,这对于一个连编译环境都没有的我来说,选这道题就等于以卵击石。所以就更换方向,找来找去就看到B题,也是个测量的题,准备下手。
但在网上找的方法都是通过计算出入射高频信号并采集反射信号的时间差,来推算出长度。但是大家也知道,没有FPGA这道题也白瞎。所以我们就做了一个让很多人都不看好的决定:把B题当成C来做,也就是把这个信号题当仪器题来做!
我们的思路就是把复杂的问题简单化:
1、测 量 长 度 ,就测量电缆的密度和质量
2、判断负载类型,就通过串联分压来判断(具体内容见下文)
3、 电 阻 ,就使用串联分压来测量
4、 电 容 ,就使用555多谐振荡,测量频率
5、 显 示 ,使用陶晶驰串口屏
这篇文章只和大家分享软件方面的经验,如果大家需要硬件方面的见解,欢迎大家参考博客
http://t.csdn.cn/IclgIhttp://t.csdn.cn/IclgI这篇文章是我们团队负责硬件的大佬所编,看后定会受益匪浅。
一、如何测量重量
我们已将长度与重量通过密度联系起来了,但是我们如何测量重量呢?
只要我们可以做出一个小型电子秤不就迎刃而解了吗。所以我们到网上看了很多重力传感器,最后我们定购了,两台5Kg量程的传感器,我们设计让它通过一个称重AD采集模数转化芯片HX711,再使用stm32读取实时的数据(通过更改商家提供的标准代码),通过公式计算出重量。代码里面包含两台设备的数据采集
具体使用方法在main中介绍
//这是称重的.C文件
#include "zhong.h"
/************************************************************************************
*************************************************************************************/
#include "delay.h"
u32 HX711_Buffer1,HX711_Buffer2;
u32 Weight_Maopi_1,Weight_Maopi_2;
s32 Weight_Shiwu_1,Weight_Shiwu_2;
u8 Flag_Error = 0;
//校准参数
//因为不同的传感器特性曲线不是很一致,因此,每一个传感器需要矫正这里这个参数才能使测量值很准确。
//当发现测试出来的重量偏大时,增加该数值。
//如果测试出来的重量偏小时,减小改数值。
//该值可以为小数
#define GapValue 106.5
void Init_HX711pin(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能PF端口时钟
//HX711_SCK
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //推挽输出
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
//HX711_DOUT
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 输入模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_4); //初始化设置为0
GPIO_SetBits(GPIOB,GPIO_Pin_6); //初始化设置为0
}
//****************************************************
//读取HX711
//****************************************************
u32 HX711_Read1(void) //增益128
{
unsigned long count;
unsigned char i;
HX711_DOUT_1=1;
delay_us(1);
HX711_SCK_1=0;
count=0;
while(HX711_DOUT_1);
for(i=0;i<24;i++)
{
HX711_SCK_1=1;
count=count<<1;
delay_us(1);
HX711_SCK_1=0;
if(HX711_DOUT_1)
count++;
delay_us(1);
}
HX711_SCK_1=1;
count=count^0x800000;//第25个脉冲下降沿来时,转换数据
delay_us(1);
HX711_SCK_1=0;
return(count);
}
u32 HX711_Read2(void) //增益128
{
unsigned long count;
unsigned char i;
HX711_DOUT_2=1;
delay_us(1);
HX711_SCK_2=0;
count=0;
while(HX711_DOUT_2);
for(i=0;i<24;i++)
{
HX711_SCK_2=1;
count=count<<1;
delay_us(1);
HX711_SCK_2=0;
if(HX711_DOUT_2)
count++;
delay_us(1);
}
HX711_SCK_2=1;
count=count^0x800000;//第25个脉冲下降沿来时,转换数据
delay_us(1);
HX711_SCK_2=0;
return(count);
}
//****************************************************
//获取毛皮重量
//****************************************************
void Get_Maopi_1(void)
{
Weight_Maopi_1 = HX711_Read1();
}
void Get_Maopi_2(void)
{
Weight_Maopi_2 = HX711_Read2();
}
//****************************************************
//称重
//****************************************************
void Get_Weight_1(void)
{
HX711_Buffer1 = HX711_Read1();
if(HX711_Buffer1 > Weight_Maopi_1)
{
Weight_Shiwu_1 = HX711_Buffer1;
Weight_Shiwu_1 = Weight_Shiwu_1 - Weight_Maopi_1; //获取实物的AD采样数值。
Weight_Shiwu_1 = (float)Weight_Shiwu_1/GapValue; //计算实物的实际重量
//因为不同的传感器特性曲线不一样,因此,每一个传感器需要矫正这里的GapValue这个除数。
//当发现测试出来的重量偏大时,增加该数值。
//如果测试出来的重量偏小时,减小改数值。
}
}
void Get_Weight_2(void)
{
HX711_Buffer2 = HX711_Read2();
if(HX711_Buffer2 > Weight_Maopi_2)
{
Weight_Shiwu_2 = HX711_Buffer2;
Weight_Shiwu_2 = Weight_Shiwu_2 - Weight_Maopi_2; //获取实物的AD采样数值。
Weight_Shiwu_2 = (float)Weight_Shiwu_2/110.5; //计算实物的实际重量
//因为不同的传感器特性曲线不一样,因此,每一个传感器需要矫正这里的GapValue这个除数。
//当发现测试出来的重量偏大时,增加该数值。
//如果测试出来的重量偏小时,减小改数值。
}
}
下面是称重的.h文件
#ifndef __ZHONG_H
#define __ZHONG_H
#include "sys.h"
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define HX711_SCK_1 PBout(4)// PB1白色
#define HX711_DOUT_1 PBin(5)// PB2白色
#define HX711_SCK_2 PBout(6)// PB0
#define HX711_DOUT_2 PBin(7)// PB1
extern void Init_HX711pin(void);
extern u32 HX711_Read1(void);
extern u32 HX711_Read2(void);
extern void Get_Maopi_2(void);
extern void Get_Maopi_1(void);
extern void Get_Weight_1(void);
extern void Get_Weight_2(void);
extern u32 HX711_Buffer;
extern u32 Weight_Maopi_1;
extern s32 Weight_Shiwu_1;
extern u32 Weight_Maopi_2;
extern s32 Weight_Shiwu_2;
extern u8 Flag_Error;
#endif
二、判断负载类型
我们使用的是串联分压来进行判断。
1、当负载是和R标等数量级的电阻R0时
我们通过ADC采集到的U1,U2是有明显的差距的(用Rb来代替R标,R0代替R负载,R电缆忽略不计)
可以发现U2/U1的取值范围是 1/3——3/5,可见他的U1到U2的变化还是很明显的。
2、当负载是电容时
我们知道电容隔直通交,所以此时电路中没有电流的产生,R标上没有压降,导致U1与 U2的数值相等或者说差距十分的的小
所以可以通过使用stm32的多通道ADC采集来实现本模块功能(具体使用方法见main)。
#include "myadc.h"
#include "stm32f4xx_adc.h"
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
void AD1_Init(void)
{
volatile uint32_t timeout = 10000;
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_84Cycles);
ADC_Cmd(ADC1, ENABLE);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_84Cycles);
ADC_Cmd(ADC1, ENABLE);
ADC_Init(ADC1, &ADC_InitStructure); // Initialize ADC to reset calibration
while (--timeout > 0);
}
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_84Cycles);
ADC_SoftwareStartConv(ADC1);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
#ifndef __MYADC_H
#define __MYADC_H
#include "sys.h"
void AD1_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);
#endif
三、测量电阻值
本方法依然是使用串联分压来实现,由上述判断出类型后,进入专门测量电阻的代码中。
此时的U1,U2都是测试好了的,R标也知道,所以很容易将R0测试出来。
四、测量电容值
当判断结果是电容时,我们会通过控制继电器断开串联分压电路,接通555多谐振荡电路,再利用输入捕获来采集到产生的方波的频率,最后再通过公式计算出我们需要的电容即可。
电路部分可以参考上述推荐的博客,本篇主要侧重于代码的分析。
1、输入捕获的代码
//输入捕获
#include "stm32f4xx.h"
#include "IntputCapture.h"
#include "stm32f4xx_tim.h"
//AutomaticReload:自动重装载值 PrioritySendCount:时钟预分频数
void TIM14_Init(u32 AutomaticReload,u32 PrioritySendCount)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE);//使能TIM14_CH1 1通道时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);// 使能GPIOF引脚
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14);//引脚复用PF9引脚复用为TIM14的通道1
//GPIO的初始化函数,设置初始化的模式为复用
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //设置GPIO模式为复用 对应上述引脚复用PF9引脚复用为TIM14的通道1
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
GPIO_Init(GPIOF,&GPIO_InitStructure);
//初始化TIM14定时器,设置预分频值和自动重装载值
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数,也就是递增计数
TIM_TimeBaseInitStructure.TIM_Period=AutomaticReload;//自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=PrioritySendCount;//时钟预分频值
TIM_TimeBaseInit(TIM14,&TIM_TimeBaseInitStructure);
//设置TIM14的PWM模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//输出极性低,也就意味着占空比中低电平有效
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//PWM调质模式1
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出比较使能
TIM_OCInitStructure.TIM_Pulse=0;
TIM_OC1Init(TIM14,&TIM_OCInitStructure);//初始化TIM14通道1
TIM_OC2PreloadConfig(TIM14,TIM_OCPreload_Enable);//使能TIM14在CCR2上的预装载寄存器
TIM_ARRPreloadConfig(TIM14,ENABLE);//使能自动重装载寄存器
TIM_Cmd(TIM14,ENABLE);//使能TIM14
}
//AutomationReload:自动重装值(TIM2,TIM5的自动重装载值是32位的) PrioritySendCount:时钟预分频数
void TIM5_CH1_InterCapture_Init(u32 AutomationReload,u16 PrioritySendCount)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//TIM5时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能GPIOA
GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5);//PA0复用为TIM5
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN; //引脚设置为下拉,因为PA0对应KEY_UP按键,KEY_UP按键左侧接V3.3
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);//GPIOA初始化
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInitStructure.TIM_Period=AutomationReload;
TIM_TimeBaseInitStructure.TIM_Prescaler=PrioritySendCount;
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;//映射通道1上
TIM_ICInitStructure.TIM_ICFilter=0x00;//不滤波
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//配置输入不分频
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//映射到TI1上,也就是TIM5通道1
TIM_ICInit(TIM5,&TIM_ICInitStructure);//初始化TIM5输入捕获
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//使能捕获和更新中断
NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;//TIM5通道
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能中断优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//响应优先级0
NVIC_Init(&NVIC_InitStructure);//初始化NVIC
TIM_Cmd(TIM5,ENABLE);//使能定时器5
}
//捕获状态位
//位7:0 还没成功捕获 1 成功捕获到一次
//位6:0 还没有捕获到低电平 1 成功捕获到一次低电平
//位5:0 捕获低电平后溢出的次数,当达到最高的溢出次数时,标记成功捕获一次
//这里需要注意,之所以设置低电平捕获状态和溢出次数,是因为初始化TIM5的时候设置的是上升沿捕获,那么就意味着初始化时上升沿就会进行一次捕获,想要获得高电平的持续时间,就要在中断中获得捕获低电平时的计数器值
u8 TIM5_CH1_CAPTURE_STA=0;//定义全局变量输入捕获的状态
u32 TIM5_CH1_CAPTURE_VAL;//输入捕获的值(注意TIM2/TIM5的输入捕获值是32位的)
void TIM5_IRQHandler(void)//两种情况 一种是溢出次数也就是循环了多少次,另一种是发生捕获也就是最后一次的时间,两个相加才是总的时间
//这个也比较好理解,因为我们不确定在我们捕获的低电平是第一次低电平,还是第n次低电平;
//如果只是单纯的记录低电平的计数器值,可能要比实际的高电平时间要小很多,因为中间掺杂着多个周期,我们都没有记录在内
{
if((TIM5_CH1_CAPTURE_STA&0x80)==0)//输入捕获状态位的最高位为0,还未成功捕获
{
if(TIM_GetITStatus(TIM5,TIM_IT_Update)!=RESET)//判断是否为更新中断,溢出
{
if(TIM5_CH1_CAPTURE_STA&0x40)//已经捕获到高电平了
{
if((TIM5_CH1_CAPTURE_STA&0x3F)==0x3F)//低6位为1,表示溢出的次数达到了最高,默认标记成功捕获一次
{
TIM5_CH1_CAPTURE_STA=TIM5_CH1_CAPTURE_STA|0X80;//状态位的最高位置1,表示已经成功捕获了一次
TIM5_CH1_CAPTURE_VAL=0xFFFFFFFF;//捕获值达到最高
}
else //不是因为溢出次数达到顶峰而标记捕获,则捕获状态++;
TIM5_CH1_CAPTURE_STA++;
}
}
if(TIM_GetITStatus(TIM5,TIM_IT_CC1)!=RESET)//捕获1发生捕获事件
{
if(TIM5_CH1_CAPTURE_STA&0x40)//捕获一个下降沿,一次捕获已经结束了,我需要做以下几件事:
//标记状态位的最高位为1,表明成功捕获一次,将捕获到的值给到全局变量VAL,
//因为要捕获高电平的频率,所以先设置上升沿捕获,在设置下降沿捕获,这样一来,一个上升沿一个下降沿就会得到一个完整的高电平频率
{
TIM5_CH1_CAPTURE_STA|=0x80;//状态最高位置1,表示成功捕获一次
TIM5_CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);//获取捕获值给到全局变量
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);//设置上升沿捕获
}
else //否则意味着还没有捕获到下降沿
{
TIM5_CH1_CAPTURE_STA=0;//清空
TIM5_CH1_CAPTURE_VAL=0;
TIM5_CH1_CAPTURE_STA|=0x40;//标记捕获到了一个上升沿
TIM_Cmd(TIM5,ENABLE);//使能定时器5
TIM_SetCounter(TIM5,0);//将定时器5清空
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);//设置下降沿捕获
TIM_Cmd(TIM5,ENABLE);//使能定时器5
}
}
}
TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_Update);//清除中断标志位
}
#ifndef _INTPUTCAPTURE__H_
#define _INTPUTCAPTURE__H_
#include <sys.h>
void TIM14_Init(u32 AutomaticReload,u32 PrioritySendCount);
void TIM5_CH1_InterCapture_Init(u32 AutomationReload,u16 PrioritySendCount);
void TIM5_IRQHandler(void);
#endif
2、控制继电器的GPIO代码
#include "myGPIO.h"
void myGPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
GPIO_ResetBits(GPIOF,GPIO_Pin_11 | GPIO_Pin_12| GPIO_Pin_13| GPIO_Pin_14);
}
void G(int x)
{
if(x==11)
{
GPIO_SetBits(GPIOF,GPIO_Pin_11);
}
if(x==12)
{
GPIO_SetBits(GPIOF,GPIO_Pin_12);
}
if(x==13)
{
GPIO_SetBits(GPIOF,GPIO_Pin_13);
}
if(x==14)
{
GPIO_SetBits(GPIOF,GPIO_Pin_14);
}
}
void D(int x)
{
if(x==11)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_11);
}
if(x==12)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_12);
}
if(x==13)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_13);
}
if(x==14)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_14);
}
}
#ifndef __MYGPIO_H
#define __MYGPIO_H
#include "sys.h"
void myGPIO_Init(void);//初始化
void G(int x);
void D(int x);
#endif
还用G ( )或者是D()就可以设置你需要的引脚是高还是低电平了。如G(12),那么就是将PF12设置为高电平。
注意:如果使用继电器更改通路,那么一定一定一定先将需要关闭的所有通路关闭后,再开启需要打开的通路,不然会影响整个系统的功能!!!
五、使用陶晶驰串口屏
本博客就不仔细教学了,在b站上有很详细的教学。我会附上我使用过的代码。
void HMISends(char *buf1) //字符串发送函数
{
u8 i=0;
while(1)
{
if(buf1[i]!=0)
{
USART_SendData(USART1,buf1[i]); //发送一个字节
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){};//等待发送结束
i++;
}
else
return ;
}
}
void HMISendb(u8 k) //字节发送函数
{
u8 i;
for(i=0;i<3;i++)
{
if(k!=0)
{
USART_SendData(USART1,k); //发送一个字节
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){};//等待发送结束
}
else
return ;
}
}
六、按键控制
#include "key.h"
#include "delay.h"
//
//按键输入驱动代码
//STM32F4工程-库函数版本
//淘宝店铺:http://mcudev.taobao.com
//
//按键初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOA,GPIOE时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //KEY0 KEY1 KEY2对应引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,3,4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//WK_UP对应引脚PA0
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA0
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下
//4,WKUP按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return 1;
else if(KEY1==0)return 2;
else if(KEY2==0)return 3;
else if(WK_UP==1)return 4;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0;// 无按键按下
}
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
//
//按键输入驱动代码
//STM32F4工程-库函数版本
//淘宝店铺:http://mcudev.taobao.com
//
/*下面的方式是通过直接操作库函数方式读取IO*/
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) //PE3
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) //PE2
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PA0
#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define WKUP_PRES 4
void KEY_Init(void); //IO初始化
u8 KEY_Scan(u8); //按键扫描函数
#endif
使用按键key0是控制长度检测,key1是控制负载检测及负载计算。
七、主函数的分析
#include "sys.h"
#include "delay.h"
#include "myadc.h"
#include "usart.h"
#include "key.h"
#include "zhong.h"
#include "myGPIO.h"
#include "IntputCapture.h"
#define midu 230
#define C_midu 81.2
#define R1 218000
#define R2 216000
#define R_biao 22.5
#define jin_shu_tou 70
#define R_w 0.98
extern u8 TIM5_CH1_CAPTURE_STA;
extern u32 TIM5_CH1_CAPTURE_VAL;
long long Temp=0;
uint16_t AD0, AD1, AD2, AD3,i=0,ii=0;
float ad0,ad1,ad2,ad3,k,time_kong,time_o,bili,C_o_k=0;
int zong,shicezhong,pan_duan_lei_xing=0,MD[10],shicezhong1[10],j;
char txt[30];
float C_kong,changdu;
float Ro=0,C_o=0;
char txt[30];
//以上为变量定义
//*************************************************************************************************************************************************************************
void PWMHZ_K(void); //
void PWMHZ(void); //PA0 测试频率
void ADC_2(void); //PA1,PA2 AD采集
void HMISends(char *buf1); //字符串发送函数
void HMISendb(u8 k); //字节发送函
void cezhong(void);
void one(void);
void two(void);
void three(void);
void clear(void);
//以上是函数声明
//**********************************************************************************************************************************************************************
/*
HMISends("t5.txt=\"三大关\"");
HMISendb(0xff);
sprintf(txt,"t6.txt=\"%d\"",i);
HMISends(txt);
HMISendb(0xff);
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
uart_init(9600);
KEY_Init();
TIM14_Init(500-1,84-1);
AD1_Init();
Init_HX711pin();
myGPIO_Init();
TIM5_CH1_InterCapture_Init(0xFFFFFFFF,84-1);
Get_Maopi_1(); //称毛皮重量
Get_Maopi_2();
delay_ms(1000);
Get_Maopi_1();
Get_Maopi_2(); //重新获取毛皮
D(11);
D(12);
clear();
HMISends("t5.txt=\"开始检测\"");
HMISendb(0xff);
//printf("开始检测\r\n");
//*********************************************************************
while(1)
{
if(KEY_Scan(0)==1)//按键1被按下时,实行测量长度
{
clear();
//printf("正在检测\r\n");
HMISends("t5.txt=\"正在检测\"");
HMISendb(0xff);
while(1)
{
two();
if(shicezhong>0.05)
{
// printf("结果保持\r\n");
HMISends("t5.txt=\"结果保持\"");
HMISendb(0xff);
// printf("开路\r\n");
HMISends("t7.txt=\"开路\"");
HMISendb(0xff);
//printf("重量为=%d g, 长度:%f m\r\n",shicezhong,changdu);
sprintf(txt,"t6.txt=\"%.4fcm\"",changdu);
HMISends(txt);
HMISendb(0xff);
shicezhong=0;
break;
}
}
D(11);
G(12);//PF12变高使得电容电路接通
delay_ms(1000);
for(i=0;i<20;i++)
{
PWMHZ();
delay_ms(1);
}
C_o_k=time_o*1000000/((R1+2*R2)*0.7);
//printf("C_o_k:%f pF\r\n",C_o_k);
}
if(KEY_Scan(0)==2)//按键2按下时,实行测量负载
{
clear();
sprintf(txt,"t6.txt=\"%.4fcm\"",changdu);
HMISends(txt);
HMISendb(0xff);
// printf("正在检测\r\n");
HMISends("t5.txt=\"正在检测\"");
HMISendb(0xff);
D(12);
G(11);
three();
}
}
}
void clear(void)
{
HMISends("t5.txt=\" \"");
HMISendb(0xff);
HMISends("t6.txt=\" \"");
HMISendb(0xff);
HMISends("t7.txt=\" \"");
HMISendb(0xff);
HMISends("t8.txt=\" \"");
HMISendb(0xff);
}
/*
//第一步,并测量密度,(检测空载时候的电容在上面的while循环里面).
//void one(void)
//{
// for(i=0;i<=5;i++)
// {
// cezhong();
// MD[i]=zong;
// }
// if(MD[2]==MD[5]&&MD[5]>50)
// {
// midu=MD[2];
// }
// printf("密度:%d i=%d\r\n",midu,ii);
// ii++;
//}
*/
//第二步,测长度(需要按键操控开始,并结束第一步)
void two(void)
{
for(i=0;i<=5;i++)
{
cezhong();
shicezhong1[i]=zong;
}
if(shicezhong1[2]==shicezhong1[5]&&shicezhong1[5]>20)
{
shicezhong=shicezhong1[5];
}
changdu=(float)(shicezhong-jin_shu_tou)/midu*100;
// printf("shicezhong=%d, 长度:%f\r\n",shicezhong,changdu);
//接下来需要减去无关量
/*
*/
}
//第三步,按下另一个按键(关闭之前的电路),开始测量负载(先定性,再定量)
void three(void)
{
i=30;
while(i--)
{
ADC_2();
}
//printf("");
if(bili>0.9)//表示是电容
{
pan_duan_lei_xing=1;
// printf("是电容\r\n");
HMISends("t7.txt=\"电容\"");
HMISendb(0xff);
}
else//否则是电阻
{
pan_duan_lei_xing=2;
//printf("是电阻\r\n");
HMISends("t7.txt=\"电阻\"");
HMISendb(0xff);
}
delay_ms(10);
i=100;
if(pan_duan_lei_xing==2)//下面要更改电路,并且测量分压电阻
{
D(12);
delay_ms(1);
G(11);//PF11变高使得电阻电路接通
delay_ms(500);
Ro=k-R_w;
//printf("电阻:%f\r\n",k);
sprintf(txt,"t8.txt=\"%f\"",Ro);
HMISends(txt);
HMISendb(0xff);
HMISends("t5.txt=\"结果保持\"");
HMISendb(0xff);
pan_duan_lei_xing=0;
}
if(pan_duan_lei_xing==1)//下面要接入到电容检测电路,继续测量周期
{
D(11);
G(12);//PF12变高使得电容电路接通
delay_ms(500);
while(i--)
{
PWMHZ();
delay_ms(1);
}
C_o=time_o*1000000/((R1+2*R2)*0.7);
C_o=C_o-C_o_k;
//printf("电容:%f pF\r\n",C_o);
sprintf(txt,"t8.txt=\"%.3fpF\"",C_o);
HMISends(txt);
HMISendb(0xff);
HMISends("t5.txt=\"结果保持\"");
HMISendb(0xff);
pan_duan_lei_xing=0;
D(12);
//下面是处理C_kong
/*
*/
}
}
void PWMHZ(void)
{
//printf("进来了\r\n");
delay_ms(10);
if(TIM5_CH1_CAPTURE_STA&0x80)//成功捕获一次高电平
{
Temp=TIM5_CH1_CAPTURE_STA&0x3F;//低6位的值给到Temp 循环次数
Temp*=0xFFFFFFFF; //溢出时间总和 循环次数乘以最大重装载值(最大重装载值就是一个周期的计数器值)就是循环这么多次的时间
Temp=Temp+TIM5_CH1_CAPTURE_VAL; //总的高电平时间 TIM5_CH1_CAPTURE_VAL表示最后一次检测到低电平时的计数器值
//printf("HIGH:%lld us\r\n",Temp);//打印总的高电平时间
time_o=Temp;
TIM5_CH1_CAPTURE_STA=0; //开启下一次捕获
}
}
void ADC_2(void)
{
AD1 = AD_GetValue(ADC_Channel_1);
AD2 = AD_GetValue(ADC_Channel_2);
ad1=AD1*3.3/4096;
ad2=AD2*3.3/4096;
bili=ad2/ad1;
k=ad1-ad2;
k=ad2/k;
k=k*R_biao;
//printf("%f,%f,%f\r\n",ad1,ad2,k);
delay_ms(100);
}
void HMISends(char *buf1) //字符串发送函数
{
u8 i=0;
while(1)
{
if(buf1[i]!=0)
{
USART_SendData(USART1,buf1[i]); //发送一个字节
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){};//等待发送结束
i++;
}
else
return ;
}
}
void HMISendb(u8 k) //字节发送函数
{
u8 i;
for(i=0;i<3;i++)
{
if(k!=0)
{
USART_SendData(USART1,k); //发送一个字节
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){};//等待发送结束
}
else
return ;
}
}
void cezhong(void)
{
Get_Weight_1();
Get_Weight_2();
zong=Weight_Shiwu_2+Weight_Shiwu_1;
//printf("净重量1 = %d g 净重量2 = %d g %d\r\n 总重量=%d\r\n",Weight_Shiwu_1,Weight_Shiwu_2,Weight_Maopi_1,zong); //打印
delay_ms(100);
}
1、第一步去毛皮重量
其实他的原理很简单。在程序开始运行时,全部初始化后,乘一次重量并将它保存在一个变量中,以后就不动它了。之后,每次测出的重量减去这个重量就是我们需要的净重量。
2、关闭硬件所有通路
使用D(11)与D(12),将所有的继电器都控制在开路状态,等待后续的命令。
3、长度检测and电缆电容
重点:在本环节里面,会测量出电缆电容
因为电缆中的电容相比于外接电容大好多,并且会随长度的变化而变化,测量它是十分棘手的问题。所以我们队伍中的硬件大佬也就是推荐的那个博客的作者想到了一个办法,在每次测量长度的时候就测出电缆的电容,将他放在一个变量里面。
又因为电缆电容与负载电容是并联关系,所以总容值就是直接相加。有了这些理论支撑,后面的工作就好开展了。
按下按键key0,开始长度检测,读秤的程序开始运行。通过读取到的数值得到净重量,除以密度就是本次长度测量的长度,并在屏幕上显示。(密度是提前测好的,并写死在程序里面了)
继电器开启555多谐振荡器通路(此时负载为开路),测量出的电容就是电缆电容,并存来变量里(不会在串口屏上显示出来,只用于以后的计算)。
所以以后不管测多少次长度,就会在测量长度是把电缆电容值测量完毕。
重点:关闭所用继电器通路
4、负载检测
检测方法已经在上述内容中介绍,本段就不重复介绍了。
a、检测为电容
继电器开启555多谐振荡器通路(此时负载为电容),进行频率采集后计算得出总电容值。要想得到净电容,只需要将现在测到的电容值减去电缆电容即可,并显示在屏幕上。
重点:关闭所用继电器通路
b、检测为电阻
不许要进行任何操作,因为在电阻判断时,参数已经计算好了,此处只需直接显示在屏幕上即可。
八、总结
参加电赛,确实很锻炼我们的学习能力。在4天3夜里完成所有任务,不光要有清晰的思路还要有队友的配合。
最重要的就是,要坚持自己的想法与方案
我们这个方案相信大家看后,也会抱有一丝顾虑,没错我们老师一开始也投反对票,可以说除了我们三个以外,大多是保持中立的人,剩下的全是反对我们的方案的。在所有的外界因素对我们不利的情况下,我们信的东西只有我们的方案与能力。我们选择继续做下去。最后能得到现在的成就,让我真的很惊讶。
但是这也不是侥幸,是平时我们有足够强的stm32的基础,和电路的设计能力,才让本次的决定得以完美实行。
希望本篇博客对大家有帮助,有什么问题随时可以评论我,制作不易喜欢的话记得收藏加关注。
♪(・ω・)ノ!!!