Skip to content

Commit

Permalink
v1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Cai-Zi committed Oct 23, 2020
1 parent 5fea4a4 commit f1b49c5
Show file tree
Hide file tree
Showing 9 changed files with 1,781 additions and 171 deletions.
219 changes: 86 additions & 133 deletions software/Config/adc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,96 +3,35 @@
#include "usart.h"
#include "sys.h"
#include "math.h"
#include "main.h"
#include "pid.h"
/*
===热敏电阻演示代码===
为了消除噪声读数,采样ADC几次,然后平均样本以获得更稳定的测量值,用readThermistor函数实现。
http://www.thermistors.cn/news/293.html
*/

#define sampleNum 10

float ADC_max = 4095.0; //最大采样值,12位ADC
/*使用beta方程计算阻值。*/
float beta = 3950.0; //商家给出的电阻对应25°C下的bata值
float roomTemp = 298.15; //以开尔文为单位的室温25°C
float balanceR = 9970.0;//参考电阻
float balanceR = 9900.0;//参考电阻
float roomTempR = 10000.0; //NTC热敏电阻在室温25°C下具有典型的电阻
float currentTemperature = 0; //保存当前温度
u16 ch1Value[10];//ADC采样值
u16 adcAverage=0;
u16 ch1Value[2*sampleNum];//ADC采样值
u16 NTC_Average=0;
u16 T12_Average=0;
u16 S_temp2Volt[]={
0,55,113,173,235,299,365,432,502,573,//0~90℃
645,719,795,872,950,1029,1109,1190,1273,1356,//100~190℃
1440,1525,1611,1698,1785,1873,1962,2051,2141,2232,
2323,2414,2506,2599,2692,2786,2880,2974,3069,3164,
3260,3356,3452,3549,3645,3743,3840,3938,4036,4135,
4234,4333,4432,4532,4632,4732,4832,4933,5034,5136,//500~590℃
5237,5339,5442,5544,5648,5751,5855,5960,6065,6169};//S型热电偶分度表,单位:uV,参考温度:0℃
u16 S_caliVolt[]={0,55,113,173,235,299};//S型热电偶参考端温度0~50℃时的校正值,实际电压-校正值,再查分度表表
#define ADC1_DR_Address ((u32)0x4001244C) //ADC1的地址
//通用定时器2中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器2控制ADC1定时采样
void TIM2_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能

//定时器TIM2初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 9; //计数达到9产生中断
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC2Init(TIM2, & TIM_OCInitStructure); //初始化外设TIM2_CH2

TIM_Cmd(TIM2, ENABLE); //使能TIMx
TIM_CtrlPWMOutputs(TIM2, ENABLE);
}


//DMA1配置
void DMA1_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //使能ADC1通道时钟

//DMA1初始化
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //ADC1地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ch1Value; //ch1Value的内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)
DMA_InitStructure.DMA_BufferSize = 10; //DMA缓存大小,存放10次采样值
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定,接收一次数据后,设备地址禁止后移
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址不固定,接收多次数据后,目标内存地址后移
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //外设数据单位,定义外设数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ; //内存数据单位,HalfWord就是为16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; //DMA模式:循环传输
DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //DMA优先级:高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //配置DMA1

DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE); //使能传输完成中断

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

DMA_Cmd(DMA1_Channel1,ENABLE);
}

//中断处理函数
void DMA1_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET){
//中断处理代码
adcAverage = GetMedianNum(ch1Value,10);//中值滤波
DMA_ClearITPendingBit(DMA1_IT_TC1);//清除标志
}
}

//初始化ADC-PA0引脚

Expand All @@ -103,38 +42,44 @@ void Adc_Init(void)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟

//PA6 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);


GPIO_InitStructure.GPIO_Pin = SLEEP_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SLEEP_GPIO_Port, &GPIO_InitStructure);

ADC_InitTypeDef ADC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1通道时钟

RCC_ADCCLKConfig(RCC_PCLK2_Div6); //配置ADC时钟,为PCLK2的6分频,即12MHz

ADC_DeInit(ADC1); //复位ADC1

//ADC1初始化
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //关闭扫描方式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //关闭连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //使用外部触发模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目
ADC_Init(ADC1, &ADC_InitStructure);
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器

RCC_ADCCLKConfig(RCC_PCLK2_Div6); //配置ADC时钟,为PCLK2的6分频,即12MHz

ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_239Cycles5); //配置ADC1通道4为239.5个采样周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_239Cycles5); //配置ADC1通道6为239.5个采样周期

//使能ADC、DMA
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1,ENABLE);
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1

ADC_ResetCalibration(ADC1); //复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待校准寄存器复位完成

ADC_StartCalibration(ADC1); //ADC校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成

ADC_ExternalTrigConvCmd(ADC1, ENABLE); //设置外部触发模式使能
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
}

//获得ADC值
Expand Down Expand Up @@ -168,48 +113,23 @@ float map(float value,float fromLow,float fromHigh,float toLow,float toHigh)
{
return ((value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow)+toLow);
}

/*函数说明:对数组进行中值滤波,返回中值
bArray - 待滤波的数组;iFilterLen - 数组元素个数
/*二分法查找数字在数组中的索引,确定数字对应的索引
ipArray:包含数字的数组
start: 查找的起始索引0~len
end: 查找的结束索引start~len
value: 要查找的数值
*/
int GetMedianNum(volatile u16 * bArray, int iFilterLen)
int SearchIndex(u16 ipArray[], int start, int end ,int value) //(二分法)
{
int i,j;// 循环变量
int bTemp;

// 用冒泡法对数组进行排序
for (j = 0; j < iFilterLen - 1; j ++)
{
for (i = 0; i < iFilterLen - j - 1; i ++)
{
if (bArray[i] > bArray[i + 1])
{
// 互换
bTemp = bArray[i];
bArray[i] = bArray[i + 1];
bArray[i + 1] = bTemp;
}
}
}

// 计算中值
if ((iFilterLen & 1) > 0)
{
// 数组有奇数个元素,返回中间一个元素
bTemp = bArray[(iFilterLen + 1) / 2];
}
else
{
// 数组有偶数个元素,返回中间两个元素平均值
bTemp = (bArray[iFilterLen / 2] + bArray[iFilterLen / 2 + 1]) / 2;
}

return bTemp;
int middle = (start + end) / 2;
if (middle == start)
return middle;
else if (value < ipArray[middle])
return SearchIndex(ipArray, start, middle, value);
else
return SearchIndex(ipArray, middle, end, value);
}


/*
函数功能:读取模拟引脚,如下所示。
/*函数功能:读取模拟引脚,如下所示。
通过模数转换将电压信号转换为数字表示。但是,这样做了多次,因此我们可以对其进行平均以消除测量误差。
然后使用该平均数来计算热敏电阻的电阻。此后,电阻用于计算热敏电阻的温度。最后,温度转换为摄氏度。
有关此过程的详细信息和一般理论,请参阅allaboutcircuits.com文章。
Expand All @@ -219,15 +139,48 @@ int GetMedianNum(volatile u16 * bArray, int iFilterLen)
|
ADC引脚
*/

float readThermistor(void)
u16 get_NTC_temp(void)
{
float rThermistor = 0; //保存热敏电阻的电阻值
float tKelvin = 0; //以开尔文温度保存温度
float tCelsius = 0; //以摄氏温度保存温度
NTC_Average = Get_Adc_Average(6,10);
/*公式计算热敏电阻的电阻。*/
rThermistor = balanceR *adcAverage/(ADC_max - adcAverage);
rThermistor = balanceR * NTC_Average/(ADC_max - NTC_Average);
tKelvin =(beta * roomTemp)/(beta +(roomTemp * log(rThermistor / roomTempR)));
tCelsius = tKelvin - 273.15; //将开尔文转换为摄氏温度
return tCelsius;//以摄氏度返回温度
}
//获取热电偶的电压,根据分度表转换为温度
u16 get_T12_temp(void)
{
u16 nowTemp,nowIndex;
u16 nowVolt,nowCaliVolt;
if(HEAT)
{
HEAT=0;//先停止加热
delay_ms(1);
T12_Average = Get_Adc_Average(4,10);//获取采样值
HEAT=1;//继续加热
}
else T12_Average = Get_Adc_Average(4,10);//获取采样值
nowCaliVolt = S_caliVolt[(u16)NTC_temp/10]+(NTC_temp%10)*(S_caliVolt[(u16)NTC_temp/10+1]-S_caliVolt[(u16)NTC_temp/10])/10;
nowVolt = T12_Average*3.3*2000/4095-nowCaliVolt;//热电偶当前电压uV
nowIndex = SearchIndex(S_temp2Volt,0,70,nowVolt);
nowTemp = nowIndex*10+10*(nowVolt-S_temp2Volt[nowIndex])/(S_temp2Volt[nowIndex+1]-S_temp2Volt[nowIndex]);
// printf("%d℃\r\n",nowTemp);
return nowTemp;//以摄氏度返回温度
}

u16 get_sleepSign(void)
{
u16 nowSleep;
nowSleep = SLEEP;
if(nowSleep==0)
{
sleepCount=0;
shutCount = 0;
}
// printf("%d\r\n",nowSleep);
return nowSleep;
}
10 changes: 6 additions & 4 deletions software/Config/adc.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
#include "stm32f10x.h" //记得添加此头文件,因为config.c用到GPIO相关函数等
#include "sys.h"

void TIM2_Init(u16 arr,u16 psc);//TIM2定时器初始化
void DMA1_Init(void);
extern u16 NTC_Average;
extern u16 T12_Average;

void Adc_Init(void);//ADC1初始化
u16 Get_Adc(u8 ch); //获取一次ADC的值
u16 Get_Adc_Average(u8 ch,u8 times);//ADC采样值进行均值滤波
float map(float value,float fromLow,float fromHigh,float toLow,float toHigh);//映射函数
int GetMedianNum(volatile u16 * bArray, int iFilterLen);//中值滤波
float readThermistor(void);
u16 get_NTC_temp(void);
u16 get_T12_temp(void);
u16 get_sleepSign(void);
#endif
1 change: 0 additions & 1 deletion software/Config/key.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ void TIM3_Init(u16 arr,u16 psc)
{
menuEvent[0]=1;//菜单事件
menuEvent[1]=KEY_enter; //旋转编码器短按
if(nowMenuIndex == home) heatFlag = !heatFlag;
}
if(status==KEY_LONG)
{
Expand Down
28 changes: 16 additions & 12 deletions software/Config/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
#include "delay.h"
#include "usart.h"
#include "pid.h"

#include "adc.h"
#include "main.h"
/*https://blog.csdn.net/embedded_guzi/article/details/35835755
https://blog.csdn.net/calmuse/article/details/79346742
*/
u8 volatile sleepFlag = 0;//是否休眠
u8 volatile shutFlag = 0;//是否休眠
u8 volatile sleepFlag = 0;//是否休眠,1为休眠
u8 volatile shutFlag = 0;//是否休眠,1为关机
u8 volatile nowMenuIndex = 0;
u8 volatile lastMenuIndex = 0;
u8 volatile heatFlag = 1;//是否加热
extern unsigned char logo[];
extern unsigned char logoR[];
char batVoltStr[10]={0};
Expand Down Expand Up @@ -85,15 +85,20 @@ void homeWindow(void)
getClockTime(timeStr);
OLED_ShowString(0,0,(u8*)timeStr,16,0);//时间00:00:00
char tempStr[4];//温度字符串
sprintf((char *)tempStr,"%d",setData.setTemp);//组合时间字符串
sprintf((char *)tempStr,"%d",setData.setTemp);//组合温度字符串
OLED_ShowString(88,0, (u8 *)tempStr,16,0);//设置温度
OLED_DrawPointBMP(112,1,tempSign,16,16,0);//℃
OLED_DrawPointBMP(112,0,tempSign,16,16,0);//℃
OLED_Fill(0,15,127,15,1);//水平分割线
OLED_DrawPointNum(0,17,2*6,1);//当前温度-百位
OLED_DrawPointNum(25,17,8*6,1);//当前温度-十位
OLED_DrawPointNum(50,17,0*6,1);//当前温度-个位
u16 bai,shi,ge;
bai = (u16)T12_temp/100;
shi = (u16)T12_temp%100/10;
ge = (u16)T12_temp%10;
OLED_DrawPointNum(0,17,bai*6,1);//当前温度-百位
OLED_DrawPointNum(25,17,shi*6,1);//当前温度-十位
OLED_DrawPointNum(50,17,ge*6,1);//当前温度-个位
OLED_DrawPointBMP(78,24,tempSign,16,16,1);//℃
if(heatFlag){
OLED_ShowNum(78,48,(u16)NTC_temp,2,16);//手柄温度
if(HEAT){
OLED_DrawPointBMP(110,24,heatSign,16,16,1);//加热标志
OLED_ShowString(104,48, (u8 *)" ON",16,1);//加热设置
}
Expand All @@ -103,7 +108,7 @@ void homeWindow(void)
}
else{
OLED_Fill(110,24,126,40,0);//清空标志
OLED_ShowString(104,48, (u8 *)"OFF",16,1);//加热设置
OLED_ShowString(104,48, (u8 *)" ON",16,1);//加热设置
}
}

Expand Down Expand Up @@ -227,7 +232,6 @@ void menu_gybjTip(void){
OLED_ShowString(32,0,(u8*)": ",16,1);
u8 czIndex[] = {40,41};
OLED_ShowChineseWords(48,0,czIndex,2,1);
OLED_ShowString(80,0,(u8*)"CaiZi",16,1);

u8 rqIndex[] = {35,36};
OLED_ShowChineseWords(0,16,rqIndex,2,1);
Expand Down
1 change: 0 additions & 1 deletion software/Config/menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ typedef struct{

extern volatile u8 nowMenuIndex;
extern volatile u8 lastMenuIndex;
extern volatile u8 heatFlag;
extern volatile u8 sleepFlag;
extern volatile u8 shutFlag;

Expand Down
Loading

0 comments on commit f1b49c5

Please sign in to comment.