STM32项目---水质水位检测
1 项目简介
1.1 项目需求
本项目通过测量水体的TDS来反映水体的质量。并同时可以测量水位(水深)。
1.2 系统总体设计
2 硬件模块
2.1 硬件选型
- 水位测量模块
- TDS采集模块
- 外置ADC模块(ADS1115)
2.2 水位测量模块使用方法
- 测量原理
水位测量传感器本质上是一个压力测量传感器。压力的值和传感器产生的电压值是线性关系,压力的值和水深也是线性关系。
- 物理接线
SCK接PB12,是时钟信号。
OUT接PB13,是数据输出信号。
- 时序图
2.3 TDS采集模块使用方法
- 测量原理
TDS采集的原理就是当水中的导电粒子多时,导电性好,采集到的电压高;导电粒子少时导电性差,采集到的电压低。可以简单的认为水中杂质多时,导电粒子多,杂质少时导电粒子少。所以可以通过采集的电压高低来计算TDS的值。
- 物理接线
2.4 ADC模块ADS1115使用方法
内置ADC精度只有12位,我们这次选用的外置ADC模块中的ADS1115是16位精度,支持4路电压转换,而且支持单端和差分输入。
与STM32通讯使用I2C通用协议。
- 物理接线
- ADC模块的在I2C中的通讯地址
模块中的ADDR引脚的不同接法决定模块的地址(7位地址)。
具体操作读写时,需要在末尾再加1位表示读或写:0表示写,1表示读。
如果我们把ADDR接GND,则写地址是就是:0x48<<1,读地址就是(0x48<<1) + 1。
- ADC中的寄存器
一共提供了5个寄存器:
Pointer Register(指针寄存器)、Conversion Register (转换寄存器)、Config Register(配置寄存器)、Lo_thresh and Hi_thresh Registers(高低阈值寄存器)
-
- 指针寄存器
-
- 转换寄存器(读出的寄存器)
-
- 配置寄存器 (写入配置的寄存器)
将来我们会配置3个地方,其他的全部使用默认值。
(1)采集通道:配置为100,表示采集0通道。
(2)增益:配置为001,表示不增益。
(3)转换模式:配置为0,表示连续转换。
- 读写时序
3 软件架构及代码实现
调试模块
Common
- Debug.c
#include "Debug.h"
void Debug_Init(void)
{
Driver_USART1_Init();
Driver_USART1_Start();
}
- Debug.h
strrchr
: 用于查找一个字符在字符串中最后一次出现的位置。其中
...
和##__VA_ARGS__
表示可变参数,需要导入头文件stdarg.h
[%s:%d]
:表示对应文件名和行号,对应后面的__FILE__
和__LINE__
#ifndef __DEBUG_H
#define __DEBUG_H
#include "Driver_USART1.h"
#include "stdarg.h"
#include "string.h"
/**
* 使用条件定义来表示是否开启printf输出功能
* 如果#define DEBUG 表名开启debug功能 后续统一使用宏定义debug_printf()来输出调试信息
* 实际产品上线的时候 将#define DEBUG修改掉 else分支中会存在debug_printf()内容为空的宏定义
* 之前调用的debug_printf()都会统一失效为空
*/
#define DEBUG
#ifdef DEBUG
#define debug_init() Debug_Init()
// 拆分文件的路径加文件名称 只保留文件名称即可
// User\main.c => main.c
//加1是为了从 \ 后面开始打印,不要这个反斜杠
#define FILE_NAME strrchr(__FILE__,'\\') ? strrchr(__FILE__,'\\') + 1 : __FILE__
// 在打印debug信息的时候 先输出处于哪个文件的哪一行
// 拼接前缀字符串"[%s:%d]" 表示对应的文件名和行号
#define debug_printf(format,...) printf("[%s:%d]" format ,FILE_NAME,__LINE__,##__VA_ARGS__)
#define debug_printfln(format,...) printf("[%s:%d]" format "\n",FILE_NAME,__LINE__,##__VA_ARGS__)
#else
#define debug_init() // 相同的宏定义名称 内容留空
#define debug_printf(format,...) // 相同的宏定义名称 内容留空
#define debug_printfln(format,...) // 相同的宏定义名称 内容留空
#endif
void Debug_Init(void);
#endif
- Delay.c
LOAD:先装载
VAL:写任意值清空
CTRL:配置
#include "Delay.h"
void Delay_us(uint16_t us)
{
/* 1. 装载需要计数的值 */
SysTick->LOAD = 72 * us;
/* 2. 清空原本val的值 */
SysTick->VAL = 0;
/* 3. 配置时钟源 关闭中断 启动定时器 */
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE;
SysTick->CTRL &= ~SysTick_CTRL_TICKINT;
SysTick->CTRL |= SysTick_CTRL_ENABLE;
/* 4. 等待定时器计数 */
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG) == 0)
;
/* 5. 关闭定时器 */
SysTick->CTRL &= ~SysTick_CTRL_ENABLE;
}
void Delay_ms(uint16_t ms)
{
while (ms--)
{
Delay_us(1000);
}
}
void Delay_s(uint16_t s)
{
while (s--)
{
Delay_ms(1000);
}
}
Driver
- Driver_USART1.c
#include "Driver_USART1.h"
/* 初始化串口通信 */
void Driver_USART1_Init(void)
{
/* 1. 开时钟 开引脚对应的时钟 PA */
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
/* 2. 配置引脚模式 */
// PA9 TX = 复用推挽输出 1011 PA10 RX = 浮空输入 0100
GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9);
GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
GPIOA->CRH |= GPIO_CRH_CNF10_0;
GPIOA->CRH &= ~(GPIO_CRH_CNF10_1 | GPIO_CRH_MODE10);
/* 3. 配置USART1的寄存器 */
// 3.1 配置波特率
USART1->BRR |= 0X271;
// 3.2 字长 停止位长度 关闭校验
USART1->CR1 &= ~USART_CR1_M;
USART1->CR2 &= ~USART_CR2_STOP;
USART1->CR1 &= ~USART_CR1_PCE;
// 3.3 开启收发功能
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE;
}
/* 启动串口通信 */
void Driver_USART1_Start(void)
{
// 总开关开启
USART1->CR1 |= USART_CR1_UE;
}
/* 停止串口通信 */
void Driver_USART1_Stop(void)
{
// 总开关关闭
USART1->CR1 &= ~USART_CR1_UE;
}
/* 发送字节 */
void Driver_USART1_SendChar(uint8_t ch)
{
/* 1. 判断标志位 */
while ((USART1->SR & USART_SR_TXE) == 0)
;
/* 2. 将字节数据写入到DR寄存器即为发送 */
USART1->DR = ch;
}
/* 发送字符串 */
void Driver_USART1_SendString(uint8_t *str, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
{
Driver_USART1_SendChar(str[i]);
}
}
/* 使用fputc重新原先printf中的调用 重定向到USART1的串口通信 实现printf打印字符串的功能 */
int fputc(int ch,FILE *file)
{
Driver_USART1_SendChar((int)ch);
return ch;
}
显示模块
Driver
- Driver_FSMC.c
#include "Driver_FSMC.h"
/**
* @description: 对FSMC用到的各个 IO 端口进行配置
*/
static void Driver_FSMC_GPIO_Init(void)
{
/* 1 配置 A10 (PG0) 复用推挽输出CNY:10 50MHz速度 MODE:11*/
/* =============MODE=============== */
GPIOG->CRL |= GPIO_CRL_MODE0;
GPIOG->CRL |= GPIO_CRL_CNF0_1;
GPIOG->CRL &= ~GPIO_CRL_CNF0_0;
/*
2 数据端口 复用推挽输出
在实际应用中,即使数据线被配置为输出模式,FSMC控制器仍然能够管理数据线的方向,使其在需要时成为输入线。
这种自动切换是由FSMC控制器硬件管理的,不需要软件干预。
因此,即使GPIO配置为复用推挽输出,FSMC依然可以实现读取操作。
*/
/* =============MODE=============== */
GPIOD->CRL |= (GPIO_CRL_MODE0 |
GPIO_CRL_MODE1);
GPIOD->CRH |= (GPIO_CRH_MODE8 |
GPIO_CRH_MODE9 |
GPIO_CRH_MODE10 |
GPIO_CRH_MODE14 |
GPIO_CRH_MODE15);
GPIOE->CRL |= (GPIO_CRL_MODE7);
GPIOE->CRH |= (GPIO_CRH_MODE8 |
GPIO_CRH_MODE9 |
GPIO_CRH_MODE10 |
GPIO_CRH_MODE11 |
GPIO_CRH_MODE12 |
GPIO_CRH_MODE13 |
GPIO_CRH_MODE14 |
GPIO_CRH_MODE15);
/* =============CNF=============== */
GPIOD->CRL |= (GPIO_CRL_CNF0_1 |
GPIO_CRL_CNF1_1);
GPIOD->CRL &= ~(GPIO_CRL_CNF0_0 |
GPIO_CRL_CNF1_0);
GPIOD->CRH |= (GPIO_CRH_CNF8_1 |
GPIO_CRH_CNF9_1 |
GPIO_CRH_CNF10_1 |
GPIO_CRH_CNF14_1 |
GPIO_CRH_CNF15_1);
GPIOD->CRH &= ~(GPIO_CRH_CNF8_0 |
GPIO_CRH_CNF9_0 |
GPIO_CRH_CNF10_0 |
GPIO_CRH_CNF14_0 |
GPIO_CRH_CNF15_0);
GPIOE->CRL |= (GPIO_CRL_CNF7_1);
GPIOE->CRL &= ~(GPIO_CRL_CNF7_0);
GPIOE->CRH |= (GPIO_CRH_CNF8_1 |
GPIO_CRH_CNF9_1 |
GPIO_CRH_CNF10_1 |
GPIO_CRH_CNF11_1 |
GPIO_CRH_CNF12_1 |
GPIO_CRH_CNF13_1 |
GPIO_CRH_CNF14_1 |
GPIO_CRH_CNF15_1);
GPIOE->CRH &= ~(GPIO_CRH_CNF8_0 |
GPIO_CRH_CNF9_0 |
GPIO_CRH_CNF10_0 |
GPIO_CRH_CNF11_0 |
GPIO_CRH_CNF12_0 |
GPIO_CRH_CNF13_0 |
GPIO_CRH_CNF14_0 |
GPIO_CRH_CNF15_0);
/* 3 其他控制端口 复用推挽输出 */
/* 3.1 PD4: 读控制引脚 PD5: 写控制引脚 */
GPIOD->CRL |= (GPIO_CRL_MODE4 |
GPIO_CRL_MODE5);
GPIOD->CRL |= (GPIO_CRL_CNF4_1 |
GPIO_CRL_CNF5_1);
GPIOD->CRL &= ~(GPIO_CRL_CNF4_0 |
GPIO_CRL_CNF5_0);
/* 3.2 PG12:NE4*/
GPIOG->CRH |= (GPIO_CRH_MODE12);
GPIOG->CRH |= (GPIO_CRH_CNF12_1);
GPIOG->CRH &= ~(GPIO_CRH_CNF12_0);
/* 3.3 背光引脚PB0:通用推挽输出*/
GPIOB->CRL |= GPIO_CRL_MODE0;
GPIOB->CRL &= ~GPIO_CRL_CNF0;
/* 3.4 重置引脚PG15:通用推挽输出*/
GPIOG->CRH |= GPIO_CRH_MODE15;
GPIOG->CRH &= ~GPIO_CRH_CNF15;
}
/**
* @description: 初始化
* @return {*}
*/
void Driver_FSMC_Init(void)
{
/* 1 开启时钟 */
/* 1.1 开启FSMC时钟 */
RCC->AHBENR |= RCC_AHBENR_FSMCEN;
/* 1.2 开启用到的 GPIO 时钟:PD PE PF PG */
RCC->APB2ENR |= (RCC_APB2ENR_IOPDEN |
RCC_APB2ENR_IOPBEN |
RCC_APB2ENR_IOPEEN |
RCC_APB2ENR_IOPFEN |
RCC_APB2ENR_IOPGEN);
/* 1.3 AFIO 时钟 */
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
/* 3 配置 FSMC 各个端口的输入输出模式 */
Driver_FSMC_GPIO_Init();
/* 4. 配置FSMC参数 */
/* 4.1 存储器块使能 根据接线图可知是 Bank1的 3区 1区对应0 2区对应2 3区对应4 4区对应6 (BCR寄存器)*/
FSMC_Bank1->BTCR[6] |= FSMC_BCR4_MBKEN;
/* 4.2 设置存储器类型 00: SRAM ROM 01:PSRAM 10:NOR闪存*/
FSMC_Bank1->BTCR[6] &= ~FSMC_BCR4_MTYP;
/* 4.3 闪存访问使能: 禁止访问闪存 */
FSMC_Bank1->BTCR[6] &= ~FSMC_BCR4_FACCEN;
/* 4.4 地址和数据总线复用: 不复用 */
FSMC_Bank1->BTCR[6] &= ~FSMC_BCR4_MUXEN;
/* 4.5 数据总线宽度 00:8位 01:16位*/
FSMC_Bank1->BTCR[6] &= ~FSMC_BCR4_MWID_1;
FSMC_Bank1->BTCR[6] |= FSMC_BCR4_MWID_0;
/* 4.6 写使能 */
FSMC_Bank1->BTCR[6] |= FSMC_BCR4_WREN;
/* 5. 配置SRAM的时序参数 BTCR[7] 访问bank1 4区对应的BTR寄存器 */
/* 5.1 地址建立时间(时钟周期) 0:表示一个时钟周期。 对同步读写来说无效,永远是1个时钟周期*/
FSMC_Bank1->BTCR[7] &= ~FSMC_BTR4_ADDSET;
/* 5.2 地址保持时间(时钟周期) 对同步读写来说无效,永远是1个时钟周期*/
FSMC_Bank1->BTCR[7] &= ~FSMC_BTR4_ADDHLD;
/* 5.3 数据保持时间(时钟周期)数据在总线上的停留时间 */
FSMC_Bank1->BTCR[7] &= ~FSMC_BTR4_DATAST; /* 对应的位置位0 */
FSMC_Bank1->BTCR[7] |= (30 << 8); /* 设置为1个us. 72个时钟周期 */
/* 5.4 设置访问模式为A */
FSMC_Bank1->BTCR[7] &= ~FSMC_BTR4_ACCMOD;
}
Inf
- Inf_LCD.h
#ifndef __INF_LCD_H__
#define __INF_LCD_H__
#include "Driver_FSMC.h"
#include "Delay.h"
#define SRAM_BANK4 0x6C000000
#define LCD_AX 10 // Inf_LCD的地址线,我们连接的时A10
#define LCD_ADDR_CMD ((__IO uint16_t *)SRAM_BANK4) // 写命令地址
#define LCD_ADDR_DATA ((__IO uint16_t *)(SRAM_BANK4 + (1 << (LCD_AX + 1)))) // 数据地址(计算地址的时候要左移一位)
/* 常见颜色 */
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 // 棕色
#define BRRED 0XFC07 // 棕红色
#define GRAY 0X8430 // 灰色
void Inf_LCD_Init(void);
uint32_t Inf_LCD_ReadId(void);
void Inf_LCD_ClearAll(uint16_t color);
void Inf_LCD_SetArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void Inf_LCD_FillColor(uint32_t nums, uint16_t color);
void Inf_LCD_DisplayAsciiChar(uint16_t x,uint16_t y,uint16_t h,uint8_t ch,uint16_t color,uint16_t bcolor);
void Inf_LCD_DisplayString(uint16_t x,uint16_t y,uint8_t * str,uint16_t color,uint16_t bcolor);
void Inf_LCD_DisplayChinese(uint16_t x,uint16_t y,uint8_t index,uint16_t color,uint16_t bcolor);
void Inf_LCD_DisplayImage(uint16_t x,uint16_t y);
#endif /* __INF_LCD_H__ */
- Inf_LCD.c
#include "Inf_LCD.h"
#include "Inf_LCD_Font.h"
/* 发送命令 */
void Inf_LCD_WriteCmd(__IO uint16_t cmd)
{
*LCD_ADDR_CMD = cmd;
}
/* 发送数据 */
void Inf_LCD_WriteData(__IO uint16_t data)
{
*LCD_ADDR_DATA = data;
}
/* 读取数据 */
uint16_t Inf_LCD_ReadData(void)
{
return *LCD_ADDR_DATA;
}
/* 初始化寄存器的值 */
void Inf_LCD_RegConfig(void)
{
/* 1. 设置灰阶电压以调整TFT面板的伽马特性, 正校准。一般出厂就设置好了 */
Inf_LCD_WriteCmd(0xE0);
Inf_LCD_WriteData(0x00);
Inf_LCD_WriteData(0x07);
Inf_LCD_WriteData(0x10);
Inf_LCD_WriteData(0x09);
Inf_LCD_WriteData(0x17);
Inf_LCD_WriteData(0x0B);
Inf_LCD_WriteData(0x41);
Inf_LCD_WriteData(0x89);
Inf_LCD_WriteData(0x4B);
Inf_LCD_WriteData(0x0A);
Inf_LCD_WriteData(0x0C);
Inf_LCD_WriteData(0x0E);
Inf_LCD_WriteData(0x18);
Inf_LCD_WriteData(0x1B);
Inf_LCD_WriteData(0x0F);
/* 2. 设置灰阶电压以调整TFT面板的伽马特性,负校准 */
Inf_LCD_WriteCmd(0XE1);
Inf_LCD_WriteData(0x00);
Inf_LCD_WriteData(0x17);
Inf_LCD_WriteData(0x1A);
Inf_LCD_WriteData(0x04);
Inf_LCD_WriteData(0x0E);
Inf_LCD_WriteData(0x06);
Inf_LCD_WriteData(0x2F);
Inf_LCD_WriteData(0x45);
Inf_LCD_WriteData(0x43);
Inf_LCD_WriteData(0x02);
Inf_LCD_WriteData(0x0A);
Inf_LCD_WriteData(0x09);
Inf_LCD_WriteData(0x32);
Inf_LCD_WriteData(0x36);
Inf_LCD_WriteData(0x0F);
/* 3. Adjust Control 3 (F7h) */
/*LCD_WriteCmd(0XF7);
Inf_LCD_WriteData(0xA9);
Inf_LCD_WriteData(0x51);
Inf_LCD_WriteData(0x2C);
Inf_LCD_WriteData(0x82);*/
/* DSI write DCS command, use loose packet RGB 666 */
/* 4. 电源控制1*/
Inf_LCD_WriteCmd(0xC0);
Inf_LCD_WriteData(0x11); /* 正伽马电压 */
Inf_LCD_WriteData(0x09); /* 负伽马电压 */
/* 5. 电源控制2 */
Inf_LCD_WriteCmd(0xC1);
Inf_LCD_WriteData(0x02);
Inf_LCD_WriteData(0x03);
/* 6. VCOM控制 */
Inf_LCD_WriteCmd(0XC5);
Inf_LCD_WriteData(0x00);
Inf_LCD_WriteData(0x0A);
Inf_LCD_WriteData(0x80);
/* 7. Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
Inf_LCD_WriteCmd(0xB1);
Inf_LCD_WriteData(0xB0);
Inf_LCD_WriteData(0x11);
/* 8. Display Inversion Control (B4h) (正负电压反转,减少电磁干扰)*/
Inf_LCD_WriteCmd(0xB4);
Inf_LCD_WriteData(0x02);
/* 9. Display Function Control (B6h) */
Inf_LCD_WriteCmd(0xB6);
Inf_LCD_WriteData(0x0A);
Inf_LCD_WriteData(0xA2);
/* 10. Entry Mode Set (B7h) */
Inf_LCD_WriteCmd(0xB7);
Inf_LCD_WriteData(0xc6);
/* 11. HS Lanes Control (BEh) */
Inf_LCD_WriteCmd(0xBE);
Inf_LCD_WriteData(0x00);
Inf_LCD_WriteData(0x04);
/* 12. Interface Pixel Format (3Ah) */
Inf_LCD_WriteCmd(0x3A);
Inf_LCD_WriteData(0x55); /* 0x55 : 16 bits/pixel */
/* 13. Sleep Out (11h) 关闭休眠模式 */
Inf_LCD_WriteCmd(0x11);
/* 14. 设置屏幕方向和RGB */
Inf_LCD_WriteCmd(0x36);
Inf_LCD_WriteData(0x08);
Delay_ms(120);
/* 14. display on */
Inf_LCD_WriteCmd(0x29);
}
/* 重置LCD PG15*/
void Inf_LCD_Reset(void)
{
GPIOG->ODR &= ~GPIO_ODR_ODR15; // 低电平重置
Delay_ms(50);
GPIOG->ODR |= GPIO_ODR_ODR15; // 高电平正常
Delay_ms(50);
}
/* 给LCD开启背光 */
void Inf_LCD_BKOpen(void)
{
GPIOB->ODR |= GPIO_ODR_ODR0; // 高电平开启背光
}
/* 给LCD关闭背光 */
void Inf_LCD_BKClose(void)
{
GPIOB->ODR &= ~GPIO_ODR_ODR0; // 低电平关闭背光(最暗)
}
/* 初始化LCD */
void Inf_LCD_Init(void)
{
/* 0. 初始化FSMC */
Driver_FSMC_Init();
// 1. 重置LCD
Inf_LCD_Reset();
// 2. 开启背光
Inf_LCD_BKOpen();
// 3. 初始化寄存器
Inf_LCD_RegConfig();
}
/* 读取lcd的id,用来测试通讯是否正常 */
uint32_t Inf_LCD_ReadId(void)
{
uint32_t id = 0x0;
Inf_LCD_WriteCmd(0x04);
Inf_LCD_ReadData(); // 无用的数据,扔掉即可
id |= Inf_LCD_ReadData() << 16; // 制造商id
id |= Inf_LCD_ReadData() << 8; // 模块/驱动版本号id
id |= Inf_LCD_ReadData(); // 模块/驱动id
return id;
}
void Inf_LCD_SetArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
/*1 设置X的范围*/
Inf_LCD_WriteCmd(0x2a);
// 起始位置
Inf_LCD_WriteData(x >> 8);
Inf_LCD_WriteData(x & 0xff);
// 结束位置
Inf_LCD_WriteData((x + w - 1) >> 8);
Inf_LCD_WriteData((x + w - 1) & 0xff);
/*2 设置y的范围*/
Inf_LCD_WriteCmd(0x2b);
// 起始位置
Inf_LCD_WriteData(y >> 8);
Inf_LCD_WriteData(y & 0xff);
// 结束位置
Inf_LCD_WriteData((y + h - 1) >> 8);
Inf_LCD_WriteData((y + h - 1) & 0xff);
}
void Inf_LCD_FillColor(uint32_t nums, uint16_t color)
{
/*1 发送填充数据的命令*/
Inf_LCD_WriteCmd(0X2C);
/*2 循环写入*/
for (uint32_t i = 0; i < nums; i++)
{
Inf_LCD_WriteData(color);
}
}
void Inf_LCD_ClearAll(uint16_t color)
{
// 1 设置涂色区域
Inf_LCD_SetArea(0, 0, 320, 480);
// 2 填充颜色
Inf_LCD_FillColor(320 * 480, color);
}
void Inf_LCD_DisplayAsciiChar(uint16_t x, uint16_t y, uint16_t h, uint8_t ch, uint16_t color, uint16_t bcolor)
{
// 统一使用32高度的字符
/*1. 设置涂色区域*/
Inf_LCD_SetArea(x, y, h / 2, h);
/*2. 根据字模 填充颜色*/
Inf_LCD_WriteCmd(0x2c);
// 找到要展示的对应的字符
uint8_t index = ch - ' ';
for (uint32_t i = 0; i < 64; i++)
{
uint8_t tmp = ascii_3216[index][i];
for (uint8_t j = 0; j < 8; j++)
{
if (tmp & 0x01)
{
Inf_LCD_WriteData(color);
}
else
{
Inf_LCD_WriteData(bcolor);
}
tmp >>= 1;
}
}
}
void Inf_LCD_DisplayString(uint16_t x, uint16_t y, uint8_t *str, uint16_t color, uint16_t bcolor)
{
uint8_t i = 0;
uint8_t h = 32;
while (str[i] != '\0')
{
// 可以展示的字符
if (str[i] == '\n')
{
x = 0;
y += h;
}
else
{
// 展示真正的字符
// 如果展示不下也要换行
if ((x + h / 2) > 320)
{
x = 0;
y += h;
}
// 写字符
Inf_LCD_DisplayAsciiChar(x, y, h, str[i], color, bcolor);
x += h / 2;
}
i++;
}
}
void Inf_LCD_DisplayChinese(uint16_t x, uint16_t y, uint8_t index, uint16_t color, uint16_t bcolor)
{
// 设置区域 32 * 32
Inf_LCD_SetArea(x, y, 32, 32);
Inf_LCD_WriteCmd(0x2c);
for (uint16_t i = 0; i < 32 * 4; i++)
{
uint8_t tmp = chinese[index][i];
for (uint8_t j = 0; j < 8; j++)
{
if (tmp & 0x01)
{
Inf_LCD_WriteData(color);
}
else
{
Inf_LCD_WriteData(bcolor);
}
tmp >>= 1;
}
}
}
void Inf_LCD_DisplayImage(uint16_t x,uint16_t y)
{
//图片分辨率 206 54
Inf_LCD_SetArea(x,y,206,54);
//写入数据
Inf_LCD_WriteCmd(0x2c);
for (uint32_t i = 0; i < 206 * 54 * 2; i+= 2)
{
uint16_t color = (gImage_atguigu[i] << 8) | gImage_atguigu[i + 1];
Inf_LCD_WriteData(color);
}
}
- Inf_LCD_Font.h
展示图像的时候要确定好分辨率
App
- App_display.c
#include "App_display.h"
void App_Display_Init(void)
{
Inf_LCD_Init();
}
void App_Display_Back(void)
{
//最后背景为白色
Inf_LCD_ClearAll(WHITE);
//最上方中间位置展示Logo 206 54
Inf_LCD_DisplayImage((320 - 206)/2,10);
//在logo下方展示标题
for (uint8_t i = 0; i < 9; i++)
{
Inf_LCD_DisplayChinese(32*i + 16,70,i,GREEN,WHITE);
}
}
void App_Display_String(uint16_t x,uint16_t y,uint8_t * str)
{
Inf_LCD_DisplayString(x, y, str, BLUE, WHITE);
}
void App_Display_ClearString(void)
{
// 只清空110行往下的内容
Inf_LCD_SetArea(0, 110, 320, 370);
// 涂色白色
Inf_LCD_FillColor(320 * 370, WHITE);
}
水位测量模块
Driver
- Driver_GPIO.h
#ifndef __DRIVER_GPIO_H__
#define __DRIVER_GPIO_H__
#include "stm32f10x.h"
#include "Delay.h"
//时钟变换和读取out数据
#define SCK_HIGH (GPIOB->ODR |= GPIO_ODR_ODR12)
#define SCK_LOW (GPIOB->ODR &= ~GPIO_ODR_ODR12)
#define READ_OUT (GPIOB->IDR & GPIO_IDR_IDR13)
void Driver_GPIO_TM7711_Init(void);
/* 返回1表示被按下,返回0表示没有被按下*/
uint8_t Driver_GPIO_isKeyPressed(void);
#endif /* __DRIVER_GPIO_H__ */
- Driver_GPIO.c
#include "Driver_GPIO.h"
void Driver_GPIO_TM7711_Init(void)
{
/*1. PB12 PB13 PF8 打开时钟*/
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPFEN;
/*2. 配置引脚模式*/
//PB12 sck 通用推挽 0011
GPIOB->CRH |= GPIO_CRH_MODE12;
GPIOB->CRH &= ~GPIO_CRH_CNF12;
//PB13 OUT 浮空输入 0100
GPIOB->CRH |= GPIO_CRH_CNF13_0;
GPIOB->CRH &= ~(GPIO_CRH_CNF13_1 | GPIO_CRH_MODE13);
//PF8 按键 上拉输入 1000
GPIOF->CRH |= GPIO_CRH_CNF8_1;
GPIOF->CRH &= ~(GPIO_CRH_CNF8_0 | GPIO_CRH_MODE8);
//ODR中的值决定上/下拉
GPIOF->ODR |= GPIO_ODR_ODR8;
}
uint8_t Driver_GPIO_isKeyPressed(void)
{
while (GPIOF->IDR & GPIO_IDR_IDR8)
;
//一直等到被按下
Delay_ms(100);
if ((GPIOF->IDR & GPIO_IDR_IDR8) == 0)
{
//消抖完成,真的被按下
//等待抬起
while ((GPIOF->IDR & GPIO_IDR_IDR8) == 0)
;
return 1;
}
//消抖之后 确认没有被按下
return 0;
}
- Driver_SPI.h
#ifndef __DRIVER_SPI_H__
#define __DRIVER_SPI_H__
#include "stm32f10x.h"
#define CS_HIGH (GPIOC->ODR |= GPIO_ODR_ODR13)
#define CS_LOW (GPIOC->ODR &= ~GPIO_ODR_ODR13)
void Driver_SPI_Init(void);
void Driver_SPI_Start(void);
void Driver_SPI_Stop(void);
uint8_t Driver_SPI_SwapByte(uint8_t data);
#endif /* __DRIVER_SPI_H__ */
- Driver_SPI.c
#include "Driver_SPI.h"
void Driver_SPI_Init(void)
{
/* 1. PA PC SPI1 */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
/* 2. 配置引脚模式 */
// PA5 复用推挽输出 PA7 复用推挽输出 1011
GPIOA->CRL |= (GPIO_CRL_CNF5_1 | GPIO_CRL_CNF7_1 | GPIO_CRL_MODE5 | GPIO_CRL_MODE7);
GPIOA->CRL &= ~(GPIO_CRL_CNF5_0 | GPIO_CRL_CNF7_0);
// PA6 浮空输入 0100
GPIOA->CRL &= ~(GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6);
GPIOA->CRL |= (GPIO_CRL_CNF6_0);
// PC13通用推挽输出 0011
GPIOC->CRH |= GPIO_CRH_MODE13;
GPIOC->CRH &= ~GPIO_CRH_CNF13;
/* 3. 配置SPI的寄存器 */
// 3.1 主设备选择
SPI1->CR1 |= SPI_CR1_MSTR;
// 3.2 配置模式选择 0模式 00
SPI1->CR1 &= ~SPI_CR1_CPHA;
SPI1->CR1 &= ~SPI_CR1_CPOL;
// 3.3 波特率选择 4分频 001
SPI1->CR1 &= ~SPI_CR1_BR;
SPI1->CR1 |= SPI_CR1_BR_0;
// 3.4 先发送高位
SPI1->CR1 &= ~SPI_CR1_LSBFIRST;
// 3.5 软件从设备管理
SPI1->CR1 |= SPI_CR1_SSM;
SPI1->CR1 |= SPI_CR1_SSI;
// 3.6 选择8位
SPI1->CR1 &= ~SPI_CR1_DFF;
// 3.7 使能
SPI1->CR1 |= SPI_CR1_SPE;
}
void Driver_SPI_Start(void)
{
// 片选
CS_LOW;
}
void Driver_SPI_Stop(void)
{
CS_HIGH;
}
uint8_t Driver_SPI_SwapByte(uint8_t data)
{
// 硬件模式 => 有状态位 直接状态位判断
while ((SPI1->SR & SPI_SR_TXE) == 0)
;
// 发送数据
SPI1->DR = data;
// 判断读取数据存在
while ((SPI1->SR & SPI_SR_RXNE) == 0)
;
return SPI1->DR;
}
Inf
- Inf_W25q32.c ---- (Flash)
#include "Inf_W25q32.h"
void Inf_W25Q32_Init(void)
{
Driver_SPI_Init();
}
/*测试*/
void Inf_W25Q32_ReadID(uint8_t *id1,uint16_t *id2)
{
//1. 启动
Driver_SPI_Start();
//2. 输入读取ID的指令
Driver_SPI_SwapByte(0x9f);
//3. 接收数据
*id1 = Driver_SPI_SwapByte(0xff);
//高位优先
*id2 |= Driver_SPI_SwapByte(0xff) << 8;
*id2 |= Driver_SPI_SwapByte(0xff);
//4. 停止
Driver_SPI_Stop();
}
/* 等待忙状态*/
void Inf_W25Q32_WaitNoBusy(void)
{
//1. 启动
Driver_SPI_Start();
//2. 发送查看状态的指令
Driver_SPI_SwapByte(0x05);
//3. 等待不忙 => 返回的字节最后一位为0 表示不忙
while (Driver_SPI_SwapByte(0xff) & 0x01)
;
//4. 停止
Driver_SPI_Stop();
}
/*等待写使能*/
void Inf_W25Q32_WriteEnable(void)
{
//1. 启动
Driver_SPI_Start();
//2. 发送写使能的指令
Driver_SPI_SwapByte(0x06);
//3. 停止
Driver_SPI_Stop();
}
/*擦除扇区*/
void Inf_W25Q32_Sector_Erase(uint32_t addr)
{
//1. 等待忙状态
Inf_W25Q32_WaitNoBusy();
//2. 开启写使能
Inf_W25Q32_WriteEnable();
//3. 擦除操作
//3.1 启动
Driver_SPI_Start();
//3.2 发送擦除指令
Driver_SPI_SwapByte(0x20);
//3.3 写入擦除地址
Driver_SPI_SwapByte(addr >> 16);
Driver_SPI_SwapByte((addr >> 8) & 0xff);
Driver_SPI_SwapByte((addr >> 0) & 0xff);
//3.4 关闭
Driver_SPI_Stop();
}
/*写入数据*/
void Inf_W25Q32_PageProgram(uint32_t addr,uint8_t data[],uint8_t len)
{
//1. 等待忙状态
Inf_W25Q32_WaitNoBusy();
//2. 开启写使能
Inf_W25Q32_WriteEnable();
//3. 开启
Driver_SPI_Start();
//4. 发送写指令
Driver_SPI_SwapByte(0x02);
//5. 写24位地址
Driver_SPI_SwapByte(addr >> 16);
Driver_SPI_SwapByte((addr >> 8) & 0xff);
Driver_SPI_SwapByte((addr >> 0) & 0xff);
//6. 依次写入数据
for (uint8_t i = 0; i < len; i++)
{
Driver_SPI_SwapByte(data[i]);
}
//7. 关闭
Driver_SPI_Stop();
}
/*读取数据*/
void Inf_W25Q32_ReadData(uint32_t addr,uint8_t * buff,uint8_t len)
{
//1. 等待忙状态
Inf_W25Q32_WaitNoBusy();
//2. 启动
Driver_SPI_Start();
//3. 发送读指令
Driver_SPI_SwapByte(0x03);
//4. 发送24位地址
Driver_SPI_SwapByte(addr >> 16);
Driver_SPI_SwapByte((addr >> 8) & 0xff);
Driver_SPI_SwapByte((addr >> 0) & 0xff);
//5. 读取数据
for (uint8_t i = 0; i < len; i++)
{
buff[i] = Driver_SPI_SwapByte(0xff);
}
//6. 停止
Driver_SPI_Stop();
}
- Inf_TM7711.c ---- (水位测量)
#include "Inf_TM7711.h"
void Inf_TM7711_Init(void)
{
Driver_GPIO_TM7711_Init();
}
//读取水位传感器的值
uint32_t Inf_TM7711_ReadV(void)
{
uint32_t data = 0;
/*1. 等待空闲信号*/
SCK_LOW;
Delay_us(5);
while (READ_OUT )
;
/*2. 循环24次读取电压值*/
for (uint8_t i = 0; i < 24; i++)
{
SCK_HIGH;
Delay_us(5);
SCK_LOW;
//每次的下降沿读取数据
data <<= 1;
if (READ_OUT)
{
data |= 0x01;
}
Delay_us(5);
}
/*3. 使用第25个空时钟信号*/
SCK_HIGH;
Delay_us(5);
SCK_LOW;
Delay_us(5);
//注意:这里data存的数据是用补码存储的 -> 转化为正数存储
return data ^ 0x800000;
}
App
- App_water_level.c
#include "App_water_level.h"
double a = 0;
double b = 0;
uint8_t ABbuff[50] = {0};
uint8_t ab_len[1] = {0};
/* 启动水位测量系统并完成校验*/
void App_Water_Level_Start(void)
{
//先初始化
Inf_TM7711_Init();
//初始化w25q32
Inf_W25Q32_Init();
//再校验
App_Water_Level_Calibrate();
}
void App_Water_Level_Calibrate(void)
{
//程序第一次启动的时候 肯定要校验
// Inf_W25Q32_Sector_Erase(0);
//程序第二次启动的时候 根据FLUSH中是否存在数据
uint8_t tmp = 0;
Inf_W25Q32_ReadData(0,&tmp,1);
if (tmp > 0 && tmp < 255)
{
//说明已经校验过了 不需要再次校验
//将AB的值从FLUSH中读取出来 赋值给变量
//strtod 将字符串 转化为double
//strtok 使用分隔符拆分字符串 第一次调用取前面的,再次调用取后面的
Inf_W25Q32_ReadData(1,ABbuff,tmp);
a = strtod(strtok((char *)ABbuff,"#"),NULL);
b = strtod(strtok(NULL,"#"),NULL);
return;
}
//校验水位传感器,计算 有= a * x + b 中的两个常量值
//(1)第一次测量 水位为0的电压值 y1 b = y1
//(2)第二次测量 水位为10的电压值 y2 y2 = a * 10 - b
//根据两次测量结果,能够算出 b = y1 a = (y2 - y1) / 10
App_Display_String(0,110,"Start Calibrate");
Delay_s(2);
App_Display_ClearString();
App_Display_String(0,110,"1.Please Don't put into water, then press the key1....");
while (Driver_GPIO_isKeyPressed() == 0)
;
//第一次测量校验
uint32_t y1 = Inf_TM7711_ReadV();
App_Display_ClearString();
App_Display_String(0,110,"2.Please put into water 10cm, then press the key1....");
while (Driver_GPIO_isKeyPressed() == 0)
;
//第二次测量校验
uint32_t y2 = Inf_TM7711_ReadV();
b = y1;
a = (y2 - y1) / 10;
App_Display_ClearString();
App_Display_String(0,110,"Calibrate is done!");
//将校验得到的ab值永久存储
sprintf((char *)ABbuff,"%.2f#%.2f",a,b);
ab_len[0] = strlen((char *)ABbuff);
//写入到W25Q32
// 在地址0的位置写入一个字节 => ab拼接的长度
//在位置1的位置开始写入 => ABbuff 写的就是ab_len
Inf_W25Q32_Sector_Erase(0);
Inf_W25Q32_PageProgram(0,ab_len,1);
Inf_W25Q32_PageProgram(1,ABbuff,ab_len[0]);
Delay_s(2);
}
/* 直接读取水位测量结果*/
double App_Read_Water_Level(void)
{
//y = a * x + b
uint32_t y = Inf_TM7711_ReadV();
return (y - b)/a;
}
水质检测模块
Driver
- Driver_I2C.c
#include "Driver_I2C.h"
void Driver_I2C_Init(void)
{
/*1. 打开时钟 PB I2C2*/
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
/*2. 配置引脚模式 PB10 PB11*/
GPIOB->CRH |= (GPIO_CRH_MODE10 | GPIO_CRH_CNF10);
GPIOB->CRH |= (GPIO_CRH_MODE11 | GPIO_CRH_CNF11);
/*3. 配置I2C*/
//3.1 选择使用I2C模式
I2C2->CR1 &= ~I2C_CR1_SMBUS;
//3.2 选择i2c时钟
I2C2->CR2 &= ~I2C_CR2_FREQ;
I2C2->CR2 |= 36;
//3.3 选择i2c标准模式
I2C2->CCR &= ~I2C_CCR_FS;
//3.4 标准模式分频系数 1 / 36us 8 ccr = 5us
I2C2->CCR |= 180;
//3.5 最大上升时间
I2C2->TRISE |= 37;
//使能
I2C2->CR1 |= I2C_CR1_PE;
}
void Driver_I2C_Start(void)
{
//置位启动信号 等待发送成功
I2C2->CR1 |= I2C_CR1_START;
while ((I2C2->SR1 & I2C_SR1_SB) == 0)
;
}
void Driver_I2C_Stop(void)
{
//置位停止信号
I2C2->CR1 |= I2C_CR1_STOP;
}
void Driver_I2C_ACK(void)
{
// 置位ACK
I2C2->CR1 |= I2C_CR1_ACK;
}
void Driver_I2C_NACK(void)
{
// 置位NACK
I2C2->CR1 &= ~I2C_CR1_ACK;
}
void Driver_I2C_SendADDR(uint8_t addr)
{
/*1. 等待发送寄存器为空 (可选操作)*/
// while ((I2C2->SR1 & I2C_SR1_TXE) == 0)
// ;
/*2. 发送地址*/
I2C2->DR = addr;
/*3. 等待地址发送成功*/
while ((I2C2->SR1 & I2C_SR1_ADDR) == 0)
;
/* 4. 清除标志位*/
I2C2->SR2;
}
void Driver_I2C_SendData(uint8_t data)
{
/*1. 等待发送寄存器为空 (必须操作)*/
while ((I2C2->SR1 & I2C_SR1_TXE) == 0)
;
/*2. 发送数据*/
I2C2->DR = data;
/*3. 等待数据发送成功*/
while ((I2C2->SR1 & I2C_SR1_BTF) == 0)
;
}
uint8_t Driver_I2C_ReadData(void)
{
/*1. 等待接收数据寄存器非空*/
while ((I2C2->SR1 & I2C_SR1_RXNE) == 0)
;
/*2. 读取数据*/
return I2C2->DR;
}
Inf
- Inf_ADS1115.h ---- (这些宏定义可以直接复制,不用再一个个手敲)
#ifndef __INF_ADS1115_H__
#define __INF_ADS1115_H__
#include "Driver_I2C.h"
#define ADS1115_ADDR_W (0x48 << 1)
#define ADS1115_ADDR_R ((0x48 << 1) + 1)
/* ADS1115的I2C地址 */
#define ADS1115_ADDRESS (0x48) // 1001 000 (ADDR = GND)
#define ADS1115_ADDRESS_W (0x90) // (0x48 << 1)
#define ADS1115_ADDRESS_R (0x91) // ((0x48 << 1) | 1)
#define ADS1115_CONFIG_DEFULT 0x8583
/* ADS1115指针寄存器 向低2位写入不同的值表示操作不同的其他寄存器 */
#define ADS1115_REG_POINTER_MASK (0x03) /* 指针寄存器掩码 */
#define ADS1115_REG_POINTER_CONVERT (0x00) /* 读取转换寄存器(ADC转换后的值) */
#define ADS1115_REG_POINTER_CONFIG (0x01) /* 读写配置寄存器 */
#define ADS1115_REG_POINTER_LOWTHRESH (0x02) /* Lo_thresh 寄存器 */
#define ADS1115_REG_POINTER_HITHRESH (0x03) /* Hi_thresh 寄存器 */
/* 配置寄存器 */
#define ADS1115_REG_CONFIG_OS_MASK (0x8000)
#define ADS1115_REG_CONFIG_OS_SINGLE (0x8000) // Write: Set to start a single-conversion
#define ADS1115_REG_CONFIG_OS_BUSY (0x0000) // Read: Bit = 0 when conversion is in progress
#define ADS1115_REG_CONFIG_OS_NOTBUSY (0x8000) // Read: Bit = 1 when device is not performing a conversion
#define ADS1115_REG_CONFIG_MUX_MASK (0x7000)
#define ADS1115_REG_CONFIG_MUX_DIFF_0_1 (0x0000) // Differential P = AIN0, N = AIN1 (default)
#define ADS1115_REG_CONFIG_MUX_DIFF_0_3 (0x1000) // Differential P = AIN0, N = AIN3
#define ADS1115_REG_CONFIG_MUX_DIFF_1_3 (0x2000) // Differential P = AIN1, N = AIN3
#define ADS1115_REG_CONFIG_MUX_DIFF_2_3 (0x3000) // Differential P = AIN2, N = AIN3
#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000) // Single-ended AIN0
#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000) // Single-ended AIN1
#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000) // Single-ended AIN2
#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000) // Single-ended AIN3
#define ADS1115_REG_CONFIG_PGA_MASK (0x0E00)
#define ADS1115_REG_CONFIG_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3
#define ADS1115_REG_CONFIG_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1
#define ADS1115_REG_CONFIG_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default)
#define ADS1115_REG_CONFIG_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4
#define ADS1115_REG_CONFIG_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8
#define ADS1115_REG_CONFIG_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16
#define ADS1115_REG_CONFIG_MODE_MASK (0x0100)
#define ADS1115_REG_CONFIG_MODE_CONTIN (0x0000) // Continuous conversion mode
#define ADS1115_REG_CONFIG_MODE_SINGLE (0x0100) // Power-down single-shot mode (default)
#define ADS1115_REG_CONFIG_DR_MASK (0x00E0)
#define ADS1115_REG_CONFIG_DR_128SPS (0x0000) // 128 samples per second
#define ADS1115_REG_CONFIG_DR_250SPS (0x0020) // 250 samples per second
#define ADS1115_REG_CONFIG_DR_490SPS (0x0040) // 490 samples per second
#define ADS1115_REG_CONFIG_DR_920SPS (0x0060) // 920 samples per second
#define ADS1115_REG_CONFIG_DR_1600SPS (0x0080) // 1600 samples per second (default)
#define ADS1115_REG_CONFIG_DR_2400SPS (0x00A0) // 2400 samples per second
#define ADS1115_REG_CONFIG_DR_3300SPS (0x00C0) // 3300 samples per second
#define ADS1115_REG_CONFIG_CMODE_MASK (0x0010)
#define ADS1115_REG_CONFIG_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default)
#define ADS1115_REG_CONFIG_CMODE_WINDOW (0x0010) // Window comparator
#define ADS1115_REG_CONFIG_CPOL_MASK (0x0008)
#define ADS1115_REG_CONFIG_CPOL_ACTVLOW (0x0000) // ALERT/RDY pin is low when active (default)
#define ADS1115_REG_CONFIG_CPOL_ACTVHI (0x0008) // ALERT/RDY pin is high when active
#define ADS1115_REG_CONFIG_CLAT_MASK (0x0004) // Determines if ALERT/RDY pin latches once asserted
#define ADS1115_REG_CONFIG_CLAT_NONLAT (0x0000) // Non-latching comparator (default)
#define ADS1115_REG_CONFIG_CLAT_LATCH (0x0004) // Latching comparator
#define ADS1115_REG_CONFIG_CQUE_MASK (0x0003)
#define ADS1115_REG_CONFIG_CQUE_1CONV (0x0000) // Assert ALERT/RDY after one conversions
#define ADS1115_REG_CONFIG_CQUE_2CONV (0x0001) // Assert ALERT/RDY after two conversions
#define ADS1115_REG_CONFIG_CQUE_4CONV (0x0002) // Assert ALERT/RDY after four conversions
#define ADS1115_REG_CONFIG_CQUE_NONE (0x0003) // Disable the comparator and put ALERT/RDY in high state (default)
/*初始化*/
void Inf_ADS1115_Init(void);
/*读取电压*/
double Inf_ADS1115_ReadV(void);
#endif /* __INF_ADS1115_H__ */
- Inf_ADS1115.c
#include "Inf_ADS1115.h"
/*初始化*/
void Inf_ADS1115_Init(void)
{
/*1.初始化底层驱动I2C*/
Driver_I2C_Init();
/*2. 配置ADS1115寄存器*/
//使用I2C驱动配置 需要 设备地址 和 字节地址 指针寄存器 -> 01 配置寄存器
//I2C写入数据流程
Driver_I2C_Start();
Driver_I2C_SendADDR(ADS1115_ADDR_W);
Driver_I2C_SendData(ADS1115_REG_POINTER_CONFIG);
//写入数据一次一个字节
uint16_t tmp = ADS1115_CONFIG_DEFULT;
//选择通道0,设置 A0 为模拟输入信号
tmp &= ~ ADS1115_REG_CONFIG_MUX_MASK;
tmp |= ADS1115_REG_CONFIG_MUX_SINGLE_0;
//4. 选择 4.096v
tmp &= ~ADS1115_REG_CONFIG_PGA_MASK;
tmp |= ADS1115_REG_CONFIG_PGA_4_096V;
//选择连续转换
tmp &= ~ADS1115_REG_CONFIG_MODE_MASK;
tmp |= ADS1115_REG_CONFIG_MODE_CONTIN;
Driver_I2C_SendData((tmp >> 8) & 0xff);
Driver_I2C_SendData((tmp >> 0) & 0xff);
Driver_I2C_Stop();
}
/*读取电压*/
double Inf_ADS1115_ReadV(void)
{
//使用I2C的流程读取电压
Driver_I2C_Start();
//发送设备地址
Driver_I2C_SendADDR(ADS1115_ADDR_W);
//发送字节地址 -> 指针寄存器
Driver_I2C_SendData(ADS1115_REG_POINTER_CONVERT);
//假写真读
Driver_I2C_Stop();
Driver_I2C_Start();
//发送设备地址
Driver_I2C_SendADDR(ADS1115_ADDR_R);
uint16_t tmp = 0;
Driver_I2C_ACK();
tmp |= Driver_I2C_ReadData() << 8;
tmp |= Driver_I2C_ReadData();
Driver_I2C_Stop();
//将二进制的值转换为测量电压
return (tmp * 4.096) / 32767;
}
App
- App_tds.c
#include "App_tds.h"
void App_TDS_Start(void)
{
Inf_ADS1115_Init();
}
double App_TDS_ReadTds(void)
{
double v = Inf_ADS1115_ReadV();
double v2 = v * v;
return 66.71 * v2 - 127.93 * v2 + 428.7 * v;
}