【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

这篇具有很好参考价值的文章主要介绍了【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

需求

1.检测参数:水温、TDS、浊度、PH
2.超出阈值声光报警
3.LCD显示目标参数的测量结果
4.测量模式:单参数测量、所有参数表同时测量
切换方式:按键切换

原理

单总线技术

单总线技术采用单根信号线实现时钟、数据的传输,且数据的传输是双向的,能够控制一个或多个从机设备。主机发送复位脉冲、从机响应应答脉冲即为单总线的初始化过程。主机检测到从机的应答脉冲后,发出ROM命令。

单总线的初始化时序

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

主机通过拉低总线至少480us以产生复位脉冲,之后主机释放总线,进入接收模式,4.7K上拉电阻将总线拉高。从机DS18B20等单总线器件检测到上升沿后,等待1560us,接着拉低总线60240us以产生从机应答脉冲。该应答脉冲向主机表明它处于总线上,且准备就绪工作。

主机读/写时隙的时序
【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)
每个时隙只能传输一位数据。

所有写时隙至少需要60us,两次独立的写时隙之间至少需要1us的恢复时间。主机拉低总线后,写时隙开始,在15us内释放总线,4.7K上拉电阻将总线拉至高电平,则为写1时隙;保持60us以上低电平则为写0时隙。

同写时隙一样,所有读时隙至少需要60us,两次独立的读时隙之间至少需要1us的恢复时间。每个读时隙都由主机发起,至少拉低总线1us。从机发送1时,总线保持高电平;从机发送0时,拉低总线,且在该时隙结束后释放总线,由4.7K上拉电阻将总线拉回至空闲高电平状态。从机发送的数据在起始时隙之后,保持15us有效时间,因此,主机在读时隙期间必须释放总线,并且在时隙起始后的15us内采样总线状态。

PH复合电极

设计选用的复合电极是通过电位法测量目标水体中的氢离子的浓度来确定PH值的。参比电极和指示电极是复合电极的两个组成部分,外管为参比电极,内管为指示电极。本设计选用的参比电极由Ag和AgCl组成,指示电极为玻璃电极。电极接触到目标水体后,溶液中的氢离子与敏感玻璃膜发生离子交换反应,产生膜电位。

浊度透射法

通过吸光率检测浊度的大小,单位NTU。

系统设计框图

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

硬件

主控MCU:STM32F103C8T6

电源模块:2串 3.7V 锂电池降压至 5V 和 3.3V
MCU主控板使用5V和3.3V供电,DS18B20、TDS传感器模块、有源蜂鸣器模块和LCD显示屏采用3.3V供电,浊度和PH传感器模块则采用5V供电。

传感器:
DS18B20 —— 水温

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

TWS-30 —— 浊度

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)
左边数字信号输出电路,右边模拟信号输出电路,仅使用右边部分。信号经过R6、R3电阻后通过MCP6002T-I/SN运算放大器组成的跟随器,最后RC滤波输出,经分压电路分压后与MCU的PA2引脚相连。

E-201-C —— PH

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

模块使用5V电源供电,3个104规格的电容滤波,并通过LED2指示模块的工作状态。J3选用BNC(Q9)接口作值为PH电极信号的输入,输出则分为两路输出:上模拟信号输出,输出电压最大值5V;下数字信号输出。仅使用模拟信号输出部分电路。
参比电极的电压固定,为参考电压,即PH-信号电压;玻璃电极的电压随PH值的变化而变化,即PH+信号电压。TLC4502芯片是双通道运算放大器,PH+信号通过其中一路放大器2倍放大后,一路通过排针经分压电路分压后与MCU的PA1引脚相连;另一路则作为数字信号处理部分的输入,后经LM358运算放大器比较后输出高低电平, 阈值(高低电平临界值)通过调节R23电位器设置。TLC4502芯片的另外一路放大器则作为PH-信号的跟随器使用。

TDS

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

语音报警:基于 PNP 型三极管的开关电路驱动有源蜂鸣器
3.3V供电,低电平触发
【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

分压电路

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

PH传感器模块和浊度传感器模块必需。由于单片机ADC无法采集5V电压,在扩展板上增加了1K和2K电阻分压电路,使得ADC输入电压范围在0~3.3V之间。

软件

!!!注释不一定对,懒得改了!!!

总体流程图

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

系统的多级菜单程序采用3级结构:0级菜单、1级菜单和2级菜单。main函数首先调用各种初始化函数,然后显示0级菜单,之后通过变量Menu_Change顺序进行菜单级数的判断,进行对应的函数处理,最后循环菜单级数的判断。

main函数设计

#include "speaker.h"
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "ds18b20.h"
#include "adc.h"
#include "usart.h"
#include "alarm.h"
#include "menu.h"
#include "lcd.h"
#include "Dis_Picture.h" 
#include "Text.h"	
#include "GBK_LibDrive.h"	


//STM32F103核心板例程--1.8寸液晶屏驱动例程
//库函数版本例程
//字号12,1个字符步进6
 	
 int main(void)
 {	 
	extern uint8_t List_Number;
	
	delay_init();	    	     //延时函数初始化	  
	NVIC_Configuration(); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	     //串口初始化为115200
 	LED_Init();			         //LED端口初始化 
	Speaker_Init(); 
	LCD_Init();           //初始化LCD SPI 接口
	GBK_Lib_Init();       //硬件GBK字库初始化--(如果使用不带字库的液晶屏版本,此处可以屏蔽,不做字库初始化)
	Adc_Init();
	KEY_Init();
	
	 
	LCD_Clear(WHITE); //清屏 
	LCD_Fill(0,0,LCD_X,LCD_H,WHITE);
	Display_Initial_Menu(10, 3, BLUE, 15);		//注册一个0级菜单,防止刚开机不显示东东西
	LCD_ShowxNum(110, 140, List_Number, 1, 12, BLUE, 1);

	
	while(1)
	{  	
		Menu_Display_Control();		
	}	
}

LCD显示

系统采用的1.8寸TFT-LCD使用SPI协议与单片机之间进行通信。横向点阵数量为128,纵向点阵数量160,每个12号字体的字符占6个像素点宽度。

LCD显示程序设计如下:首先进行GPIO口设置并使能时钟,其中PB13、PB14、PB15设置为推挽复用输出。然后使能SPI2端口时钟,注意SPI2端口是挂载在APB1总线上的。接着初始化SPI2并进行SPI2的具体设置,使能SPI2,发送0xff指令启动传输,最后进行其余GPIO口的配置以及LCD功能函数的设置。

void SPI2_Init(void)
{	 
	GPIO_InitTypeDef  GPIO_InitStructure;
	SPI_InitTypeDef  SPI_InitStructure;
	
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB ,ENABLE);
	
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//推挽复用输出
	GPIO_Init(GPIOB, &GPIO_InitStructure);
			
	//SPI2配置选项
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2 ,ENABLE);
 
	//这里只针对SPI口初始化
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,ENABLE);    //复位SPI2
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,DISABLE);   //停止复位SPI2

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:全速双工模式
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		                    //设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		                //设置SPI的数据大小:SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		                      //串行同步时钟的空闲状态为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	                      //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		                        //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;	//定义波特率预分频的值:波特率预分频值
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	                //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	                          //CRC值计算的多项式, >1即可
	SPI_Init(SPI2, &SPI_InitStructure);                                 //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI2, ENABLE); //使能SPI外设

	SPI2_ReadWriteByte(0xff);//启动传输		  
	
}

void LCD_GPIO_Init(void)
{

	GPIO_InitTypeDef  GPIO_InitStructure;
	      
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC ,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 |GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;   //PC6  命令/数据--切换引脚  //PC7    	  //背光控制
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       //100MHz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);   

  
		
  SPI2_Init();           //初始化硬件SPI2

//  LCD_HardwareRest();   //硬复位--如果IO连接,硬件复位可控有效
	
	LCD_BLK_On;           //开启背光
	
//	LCD_BLK_Off;   //关闭背光
//	LCD_BLK_On;    //开启背光
      
}

按键

系统硬件设置按键按下松开,IO口输入低电平。具体程序设计如下:首先进行GPIO口配置,注意PB3引脚默认JTDO功能,PB4引脚默认JNTRST功能,因此需要开启AFIO复用时钟,并关闭JTAG,使能SWD。然后进行按键扫描函数的设置,按下UP键返回1,按下DOWN键返回2,按下CONFIRM键返回3,按下CANCEL键返回4。

#include "stm32f10x.h"
#include "key.h"
#include "sys.h" 
#include "delay.h"


//STM32F103核心板例程
//库函数版本例程



//	 

//STM32开发板
//按键驱动代码	   
						  
//  
								    
//按键初始化函数
void KEY_Init(void) //IO初始化
{ 
 	GPIO_InitTypeDef GPIO_InitStructure;
	//初始化KEY1-->PA0上拉输入
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO,ENABLE);//使能PA时钟
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_3 |GPIO_Pin_4 |GPIO_Pin_8 |GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
 	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化PA


}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下


static u8 key_up=1;//按键按松开标志

u8 KEY_Scan(u8 mode)
{	 
	
	if(mode)key_up=1;  //支持连按
	
	if(key_up&&(UP==0||DOWN==0||CONFIRM==0||CANCEL==0))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(UP==0) return 1;
		if(DOWN==0) return 2;
		if(CONFIRM==0) return 3;
		if(CANCEL==0) return 4;
	}
	else if(UP==1&&DOWN==1&&CONFIRM==1&&CANCEL==1)key_up=1; 	    
 	
	return 0;// 无按键按下
}


多级菜单

参考大佬的程序:https://blog.csdn.net/qq_43940175/article/details/123396075?spm=1001.2014.3001.5506

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)
程序设计如下:首先进行各级菜单界面显示设置,然后进行上滑、下滑动作的当前选项序号List_Number的更新设置,再设置菜单级数的切入、切出动作,其中包括当前菜单级数Menu_Change的更新设置以及List_Number的保存与加载设置。接着设置选项框绘制函数以及选项框的上滑、下滑显示,最后进行多级菜单控制函数的设计。

Menu_Change的更新和List_Number的保存、加载过程的状态解析
【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)
Button_Menu_Enter()函数是切入控制按键,Button_Menu_Exit()是切出控制按键,select是切入切出状态标志位,切入为0,切出为1。List和List2用于保存切入前所在选项序号,List保存的是0级的选项序号,List2则保存1级的选项序号。0级切入后当前选项序号List_Number归1,即选项框从头开始,位于新页面起始位置。切出后List_Number从对应来源重新加载。

多级菜单控制函数
首先在函数外部定义全局变量mode作为Alarm()声光报警程序的标志位,具体模式设置在后续展开,此处不作讨论。函数主体通过3个if语句判断当前菜单级数Menu_Change,具体设置如下:

0级显示模式选择界面及选项框,配置UP、DOWN、CONFIRM按键。

1级判断List变量,为1时显示参数选择界面及选项框,UP、DOWN、CONFIRM、CANCEL按键均配置;为2时则显示全部参数测量结果界面,设置mode=5,采集所有参数的数据,然后调用Alarm()声光报警函数。此界面没有下一级结构,且未设置选项,因此仅配置CANCEL按键,触发后更新Menu_Change并重置mode为0,最后切出上一级菜单,重新显示跳转前的界面及选项框所在位置。选项未触发则跳回当前界面显示,进行下一轮循环。

2级判断List2变量,根据变量值选择对应的分支结构处理,分支结构一共有4条:为1时显示温度测量结果界面,设置mode=1,进行DS18B20初始化成功与否的检查,成功则采集温度数据;为2时显示PH测量结果界面,设置mode=2,然后采集PH值;为3时显示浊度测量结果界面,设置mode=3,然后采集浊度数据;为4时显示TDS测量结果界面,设置mode=4,然后采集TDS数据。该条分支结构处理完毕后,主线调用Alarm()声光报警函数。同样没有下一级结构,且无选项设置,所以仅配置CANCEL按键,触发后更新Menu_Change并重置mode为0,最后切出上一级菜单,重新显示跳转前的界面及选项框所在位置。选项未触发则跳回当前界面显示,进行下一轮循环。

List = = 2时的界面显示及数据处理流程

结束后进行相应按键处理。

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

#include "lcd.h"
#include "menu.h"
#include "stdio.h"
#include "led.h"
#include "ds18b20.h"
#include "adc.h"
#include "alarm.h"

//多级菜单的级数
#define		Menu_Select		3

//选项显示缓存区
char buf[100];

//当前选项序号
uint8_t List_Number = 1;

//当前菜单级数   0到(Menu_Select-1)
static uint8_t Menu_Change = 0;
//菜单刷新标志位
static uint8_t Menu_Refresh_Flag = 0;
//up/down控制按键标志位,1下2上
static uint8_t UD_Action_Flag = 0;




/******************************************************************************
  * @brief  绘制窗体
  * @param  x1,y1 起始坐标
			x2,y2 终点坐标
			Box_Height 选项框的高度
			Menu_List	选项框个数
  * @retval 无
 ******************************************************************************/

void LCD_Draw_Windows(void)
{
	LCD_Draw_Box(0, 0, LCD_X, LCD_H, LBBLUE, 5);
	LCD_Draw_Box(0, 10, LCD_X, 15, LBBLUE, 5);
	sprintf(buf, "   WINDOWS1      ");//发送格式化输出到指针所指向的字符串
	LCD_ShowString(5, 5, 100, 12, 12, LBBLUE, (uint8_t*)buf);
}



/******************************************************************************
      * @brief	显示初始菜单,自己任意配置
      * @param  x,y 显示坐标
				fc 字的颜色
				Box_Height 选项框的高度
      * @retval 无
******************************************************************************/
void Display_Initial_Menu(u16 x,u16 y,u16 fc,u8 Box_Height)
{
		sprintf(buf,"Single Parameter");
		LCD_ShowString(x, y + ( Box_Height * 0 ), 200, 12, 12, fc, (uint8_t *)buf );
		sprintf(buf,"All Parameters");
		LCD_ShowString(x, y + ( Box_Height * 1 ), 200, 12, 12, fc, (uint8_t *)buf );
		//sprintf(buf,"NOTICE");
		//LCD_ShowString(x, y + ( Box_Height * 2 ), 200, 12, 12, fc, "NOTICE" );
		
}



/******************************************************************************
	  * @brief	显示二级菜单,自己任意配置
	  * @param  x,y 显示坐标
				fc 字的颜色
				Box_Height 选项框的高度
	  * @retval 无
******************************************************************************/
void Display_Second_Menu(u16 x,u16 y,u16 fc,u8 Box_Height)
{
		sprintf(buf,"Temperature");
		LCD_ShowString(x, y + ( Box_Height * 0 ), 200, 12, 12, fc, (uint8_t *)buf );
		sprintf(buf,"PH");
		LCD_ShowString(x, y + ( Box_Height * 1 ), 200, 12, 12, fc, (uint8_t *)buf );
		sprintf(buf,"Turbidty");
		LCD_ShowString(x, y + ( Box_Height * 2 ), 200, 12, 12, fc, (uint8_t *)buf );
		sprintf(buf,"TDS");
		LCD_ShowString(x, y + ( Box_Height * 3 ), 200, 12, 12, fc, (uint8_t *)buf );
}




/******************************************************************************
	  * @brief	显示三级菜单,自己任意配置
	  * @param  x,y 显示坐标
				fc 字的颜色
				bc 字的背景色
				sizey 字号
				mode:  0非叠加模式  1叠加模式
				Box_Height 选项框的高度
	  * @retval 无
******************************************************************************/
void Display_Third_Menu(void)
{
	LCD_ShowString( 5, 20, 200, 12, 12, BLUE, "PH_VOL:0.000V");	
	LCD_ShowString( 5, 35, 200, 12, 12, BLUE, "PH:  .  ");	
	LCD_ShowString( 5, 50, 200, 12, 12, BLUE, "TBD_VOL:0.000V");	
	LCD_ShowString( 5, 65, 200, 12, 12, BLUE, "TBD:    NTU");	
	LCD_ShowString( 5, 80, 200, 12, 12, BLUE, "TDS_VOL:0.000V");
	LCD_ShowString( 5, 95, 200, 12, 12, BLUE, "TDS:    ppm");	

	
	LCD_ShowString(5,5,100,12,12,BLUE,"DS18B20:   . C");
}

void Display_Temp_Menu(void)
{
	LCD_ShowString(5,5,100,12,12,BLUE,"DS18B20:   . C");
}

void Display_PH_Menu(void)
{
	LCD_ShowString( 5, 20, 200, 12, 12, BLUE, "PH_VOL:0.000V");	
	LCD_ShowString( 5, 35, 200, 12, 12, BLUE, "PH:  .  ");
}

void Display_TBD_Menu(void)
{
	LCD_ShowString( 5, 50, 200, 12, 12, BLUE, "TBD_VOL:0.000V");	
	LCD_ShowString( 5, 65, 200, 12, 12, BLUE, "TBD:    NTU");
}

void Display_TDS_Menu(void)
{
	LCD_ShowString( 5, 80, 200, 12, 12, BLUE, "TDS_VOL:0.000V");
	LCD_ShowString( 5, 95, 200, 12, 12, BLUE, "TDS:    ppm");
}

/******************************************************************************
  * @brief  注册up控制按键
  * @param  Menu_List:控制的列表项数目
  * @retval 无
  * eg:一个页面有6个选项,就让 Menu_List = 6
 ******************************************************************************/
void Button_Up_Click(uint8_t Menu_List)
{
	
		LED0=!LED0;
		if (List_Number > 1)
			List_Number = List_Number - 1;
		else if (List_Number == 1)
			List_Number = Menu_List;
		else
			List_Number = List_Number;

		UD_Action_Flag = 2;
		
		LCD_ShowxNum(10, 140, List_Number, 1, 12, BLUE, 0);//当前选项序号
	
}



/******************************************************************************
  * @brief  注册down控制按键
  * @param  控制的列表项数目
  * @retval 无
 ******************************************************************************/
void Button_Down_Click(uint8_t Menu_List)
{
	
	
		LED0=!LED0;
		if (List_Number < Menu_List)
			List_Number = List_Number + 1;
		else if (List_Number == Menu_List)
			List_Number = 1;
		else
			List_Number = List_Number;

		UD_Action_Flag = 1;
		
		LCD_ShowxNum(10, 140, List_Number, 1, 12, BLUE, 0);//当前选项序号
	
}



/******************************************************************************
  * @brief  注册菜单切入控制按键
  * @param  Desk_Num 菜单级数
  * @retval 无
  ****************************************************************************/
void Button_Menu_Enter(u8 Desk_Num)
{
	
	
	//切入菜单,即进入下一级菜单
	
		LED0=!LED0;
		Action_Menu_Change(0);		//List_Number的保存与重载
		Menu_Refresh_Flag = 1;

		if (Menu_Change < Desk_Num - 1)
			Menu_Change = Menu_Change + 1;
		else if (Menu_Change == Desk_Num - 1)
			Menu_Change = Desk_Num - 1;

		//显示菜单级标号
		LCD_ShowxNum(10, 140, List_Number, 1, 12, BLUE, 0);
		LCD_ShowxNum(110, 140, Menu_Change, 1, 12, BLUE, 0);//显示当前菜单级数
}



/******************************************************************************
  * @brief  注册菜单切出控制按键
  * @param  Desk_Num 菜单级数
  * @retval 无
  ****************************************************************************/
void Button_Menu_Exit(u8 Desk_Num)
{
	
	
	//切出菜单,即返回上一级菜单
	
		LED0=!LED0;
		Action_Menu_Change(1);		//List_Number的保存与重载
		Menu_Refresh_Flag = 1;
		
		if (Menu_Change > 0)
			Menu_Change = Menu_Change - 1;
		if (Menu_Change < Desk_Num - 2)
			Menu_Change = 0;

		//显示菜单级标号
		LCD_ShowxNum(10, 140, List_Number, 1, 12, BLUE, 0);
		LCD_ShowxNum(110, 140, Menu_Change, 1, 12, BLUE, 0);
}



/******************************************************************************
  * @brief  注册菜单切换控制按键搭配的动作
  * @param  select 事件标志
  * @retval 无
  ****************************************************************************/
u8 List = 0,List2 = 0;
void Action_Menu_Change(u8 select)              //确认键select=0,返回键select=1,List_Number的保存与重载
{
	switch (select)
	{
	case 0:
		switch (Menu_Change)
		{
		case 0:
			List = List_Number;                       //储存跳转前选项序号
			List_Number = 1;                          //跳转后选项序号从头开始
			LCD_Fill(0, 0, LCD_X, LCD_H, WHITE);
			break;
		case 1:
			List2 = List_Number;
			List_Number = 1;
			LCD_Fill(0, 0, LCD_X, LCD_H, WHITE);
			break;
		//case ...3级、4级菜单
		}
		break;

	case 1:
		switch (Menu_Change)
		{
		case 1:
			List_Number = List;                      //恢复跳转前选项序号 
			LCD_Fill(0, 0, LCD_X, LCD_H, WHITE);
			break;
		case 2:
			List_Number = List2;	                   //恢复跳转前选项序号 
			LCD_Fill(0, 0, LCD_X, LCD_H, WHITE);
			break;
		//case ...3级、4级菜单
		}
	default:
		break;
	}
	
}



/******************************************************************************
  * @brief  注册选项框上滑动作
  * @param  x1,y1 起始坐标
			x2,y2 终点坐标
			Box_Height 选项框的高度
			Menu_List	选项框个数
  * @retval 无
 ******************************************************************************/
void Action_Box_Up(u16 x1,u16 y1,u16 x2,u16 y2,uint8_t Menu_List, uint8_t Box_Height)
{
	if(UD_Action_Flag == 2)
	{
		UD_Action_Flag = 0;
		if(List_Number >= 1 && List_Number < Menu_List)
		{
			LCD_DrawLine(x1, y2+Box_Height*(List_Number), x2, y2+Box_Height*(List_Number), WHITE);
			LCD_DrawLine(x1, y1+Box_Height*(List_Number), x1, y2+Box_Height*(List_Number), WHITE);
			LCD_DrawLine(x2, y1+Box_Height*(List_Number), x2, y2+Box_Height*(List_Number), WHITE);
			printf("%d",List_Number);
		}
		if(List_Number == Menu_List)
		{
			LCD_DrawRectangle(x1, y1 + Box_Height*(0), x2, y2+Box_Height*(0), WHITE);
		}
	}
	
}



/******************************************************************************
  * @brief  注册选项框下滑动作
  * @param  x1,y1 起始坐标
			x2,y2 终点坐标
			Box_Height 选项框的高度
			Menu_List	选项框个数
  * @retval 无
 ******************************************************************************/
void Action_Box_Down(u16 x1, u16 y1, u16 x2, u16 y2, uint8_t Menu_List, uint8_t Box_Height)
{
	if (UD_Action_Flag == 1)
	{
		UD_Action_Flag = 0;
		if (List_Number > 1 && List_Number <= Menu_List)
		{
			LCD_DrawLine(x1, y1 + Box_Height * (List_Number - 2), x2, y1 + Box_Height * (List_Number - 2), WHITE);
			LCD_DrawLine(x1, y1 + Box_Height * (List_Number - 2), x1, y2 + Box_Height * (List_Number - 2), WHITE);
			LCD_DrawLine(x2, y1 + Box_Height * (List_Number - 2), x2, y2 + Box_Height * (List_Number - 2), WHITE);
			printf("%d", List_Number);
		}
		if (List_Number == 1)
		{
			LCD_DrawRectangle(x1, y1 + Box_Height * (Menu_List - 1), x2, y2 + Box_Height * (Menu_List - 1), WHITE);
		}
	}
}


/******************************************************************************
  * @brief  绘制选项框
  * @param  x1,y1 起始坐标
			x2,y2 终点坐标
			Box_Height 选项框的高度
			Menu_List	选项框个数
  * @retval 无
 ******************************************************************************/
void Draw_Option_Box(u16 x1, u16 y1, u16 x2, u16 y2, uint8_t Menu_List, uint8_t Box_Height)
{
	LCD_DrawRectangle(x1, y1 + Box_Height * (List_Number - 1), x2, y2 + Box_Height * (List_Number - 1), BLUE);
}



/******************************************************************************
  * @brief  注册菜单
  * @param  无
  * @retval 无
 ******************************************************************************/
int mode=0;//用于Alarm判断模式
void Menu_Display_Control(void)
{
		
	if (Menu_Change==0)
	{
		if (Menu_Refresh_Flag == 1)
		{
			Display_Initial_Menu(10, 3, BLUE, 15);		//注册一个0级菜单
			Menu_Refresh_Flag = 0;
		}
		Draw_Option_Box(3, 0, 200, 15, 2, 15);							//注册选项框
		
		switch(KEY_Scan(0))
		{
			case 1:
				Button_Up_Click(2);										//注册up控制按钮
				Action_Box_Up(3, 0, 200, 15, 2, 15); 					//添加选项框的上滑动作
				break;
			
			case 2:
				Button_Down_Click(2);											//注册down控制按钮
				Action_Box_Down(3, 0, 200, 15, 2, 15); 					//添加选项框的下滑动作
				break;
			
			case 3:
				Button_Menu_Enter(Menu_Select);
				break;
			
			
			
			default:
				Menu_Refresh_Flag = 0;
		}
	}	
	
	if(Menu_Change==1)		
	{
		switch(List)
		{
			case 1:
				if (Menu_Refresh_Flag == 1)
				{
					Display_Second_Menu(10, 3, BLUE, 15);			//注册一个1级菜单
					Menu_Refresh_Flag = 0;
				}
				
				Draw_Option_Box(3, 0, 150, 15, 4, 15);							//注册选项框
				
				switch(KEY_Scan(0))
				{
					case 1:
						Button_Up_Click(4);										//注册up控制按钮
						Action_Box_Up(3, 0, 150, 15, 4, 15); 					//添加选项框的上滑动作
						break;
					
					case 2:
						Button_Down_Click(4);											//注册down控制按钮
						Action_Box_Down(3, 0, 150, 15, 4, 15); 					//添加选项框的下滑动作
						break;
					
					case 3:
						Button_Menu_Enter(Menu_Select);
						break;
					
					case 4:
						Button_Menu_Exit(Menu_Select);
						break;
					
					default:
						Menu_Refresh_Flag = 0;
				}	
				break;	

			case 2:
				if (Menu_Refresh_Flag == 1)
				{
					Display_Third_Menu();
					Menu_Refresh_Flag = 0;
				}
				mode=5;			
				PH();
				TBD();	
				TDS();
				
				while(DS18B20_Init()) 
				{		 		
					LCD_ShowString(5,5,100,12,12,BLUE,"DS18B20 Error!");
					delay_ms(200);
					LCD_Fill(5,5,88,20,WHITE);
					delay_ms(200);
				}
				DS18B20();
				Alarm();
						
				switch(KEY_Scan(0))
				{
					

					case 4:
						Button_Menu_Exit(2);
						mode=0;
						break;
					
					default:
						Menu_Refresh_Flag = 0;
				}
				
				break;														
		}
			
	}
		
	
	if(Menu_Change==2)		
	{
		switch(List2)	
		{
			case 1:
				if (Menu_Refresh_Flag == 1)
				{
					Display_Temp_Menu();
					Menu_Refresh_Flag = 0;
				}
				mode=1;
				while(DS18B20_Init()) 
				{		 		
					LCD_ShowString(5,5,100,12,12,BLUE,"DS18B20 Error!");
					delay_ms(200);
					LCD_Fill(5,5,88,20,WHITE);
					delay_ms(200);
				}
				DS18B20();
				break;
				
				
			case 2:
				if (Menu_Refresh_Flag == 1)
				{
					Display_PH_Menu();
					Menu_Refresh_Flag = 0;
				}
				mode=2;
				PH();
				break;
					
					
			case 3:
				if (Menu_Refresh_Flag == 1)
				{
					Display_TBD_Menu();
					Menu_Refresh_Flag = 0;
				}
				mode=3;
				TBD();
				break;
				
			case 4:
				if (Menu_Refresh_Flag == 1)
				{
					Display_TDS_Menu();
					Menu_Refresh_Flag = 0;
				}
				mode=4;
				TDS();
				break;
		}
		
		Alarm();	
				
		switch(KEY_Scan(0))
		{
			case 4:
				Button_Menu_Exit(Menu_Select);
				mode=0;
				break;
			
			default:
				Menu_Refresh_Flag = 0;
		}
	}		
		
}


DS18B20

程序设计显示精度为0.1℃。

首先主机发送复位脉冲,从机发送应答脉冲初始化序列,因为系统仅使用一个DS18B20设备,即单节点,所以主机发送0xcc跳越ROM命令,然后发送0x44温度转换功能命令,至此温度转换功能开启。因为单片机每次发功能指令前都必须复位DS18B20并写ROM指令,所以主机再次发送复位脉冲,从机发送应答脉冲初始化序列,然后主机发送0xcc跳越ROM命令和0xbe读暂存器功能指令,最后进行数据处理并显示。

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

#include "ds18b20.h"
#include "delay.h"
#include "lcd.h"

short temperature;
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK miniSTM32开发板
//DS18B20驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/12
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved									  
//
  

//复位DS18B20
void DS18B20_Rst(void)	   
{                 
	DS18B20_IO_OUT(); //SET PA0 OUTPUT
    DS18B20_DQ_OUT=0; //拉低DQ
    delay_us(750);    //拉低750us,>480us
    DS18B20_DQ_OUT=1; //DQ=1 
	delay_us(15);     
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void) 	   
{   
	u8 retry=0;
	DS18B20_IO_IN();//SET PA15 INPUT
	
//应答操作,判断引脚低电平时间是否在60s和240s之间,返回应答结果,此处取200s-240s	
    while (DS18B20_DQ_IN&&retry<200)   //&&逻辑与,两个表达式都为真(true)时才为真,优先级低于关系运算符<
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=200)return 1;
	else retry=0;
	
    while (!DS18B20_DQ_IN&&retry<240)
	{
		retry++;
		delay_us(1);
	};
	if(retry>=240)return 1;	    
	return 0;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void) 			 // read one bit
{
    u8 data;
	DS18B20_IO_OUT();//设置IO方向为输出
    DS18B20_DQ_OUT=0; //主机拉低总线
	delay_us(2);
    DS18B20_DQ_OUT=1; //拉高总线
	DS18B20_IO_IN();//SET PA0 INPUT
	delay_us(12);
	if(DS18B20_DQ_IN)data=1;//输入判断
    else data=0;	 
    delay_us(50);           
    return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)    // read one byte
{        
    u8 i,j,dat;
    dat=0;
	for (i=1;i<=8;i++) 
	{
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
    }						    
    return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)     
 {             
    u8 j;
    u8 testb;
	DS18B20_IO_OUT();//设置IO方向为输出
    for (j=1;j<=8;j++) 
	{
        testb=dat&0x01;
        dat=dat>>1;
        if (testb) 
        {
					// Write 1
            DS18B20_DQ_OUT=0;//主机拉低总线
            delay_us(2);// >1us                            
            DS18B20_DQ_OUT=1;//拉高总线
            delay_us(60); //>15us            
        }
        else 
        {
					// Write 0
            DS18B20_DQ_OUT=0;//主机拉低总线
            delay_us(60); //60-120us            
            DS18B20_DQ_OUT=1;//拉高总线
            delay_us(2); // >1us                         
        }
    }
}
//开始温度转换
void DS18B20_Start(void)
{   						               
    DS18B20_Rst();	//复位   
	DS18B20_Check();	//检测 
    DS18B20_Write_Byte(0xcc);// 仅适用单节点,跳越ROM
    DS18B20_Write_Byte(0x44);// 温度转换
} 
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在    	 
u8 DS18B20_Init(void)
{
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PORTA口时钟 
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;				
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);

 	GPIO_SetBits(GPIOA,GPIO_Pin_15);    //输出1

	DS18B20_Rst();

	return DS18B20_Check();
}  
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250) 
short DS18B20_Get_Temp(void)
{
    u8 temp;
    u8 TL,TH;
	short tem;
    DS18B20_Start();         //开始温度转换
    DS18B20_Rst();
    DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);// skip rom
    DS18B20_Write_Byte(0xbe);// 读暂存器    
    TL=DS18B20_Read_Byte(); // LSB   
    TH=DS18B20_Read_Byte(); // MSB  
	    	  
    if(TH>7)
    {
        TH=~TH;
        TL=~TL; 
        temp=0;//温度为负  
    }else temp=1;//温度为正	  	  
    tem=TH; //获得高八位
    tem<<=8;    
    tem+=TL;//获得底八位
    tem=(float)tem*0.625;//转换     
	if(temp)return tem; //返回温度值
	else return -tem;    
} 
 
void DS18B20(void)
{
	
	temperature=DS18B20_Get_Temp();	
	if(temperature<0)
	{
		LCD_ShowChar(5+48,5,'-',12,RED,0);			//显示负号
		temperature=-temperature;					//转为正数
	}
	else
	{
		LCD_ShowChar(5+48,5,' ',12,RED,0);			//去掉负号
	}
			
	LCD_ShowNum(5+48+6,5,temperature/10,2,12,RED);	//显示正数部分	    
  LCD_ShowNum(5+48+24,5,temperature%10,1,12,RED);	//显示小数部分,保留1位小数
}

端口设置方向:https://zhiguoxin.blog.csdn.net/article/details/112070047?spm=1001.2014.3001.5506

ADC

PH

PH电极在使用前需使用标准缓冲溶液进行校准,每次测量后均需用蒸馏水洗净。

校准过程中的测试结果

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

模块输出电压和PH值的关系

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)
解得:PH=-5.23×U+26.66

浊度

模块输出电压和浊度值的关系

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)
浊度 = -865.68×U+3291.3

TDS

标准曲线

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)
【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

 #include "adc.h"
 #include "delay.h"
 #include "lcd.h"
 #include "led.h"
 #define K 3291.3
 #define offset 26.39

float PH_Value,TBD_Vol,TDS_Vol;
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3	
//总转换时间21us
void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

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

	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

	//PA1 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	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的寄存器   

  
	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
	
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	
	ADC_StartCalibration(ADC1);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
 
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能

}				  
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)   
{
  	//设置指定ADC的规则组通道,一个序列,采样时间
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  			    
  
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}

u16 Get_Adc_Average(u8 ch,u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=Get_Adc(ch);
		delay_ms(5);
	}
	return temp_val/times;
} 	 

void PH(void)
{
	u16 tp,TEMP;
	float temp,TP,PH_Vol;
	
	tp=Get_Adc_Average(ADC_Channel_1,10);//采样10次,取平均
	temp=(float)tp*(3.3/4096)/0.66;//12位ADC,参考电压3.3V被分成4096份,转换为5V参考
	tp=temp;
	PH_Vol=temp;
	PH_Value=-5.4*PH_Vol+offset;
	
	/***********PH值**********/
	LCD_ShowxNum(5+18,35,PH_Value,2,12,RED,0);//整数部分
	//小数部分
	TEMP=PH_Value;
	TP=PH_Value-TEMP;
	TP*=100;
	LCD_ShowxNum(5+36,35,TP,2,12,RED,0);
	
	/***********电压**********/
	LCD_ShowxNum(5+42,20,temp,1,12,RED,0);//整数部分
	//小数部分
	temp-=tp;
	temp*=1000;
	LCD_ShowxNum(5+54,20,temp,3,12,RED,0X80);
}

void TBD(void)
{
	u16 tp;
	float temp,TBD_Value;
	
	tp=Get_Adc_Average(ADC_Channel_2,10);//采样10次,取平均
	temp=(float)tp*(3.3/4096)/0.66;//12位ADC,参考电压3.3V被分成4096份,转换为5V参考
	tp=temp;
	TBD_Vol=temp;
	TBD_Value=-865.68*temp+K;
	LCD_ShowxNum(5+24,65,TBD_Value,4,12,RED,0);
	LCD_ShowxNum(5+48,50,tp,1,12,RED,0);//整数部分
	//小数部分
	temp-=tp;
	temp*=1000;
	LCD_ShowxNum(5+60,50,temp,3,12,RED,0X80);
	
}

void TDS(void)
{
	u16 tp;
	float temp,TDS_Value;
	
	tp=Get_Adc_Average(ADC_Channel_7,10);//采样10次,取平均
	temp=(float)tp*(3.3/4096);//12位ADC,参考电压3.3V被分成4096份
	tp=temp;
	TDS_Vol=temp;
	TDS_Value=66.71*temp*temp*temp-127.93*temp*temp+428.7*temp;
	LCD_ShowxNum(5+24,95,TDS_Value,4,12,RED,0);
	LCD_ShowxNum(5+48,80,tp,1,12,RED,0);//整数部分
	//小数部分
	temp-=tp;
	temp*=1000;
	LCD_ShowxNum(5+60,80,temp,3,12,RED,0X80);
}



声光报警

水温、PH或浊度超出设定的阈值(水温2532℃,PH值6.58.5,浊度电压2.0V以上)时点亮,触发方式为低电平。

mode标志位模式设置
【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

#include "sys.h"
#include "adc.h"
#include "led.h"
#include "ds18b20.h"
#include "speaker.h"
#include "alarm.h"
#include "delay.h"

void Alarm(void)
{
	extern float PH_Value,TBD_Vol,TDS_Vol;
	extern short temperature;
	extern int mode;
	
	switch(mode)
	{
		case 1:
			if((temperature/10)<25||(temperature/10)>=32)
			{
				LED1=0;
				Speaker=0;
			}
			else 
			{
				LED1=1;
				Speaker=1;
			}
			break;
			
		case 2:
			if(PH_Value<6.5||PH_Value>8.5)
			{
				LED1=0;
				Speaker=0;
			}
			else 
			{
				LED1=1;
				Speaker=1;
			}
			break;
		
		case 3:
			if(TBD_Vol<2.5)
			{
				LED1=0;
				Speaker=0;
			}
			else 
			{
				LED1=1;
				Speaker=1;
			}
			break;
			
		case 5:
			if((temperature/10)<25||(temperature/10)>=32||PH_Value<6.5||PH_Value>8.5||TBD_Vol<2.5)
			{
				LED1=0;
				Speaker=0;
			}
			else 
			{
				LED1=1;
				Speaker=1;
			}
			break;
	}
	
	
	
}

实物

【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)

功能

1) 通过UP、DOWN、CONFIRM、CANCEL按键进行单参数测量和所有参数同时测量模式的选择以及多级菜单之间的切换,按键每次按下,LED0翻转一次状态。屏幕左下角显示当前选项序号,右下角显示当前菜单级数。

2) 实时测量目标水体的温度、PH值、浊度、TDS,同时显示对应电压值,动态变化部分以红色字体显示,便于区分。

3) 温度、PH、浊度设置阈值,进入参数显示界面后,超出阈值蜂鸣器报警,同时LED1点亮。参数保持超出阈值状态,退出结果显示界面仍然声光报警,提示用户水质亟待改善。按下复位键可结束报警。文章来源地址https://www.toymoban.com/news/detail-429043.html

到了这里,关于【STM32】基于STM32F103C8T6的水质检测系统设计(声光报警、多级菜单)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于STM32F103C8T6的超声波测距应用

    #一、超声波HC_SR04简介 #二、超声波工作原理 #三、超声波测距步骤 #四、硬件接线 #五、项目代码 一、超声波HC_SR04简介 超声波传感器模块上面通常有两个超声波元器件,一个用于发射,一个用于接收。电路板上有四个引脚:VCC、GND、Trig(触发)、Echo(回应) 工作电压与电流

    2024年02月03日
    浏览(44)
  • STM32f103c8t6模板的搭建-基于正点例程

           笔者认为正点编写的官方例程结构较为整洁,可以便于后期的例程开发,如果开发者对于项目开发中芯片要求较高的话,有很多人会选择正点的开发板,但是通常大多数是stm32初学者会选择用价格更为便宜的c8t6来进行学习,而正点选用的教程开发板大多都是些RC、ZE、

    2024年02月06日
    浏览(66)
  • 基于STM32F103C8T6ADC检测交流电压

    上篇文章写了硬件部分的实现思路,通过采样电阻的到小电压后经过二级放大电路得到单片机可处理的交流电压,此文介绍了如何采用单片机采集交流电压以及stm32ADC外设的使用。首先是硬件电路部分。  电路没有采用核心板,而是直接将芯片焊接到主板上,采用type-c接口供

    2024年02月12日
    浏览(54)
  • 基于STM32F103C8T6的HC-06蓝牙通信

    如果朋友们 遇到了如下问题 ,可以仔细借鉴本文章和另一篇专门讲解 蓝牙通信问题 的文章,一定能够解决你在蓝牙通信时遇到的诸多困难 1.在调试蓝牙模块AT指令时无返回值 2.身边 无USB转TTL模块 可以直接调试蓝牙模块(本人就是由于无模块花了了整整一天才调试成功)

    2024年02月03日
    浏览(61)
  • 基于stm32f103c8t6的fft频率计

    之前项目中需要用到正弦信号的频率测量,也参考了几个大佬的博客(链接如下),但可能是由于stm32的型号不匹配,虽然也在网上查了一些需要修改的地方,但结果一直不太对,后来经过自己摸索结果终于对了,在这里给大家分享下,具体原理不在赘述。 参考的部分大佬博

    2024年02月14日
    浏览(62)
  • 基于stm32f103c8t6的定时器详解(持续更新)

    先声明:stm32f103c8t6中没有基本定时器、只有TIM1-TIM4:分别是高级定时器和通用定时器(对照下图请自行阅读stm32f103x的datasheet) 1、定时器功能:定时、输出比较、输入捕获、互补输出,其中基本定时器只有定时功能、通用定时器只没有互补输出功能、高级定时器具有所有功能

    2023年04月24日
    浏览(65)
  • HX711压力传感器(基于STM32F103C8T6)

    HX711模块是我们目前比较常见的压力传感器模块,主要的作用是用来做压力检测,重量监测等等。博主的这篇博文主要实现功能为,在对重量或者压力进行监测的同时,可以累加或者清零数值,在此基础上就可以对比如饮水量进行统计等等。 HX711模块是市面上比较常见的模块

    2024年02月11日
    浏览(53)
  • [STM32F103C8T6]基于stm32的循迹,跟随,避障智能小车

    目录 1.小车驱动主要是通过L9110S模块来驱动电机 motor.c 2.我们可以加入串口控制电机驱动(重写串口接收回调函数,和重定向printf) Uart.c main.c  3.点动功能 uart.c main.c 为什么使用的是HAL_Delay()要设置滴答定时器的中断优先级呢? 4.小车PWM调速,  6.跟随功能 7.避障功能 超声波测距

    2024年02月13日
    浏览(54)
  • 基于STM32F103C8T6的UAV飞控板硬件设计

    一、主控单元:         主控单元基于意法半导体公司的STM32F103C8T6单片机进行设计。STM32F103C8T6DE 内核为ARM Cortex-M3;最大主频:72MHz ;工作电压范围:2V~3.6V ;程序存储容量:64KB; 程序存储器类型:FLASH ;RAM总容量:20KB; GPIO端口数量:37 ;封装为LQFP-48;串行单线调试(

    2024年02月08日
    浏览(54)
  • 基于stm32f103c8t6及AS608-----指纹锁项目

              博主纯小白, 本文适合于初学者,大佬还请勿喷,欢迎提出意见,有纰漏之处将及时纠正。 在浅学了stmf103c8t6后,想着依据现在所拥有的知识和能力做一个小项目。 注:工程代码在文章末尾。 掌握C语言基础....这个最基础啦... 接触过类似单片机,稍微看得懂芯片

    2023年04月09日
    浏览(82)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包