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
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__

c 复制代码
#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:配置

c 复制代码
#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
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
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
c 复制代码
#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
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
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
c 复制代码
#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
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
c 复制代码
#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
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)
c 复制代码
#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 ---- (水位测量)
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
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
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 ---- (这些宏定义可以直接复制,不用再一个个手敲)
c 复制代码
#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
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
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;
}

4 成果展示

相关推荐
cjy_Somnr3 小时前
keil5报错显示stm32的SWDIO未连接不能烧录
stm32·单片机·嵌入式硬件
Lay_鑫辰4 小时前
西门子诊断-状态和错误位(“轴”工艺对象 V1...3)
服务器·网络·单片机·嵌入式硬件·自动化
无垠的广袤6 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
雲烟8 小时前
嵌入式设备EMC安规检测参考
网络·单片机·嵌入式硬件
泽虞8 小时前
《STM32单片机开发》p7
笔记·stm32·单片机·嵌入式硬件
田甲8 小时前
【STM32】 数码管驱动
stm32·单片机·嵌入式硬件
up向上up9 小时前
基于51单片机垃圾箱自动分类加料机快递物流分拣器系统设计
单片机·嵌入式硬件·51单片机
纳祥科技18 小时前
Switch快充方案,内置GaN,集成了多个独立芯片
单片机
单片机日志19 小时前
【单片机毕业设计】【mcugc-mcu826】基于单片机的智能风扇系统设计
stm32·单片机·嵌入式硬件·毕业设计·智能家居·课程设计·电子信息
松涛和鸣20 小时前
从零开始理解 C 语言函数指针与回调机制
linux·c语言·开发语言·嵌入式硬件·排序算法