解读:
模拟液位检测系统中,实验设计任务要求:
依据实验设计任务,所要用到的片内,片外外设有:
1.ADC, 2.UART,3. LCD, 4.LED, 5.E2PROM, 6.按键
这里自己多读几遍设计任务以及要求再看下面
A):先搭总体框架
//引入头文件
#include "key.h"
#include "led.h"
#include "lcd.h"
#include "usart.h"
#include "i2c.h"
#include "adc.h"
//函数声明
void LCD_Proc(void);
void KEY_Proc(void);
void ADC_Proc(void);
void UART_Proc(void);
//主函数
int main(void)
{
SysTick_Config(72000);//开启系统时钟
STM3210B_LCD_Init();//LCD初始化四部曲
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
KEY_Init(); //按键初始化
ADC1_Init();//ADC初始化
i2c_init(); //i2c初始化
USART2_Init(9600);//串口初始化
LED_Init();//初始化
LED_Disp(ucLed);//LED功能函数
i2c_read(pucTh, 0, 3);//i2c读取函数
while(1)
{
ADC_Proc();
LCD_Proc();
KEY_Proc();
UART_Proc();
}
}
//LCD显示处理
void LCD_Proc(void)
{
}
//按键扫描
void KEY_Proc(void)
{
}
//ADC处理程序
void ADC_Proc(void)
{
}
//USART2处理程序
void UART_Proc(void)
{
}
void SysTick_Handler(void)
{
ulTick_ms++;
}
// USART2中断处理程序
void USART2_IRQHandler(void)
{
}
LED的处理函数等会儿直接在系统滴答定时器里进行
各初始化函数:
LCD初始化
void STM3210B_LCD_Init(void)
{
LCD_CtrlLinesConfig();
dummy = LCD_ReadReg(0);
if(dummy == 0x8230){
REG_8230_Init();
}
else{
REG_932X_Init();
}
dummy = LCD_ReadReg(0);
}
*******************************************************
void LCD_Clear(u16 Color)
{
u32 index = 0;
LCD_SetCursor(0x00, 0x0000);
LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
for(index = 0; index < 76800; index++)
{
LCD_WriteRAM(Color);
}
}
*******************************************************
void LCD_SetBackColor(vu16 Color)
{
BackColor = Color;
}
*******************************************************
void LCD_SetTextColor(vu16 Color)
{
TextColor = Color;
}
按键初始化
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 允许GPIOA和GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// PA0和PA8浮空输入(复位状态,可以省略)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// PB1和PB2浮空输入(复位状态,可以省略)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
ADC初始化
void ADC1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;
// 允许GPIOB和ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// PB0-IN8模拟输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// 初始化ADC1
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; //ADC为独立工作模式
ADC_InitStruct.ADC_ScanConvMode = DISABLE;//单通道模式
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;//单次模式
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发方式,软件触发
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据对齐方式右对齐
ADC_InitStruct.ADC_NbrOfChannel = 1;//进行规则转换的ADC通道数目
ADC_Init(ADC1, &ADC_InitStruct);
// 配置通道8
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1,
ADC_SampleTime_1Cycles5);
// 配置通道16
ADC_InjectedChannelConfig(ADC1, ADC_Channel_16, 1,
ADC_SampleTime_239Cycles5);
ADC_TempSensorVrefintCmd(ENABLE); //使能或者失能温度传感器和内部参考电压通道
ADC_AutoInjectedConvCmd(ADC1, ENABLE);//使能或者失能指定ADC在规则组转化后自动开始注入组转化
// 启动ADC1
ADC_Cmd(ADC1, ENABLE);
// 校准ADC1
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
i2c初始化
void i2c_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = SDA_Pin | SCL_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}
USART2初始化
void USART2_Init(unsigned long ulBaud)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 允许GPIOA和USART2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// PA2-TX2复用推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// PA3-TX2浮空输入(复位状态,可以省略)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 初始化USART2(波特率ulBaud,默认8个数据位,1个停止位,无校验,允许Rx和Tx)
USART_InitStruct.USART_BaudRate = ulBaud;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStruct.USART_HardwareFlowControl
= USART_HardwareFlowControl_None;
USART_Init(USART2, &USART_InitStruct);
// 允许USART2
USART_Cmd(USART2, ENABLE);
//中断优先级组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 允许USART2接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
// 允许NVIC USART2中断
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
LED初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 |\
GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 |\
GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStruct);
}
*******************************************************
void LED_Disp(unsigned char ucLed)
{
GPIO_Write(GPIOC, ~ucLed << 8);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
注:仅展示部分初始化函数,完整程序请下载工程查看
B):LCD处理函数
要做两面屏,并且通过B1按键切换,这里可以设置标志位,等做按键的时候通过标志位切换
第一面屏中液位等级,液位高度,R37电压采集值均为变量
第二面屏通过B2按键上下切换,且颜色突出显示,这里同样可以通过改变标志位切换
第二面屏的阈值可以通过B3,B4改变,同样设立变量先行替代
LCD程序设计如下
unsigned char ucState,pucStr[21], ucHeight, ucLevel,pucTh[3];
unsigned int Vadc_Val;
float temp;
void LCD_Proc(void)
{
if(!ucState)// ucState 标志位
{
LCD_DisplayStringLine(Line1, (u8*)" Liquid Level ");
sprintf((char*)pucStr, " Height:%03dcm ", ucHeight);//ucHeight 液位高度
LCD_DisplayStringLine(Line4, pucStr);
temp = (float)Vadc_Val*3.3/4095;
sprintf((char*)pucStr, " ADC: %4.2fV ", temp);//temp R37电压采集
LCD_DisplayStringLine(Line6, pucStr);
sprintf((char*)pucStr, " Level: %1u ", ucLevel);//ucLevel 液位等级
LCD_DisplayStringLine(Line8, pucStr);
}
else // 阈值设置
{
LCD_DisplayStringLine(Line1, (u8*)" Parameter Setup ");
sprintf((char*)pucStr, " Threshold1:%02dcm ", pucTh[0]); //pucTh[0] 阈值1
if(ucState == 1)
LCD_SetBackColor(Magenta);
LCD_DisplayStringLine(Line4, pucStr);
LCD_SetBackColor(Blue);
sprintf((char*)pucStr, " Threshold2:%02dcm ", pucTh[1]); //pucTh[1] 阈值2
if(ucState == 2)
LCD_SetBackColor(Magenta);
LCD_DisplayStringLine(Line6, pucStr);
LCD_SetBackColor(Blue);
sprintf((char*)pucStr, " Threshold3:%02dcm ", pucTh[2]); //pucTh[2] 阈值3
if(ucState == 3)
LCD_SetBackColor(Magenta);
LCD_DisplayStringLine(Line8, pucStr);
LCD_SetBackColor(Blue);
}
}
C):按键处理函数
实验按键功能:
B1:第一二屏切换
B2:第二屏的上下切换
B3:阈值+5cm
B4:阈值-5cm
注:阈值的范围为5—95,设计时应该加入限制条件,并保存在E2PROM。
//按键扫描
void KEY_Proc(void)
{
unsigned char ucKey_Val;
ucKey_Val = KEY_Scan();
if(ucKey_Val != ucKey_Long)
ucKey_Long = ucKey_Val;
else
ucKey_Val = 0;
switch(ucKey_Val)
{
case 1: // B1: 设置键
if(!ucState)
ucState = 1;
else
{
if(pucTh[0] < pucTh[1] && pucTh[1] < pucTh[2])
{
LCD_DisplayStringLine(Line3, (u8*)" ");
i2c_write(pucTh, 0, 3); // 保存阈值
ucState = 0;
}
else
{
LCD_SetTextColor(Red);
LCD_DisplayStringLine(Line3, (u8*)" Threshold Error ");
LCD_SetTextColor(White);
}
}
break;
case 2: // B2: 切换键
if(ucState)
if(++ucState == 4)
ucState = 1;
break;
case 3: // B3: 阈值加
if(ucState)
if(pucTh[ucState-1] < 95)
pucTh[ucState-1] += 5;
break;
case 4: // B4: 阈值减
if(ucState)
if(pucTh[ucState-1] > 5)
pucTh[ucState-1] -= 5;
}
}
D):ADC处理函数
ADC处理函数功能:
R37电压采集,以及液位高度的转换,液位等级的设置,这里与阈值设定关联。
void ADC_Proc(void)
{
if(!ucState && ucSec != ucSec1)
{
ucSec1 = ucSec;
Vadc_Val = ADC1_Conv(); // 获取转换值
ucHeight = Vadc_Val*100/4095; // 计算液位高度
//ucHeight =(temp/3.3)*100;//注:此种转换值只能到99;
if(ucHeight < pucTh[0])
ucLevel = 0;
else if(ucHeight < pucTh[1])
ucLevel = 1;
else if(ucHeight < pucTh[2])
ucLevel = 2;
else
ucLevel = 3;
}
}
注:
ucHeight = Vadc_Val*100/4095; // 计算液位高度
ucHeight =(temp/3.3)*100;//注:此种转换值只能到99;
这两种计算结果第一种直接用R37采集到的电压值转换的更为准确,
E):UART处理函数
UART处理函数功能:
UART程序设计如下
程序功能:如4.1查询
//USART2处理程序
void UART_Proc(void)
{
if(pucRcv[0] == 'C')
printf("C:H%02u+L%1u\r\n", ucHeight, ucLevel);
if(pucRcv[0] == 'S')
printf("S:TL%02u+TM%02u+TH%02u\r\n", pucTh[0], pucTh[1], pucTh[2]);
pucRcv[0] = 0;
}
*******************************************************
// USART2中断处理程序
void USART2_IRQHandler(void)
{
pucRcv[0] = USART_ReceiveData(USART2);
}
4.2输出所要实现的功能可以放入ADC处理函数中,这样液位等级在改变同时自动输出到串口
把该条件放入ADC_Proc中,实现4.2输出的功能
if(ucLevel != ucLevel1)
{
(ucLevel > ucLevel1)?
printf("A:H%02u+L%1u+U\r\n", ucHeight, ucLevel)
: printf("A:H%02u+L%1u+D\r\n", ucHeight, ucLevel);
ucLevel1 = ucLevel;
}
F):LED处理函数
这里我们直接在SysTick_Handler里面处理
要求实现功能:
思路:
LED1: 一直闪烁,间隔1s(没什么好说的)
LED2: 液位等级变化时候,UART会发送数据,LED指示灯会工作,可以放到一起
LED3:设备接受到查询指令时候,会工作,可以放到UART里面
这里同样设立标志位,为该标志位时候执行对应功能,屡试不爽。
void SysTick_Handler(void)
{
ulTick_ms++;
if(ulTick_ms%1000 == 0)
{
ucSec++;
ucLed ^= 1; // LED1闪烁
}
if(ucLd2) // 等级改变
{
if(ulTick_ms%200 == 0)
{
if(ucNum--)
ucLed ^= 2; // LED2闪烁5次
else
{
ucLd2 = 0;
ucNum = 10;
}
}
}
if(ucLd3) // 串口查询
{
if(ulTick_ms%200 == 0)
{
if(ucNum--)
ucLed ^= 4; // LED3闪烁5次
else
{
ucLd3 = 0;
ucNum = 10;
}
}
}
LED_Disp(ucLed);
}
G):整体程序设计参考
#include "key.h"
#include "led.h"
#include "lcd.h"
#include "usart.h"
#include "i2c.h"
#include "adc.h"
unsigned int uiAdc_Val;
unsigned char ucHeight, ucLevel, ucLevel1;
unsigned char ucState, pucStr[21], pucTh[3], pucRcv[1];
unsigned char ucLed, ucLd2, ucLd3, ucNum = 10;
unsigned char ucSec, ucSec1, ucKey_Long;
unsigned long ulTick_ms;
void KEY_Proc(void);
void LCD_Proc(void);
void ADC_Proc(void);
void UART_Proc(void);
int main(void)
{
SysTick_Config(72000);
STM3210B_LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
KEY_Init();
LED_Init();
USART2_Init(9600);
i2c_init();
ADC1_Init();
i2c_read(pucTh, 0, 3);
if(pucTh[0] > 100) pucTh[0] = 10;
if(pucTh[1] > 100) pucTh[0] = 20;
if(pucTh[2] > 100) pucTh[0] = 30;
while(1)
{
KEY_Proc();
LCD_Proc();
ADC_Proc();
UART_Proc();
}
}
void KEY_Proc(void)
{
unsigned char ucKey_Val;
ucKey_Val = KEY_Scan();
if(ucKey_Val != ucKey_Long)
ucKey_Long = ucKey_Val;
else
ucKey_Val = 0;
switch(ucKey_Val)
{
case 1: // B1: 设置键
if(!ucState)
ucState = 1;
else
{
if(pucTh[0] < pucTh[1] && pucTh[1] < pucTh[2])
{
LCD_DisplayStringLine(Line3, (u8*)" ");
i2c_write(pucTh, 0, 3); // 保存阈值
ucState = 0;
}
else
{
LCD_SetTextColor(Red);
LCD_DisplayStringLine(Line3, (u8*)" Threshold Error ");
LCD_SetTextColor(White);
}
}
break;
case 2: // B2: 切换键
if(ucState)
if(++ucState == 4)
ucState = 1;
break;
case 3: // B3: 阈值加
if(ucState)
if(pucTh[ucState-1] < 95)
pucTh[ucState-1] += 5;
break;
case 4: // B4: 阈值减
if(ucState)
if(pucTh[ucState-1] > 5)
pucTh[ucState-1] -= 5;
}
}
void LCD_Proc(void)
{
float temp;
if(!ucState) // 液位检测
{
LCD_DisplayStringLine(Line1, (u8*)" Liquid Level ");
sprintf((char*)pucStr, " Height:%03ucm ", ucHeight);
LCD_DisplayStringLine(Line4, pucStr);
temp = (float)uiAdc_Val*3.3/4095;
sprintf((char*)pucStr, " VR37: %4.2fV ", temp);
LCD_DisplayStringLine(Line6, pucStr);
sprintf((char*)pucStr, " Level: %1u ", ucLevel);
LCD_DisplayStringLine(Line8, pucStr);
}
else // 阈值设置
{
LCD_DisplayStringLine(Line1, (u8*)" Threshold Setup ");
sprintf((char*)pucStr, " Threshold 1:%02ucm ", pucTh[0]);
if(ucState == 1) LCD_SetBackColor(Red);
LCD_DisplayStringLine(Line4, pucStr);
LCD_SetBackColor(Blue);
sprintf((char*)pucStr, " Threshold 2:%02ucm ", pucTh[1]);
if(ucState == 2) LCD_SetBackColor(Red);
LCD_DisplayStringLine(Line6, pucStr);
LCD_SetBackColor(Blue);
sprintf((char*)pucStr, " Threshold 3:%02ucm ", pucTh[2]);
if(ucState == 3) LCD_SetBackColor(Red);
LCD_DisplayStringLine(Line8, pucStr);
LCD_SetBackColor(Blue);
}
}
void ADC_Proc(void)
{
if(!ucState && ucSec != ucSec1)
{
ucSec1 = ucSec;
uiAdc_Val = ADC1_Conv(); // 获取转换值
ucHeight = uiAdc_Val*100/4095; // 计算液位高度
if(ucHeight < pucTh[0])
ucLevel = 0;
else if(ucHeight < pucTh[1])
ucLevel = 1;
else if(ucHeight < pucTh[2])
ucLevel = 2;
else
ucLevel = 3;
if(ucLevel != ucLevel1)
{
if(ucLevel > ucLevel1) // 液位升高
printf("A:H%02u+L%1u+U\r\n", ucHeight, ucLevel);
else // 液位降低
printf("A:H%02u+L%1u+D\r\n", ucHeight, ucLevel);
ucLevel1 = ucLevel;
ucLd2 = 1; // 等级改变
}
}
}
void UART_Proc(void)
{
if(pucRcv[0] == 'C')
printf("C:H%02u+L%1u\r\n", ucHeight, ucLevel);
if(pucRcv[0] == 'S')
printf("S:TL%02u+TM%02u+TH%02u\r\n", pucTh[0], pucTh[1], pucTh[2]);
if(pucRcv[0] == 'C' || pucRcv[0] == 'S')
ucLd3 = 1; // 串口查询
pucRcv[0] = 0;
}
// SysTick中断处理程序
void SysTick_Handler(void)
{
ulTick_ms++;
if(ulTick_ms%1000 == 0)
{
ucSec++;
ucLed ^= 1; // LD1闪烁
}
if(ucLd2) // 等级改变
{
if(ulTick_ms%200 == 0)
{
if(ucNum--)
ucLed ^= 2; // LD2闪烁5次
else
{
ucLd2 = 0;
ucNum = 10;
}
}
}
if(ucLd3) // 串口查询
{
if(ulTick_ms%200 == 0)
{
if(ucNum--)
ucLed ^= 4; // LD3闪烁5次
else
{
ucLd3 = 0;
ucNum = 10;
}
}
}
LED_Disp(ucLed);
}
// USART2中断处理程序
void USART2_IRQHandler(void)
{
pucRcv[0] = USART_ReceiveData(USART2);
}
完整工程下载
选择第4个工程