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 成果展示

相关推荐
网易独家音乐人Mike Zhou3 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
zy张起灵4 小时前
48v72v-100v转12v 10A大功率转换电源方案CSM3100SK
经验分享·嵌入式硬件·硬件工程
PegasusYu6 小时前
STM32CUBEIDE FreeRTOS操作教程(九):eventgroup事件标志组
stm32·教程·rtos·stm32cubeide·free-rtos·eventgroup·时间标志组
lantiandianzi11 小时前
基于单片机的多功能跑步机控制系统
单片机·嵌入式硬件
文弱书生65611 小时前
输出比较简介
stm32
哔哥哔特商务网11 小时前
高集成的MCU方案已成电机应用趋势?
单片机·嵌入式硬件
跟着杰哥学嵌入式11 小时前
单片机进阶硬件部分_day2_项目实践
单片机·嵌入式硬件
电子科技圈11 小时前
IAR与鸿轩科技共同推进汽车未来
科技·嵌入式硬件·mcu·汽车
东芝、铠侠总代1361006839312 小时前
浅谈TLP184小型平面光耦
单片机·嵌入式硬件·物联网·平面
lantiandianzi12 小时前
基于单片机中医药柜管理系统的设计
单片机·嵌入式硬件