前言

参考文章/博主

MQ-2烟雾传感器模块功能实现STM32

MQ-2烟雾浓度传感器(STM32F103)

OLED选项界面制作

我的工程

github

准备阶段

  • 用到的模块

TB店铺:优信电子科技

模块 链接 管脚分配 备注
SHT30数字温湿度传感器 淘宝 PG11 -> SCL
PG12 -> SDA
GND -> GND
3.3V -> VCC
2.4-5.5V工作,I2C接口,温度精确度±3°C,湿度±0.3%RH
MQ-2烟雾传感器模块 淘宝 PC0(ADC123_IN10) -> A0
悬空 -> D0
GND -> GND
5V -> VCC
工作电压 5V
火光/火焰传感器模块 淘宝 PC1(ADC123_IN11) -> A0
悬空 -> D0
GND -> GND
3.3V -> VCC
工作电压 3.3V-5V
ESP-01s无线模块 淘宝 PC10(U3_TX) -> RXD
PC11(U3_RX) -> TXD
GND -> GND
3.3V -> VCC
3.3V -> EN
PC12 -> RST
悬空 -> IO0
悬空 -> IO2
3.0-3.6V工作,SPI Flash是1MB
STM32F103ZET6系统板 淘宝 / 3.3-5V工作
小车底盘4驱带编码器电机+驱动板 淘宝 /
0.96OLED模块 淘宝 PF6 -> SCL
PF7 -> SDA
GND -> GND
3.3V -> VCC
功耗正常显示时0.06W,I2C接口,3.3-5V工作,内部驱动IC是SSD1306
七路灰度光电寻迹模块 淘宝 PG0 -> R3
PG1 -> R2
PG2 -> R1
PG3 -> M
PG4 -> L1
PG5 -> L2
PG6 -> L3
5V -> VCC
GND -> GND
数字量(0,1),工作电压5V,可调电阻
HMI串口屏 淘宝 GND -> GND
PA2(U2_TX) -> RXD
PA3(U2_RX) -> TXD
5V -> +5V
K0系列,5V工作
VL53L0X激光测距模块 淘宝 5V -> VIN
GND -> GND
PF8 -> SCL
PF9 -> SDA
悬空 -> INT
悬空 -> XSH
I2C接口,最远测距2m,高精度下1.2m
  • 用到的器件
器件名称 数量 备注
XH 2.54mm 1x4P 弯插 1 线对板/线对线连接器
拨动开关 KH-SS12F17-G5 1 插件
2.54mm 1x2P弯插方针 1 /
LM2596T-5.0G 1 降压型 输入4.5V~40V 输出4.8V~5.2V 3A
AMS1117-3.3 1 线性稳压器(LDO),最大输入电压18V,输出电压3.3V,输出电流1A,封装是SOT-223
C0603封装100nF电容 6
C1206封装100nF电容 1 相同电容值的电容器,尺寸越大,其最大耐受电压也就越高,较大封装的电容器由于体积更大,通常能够承受更大的最大电流
C1206封装22uF电容 2
SLH1207S330MTT功率电感 1 33uH ±20%,额定电流3A,封装IND-SMD_L12.0-W12.0
1N5824二级管 1 30V 5A,封装是DO-201AD
C0603封装10uF电容 2
VLMTG1401-GS08 1 发光二极管/LED,正向电流20mA,颜色翠绿,0603封装
厚膜电阻 5.1kΩ ±1% 100mW 4 贴片电阻,0603封装
XL-1608SURC-06 1 发光二极管/LED,正向电流25mA,颜色红色,0603封装
XL-1608UBC-04 1 发光二极管/LED,正向电流25mA,颜色蓝色,0603封装
10kΩ ±1% 100mW贴片电阻 4 0603封装
TMB12A05电磁式有源蜂鸣器 1 插件,5V,给高低电平即可响
6x3.5x2.5mm 立贴 轻触开关 3 型号TSB008A2526A
贴片电阻 1kΩ 1 0603封装
2.54mm 1x4P排母 2 直插
2.54mm 1x4P排母 1 弯插
2.54mm 2x4P排母 1 直插
XH 2.5mm 1x9P直插 1 线对板/线对线连接器
2.54mm 1x6P排母 1 弯插
2.54mm 2x4P排针 3 直插
2.54mm 2x29P排母 2 直插
2.54mm 1x29P排针 4 直插
  • 绘制原理图

  • PCB以及3D效果图
  • 嘉立创免费下单即可,因为后面下单才知道丝印忘了画所以再下一单(免费2次刚刚好),5块黑色5块白色

  • 焊接

电阻,电容,电感不分正负,直接焊就行

LM2596T需要注意有圆点的那边是VCC输入

二极管的话没有白边的是正,有白边的是负,焊的时候注意不要看板子丝印,丝印我写反了,直接正极对照丝印-那插就行了

电感型号

工程建立+基本MX配置

Timer定时1ms,中断优先级设置低点,2左右

模块

SHT30

  • 模块资料

  • 硬件电路

  • MX配置

  • 程序编写
SHT30.h
#ifndef __SHT30_H
#define __SHT30_H

// SHT30 - SCL
#define SHT30_SCL_PORT GPIOG
#define SHT30_SCL_PIN GPIO_PIN_11
// SHT30- SDA Pin
#define SHT30_SDA_PORT GPIOG
#define SHT30_SDA_PIN GPIO_PIN_12

// 器件地址A(默认)(地址因为最后一位是读写位所以需要左移一位)
#define SHT30_ADDR (uint8_t)(0x44 << 1)
// 器件地址B
// #define SHT30_ADDR (uint8_t)(0x45 << 1)

// 读和写
#define SHT30_Write_CMD 0xFE
#define SHT30_Read_CMD 0x01

typedef struct
{
    float fTemperature;	// 温度
	uint8_t ucHumidity;	// 湿度
    void (*SHT30_Init)(void);   // SHT30初始化
    void (*SHT30_Cycle_Mode)(void); // SHT30周期性测量
}SHT30_t;


extern SHT30_t SHT30;

#endif
SHT30.c
/***************************************************************************
 * File: SHT30.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: SHT30温湿度传感器
 -----------------------------------
接线:
    PG11 ---> SCL
    PG12 ---> SDA
    GND ---> GND
    3.3V ---> VCC
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/*====================================variable definition declaration area BEGIN===================================*/

// 定义结构体
IIC_Data_t SHT30_IIC;

/*====================================variable definition declaration area   END===================================*/

/*====================================static function declaration area BEGIN====================================*/

static void SHT30_Init(void);
static void SHT30_Cycle_Mode(void);
static uint8_t CRC_8(uint8_t *Crc_ptr,uint8_t LEN);

/*====================================static function declaration area   END====================================*/
SHT30_t SHT30 = 
{
    0.0,
    0,
    SHT30_Init,
    SHT30_Cycle_Mode
};

/*
* @function: SHT30_Init
* @param: None
* @retval: None
* @brief: SHT30初始化
*/
static void SHT30_Init(void)
{
	IIC_Soft.IIC_Registered(&SHT30_IIC, SHT30_SCL_PORT, SHT30_SCL_PIN, SHT30_SDA_PORT, SHT30_SDA_PIN);	// 注册IIC设备
}

/*
* @function: SHT30_Cycle_Mode
* @param: None
* @retval: None
* @brief: 周期性测量模式
*/
static void SHT30_Cycle_Mode(void)
{
    uint8_t temp_array[6] = {0};
    uint16_t temp_uint = 0;
    float temp_float = 0;

    // 启动周期性测量
    IIC_Soft.IIC_Start(&SHT30_IIC);
    // 写操作
    IIC_Soft.IIC_Write_Byte(&SHT30_IIC, SHT30_ADDR & SHT30_Write_CMD);
    IIC_Soft.IIC_Write_Byte(&SHT30_IIC, 0x27); // High repeat , mps = 10
    IIC_Soft.IIC_Write_Byte(&SHT30_IIC, 0x37);

    Timer6.SHT30_Measure_Timeout = 0;
    // 发送接收数据命令
    do
    {
        if (Timer6.SHT30_Measure_Timeout >= TIMER6_2S) // 2s内没获取到数据,退出等待
        {
            break;
        }    

        IIC_Soft.IIC_Start(&SHT30_IIC);
        IIC_Soft.IIC_Write_Byte(&SHT30_IIC, SHT30_ADDR & SHT30_Write_CMD);
        // 0xE000是向SHT30取数据的指令,主机发送该指令后开始读取SHT30的温湿度数据
        IIC_Soft.IIC_Write_Byte(&SHT30_IIC, 0xE0);
        IIC_Soft.IIC_Write_Byte(&SHT30_IIC, 0x00);
        // 重新发送起始信号,往SHT30发送地址加读取数据指令
        IIC_Soft.IIC_Start(&SHT30_IIC);
    } while (IIC_Soft.IIC_Write_Byte(&SHT30_IIC, SHT30_ADDR | SHT30_Read_CMD) == NACK); // 0则退出

    // 开始接收测量数据,并计算
    if (Timer6.SHT30_Measure_Timeout < TIMER6_2S)
    {
        temp_array[0] = IIC_Soft.IIC_Read_Byte(&SHT30_IIC, ACK);
        temp_array[1] = IIC_Soft.IIC_Read_Byte(&SHT30_IIC, ACK);
        temp_array[2] = IIC_Soft.IIC_Read_Byte(&SHT30_IIC, ACK);
        temp_array[3] = IIC_Soft.IIC_Read_Byte(&SHT30_IIC, ACK);
        temp_array[4] = IIC_Soft.IIC_Read_Byte(&SHT30_IIC, ACK);
        temp_array[5] = IIC_Soft.IIC_Read_Byte(&SHT30_IIC, NACK);
        IIC_Soft.IIC_Stop(&SHT30_IIC);

        // 计算温度,精度0.1
        if (CRC_8(temp_array, 2) == temp_array[2]) // CRC-8 校验
        {
            //  取出16位的温度值
            temp_uint = temp_array[0] * 256 + temp_array[1];
            //  根据手册公式计算,为了精度,计算数值先*100
            temp_float = ((float)temp_uint) * 0.267032 - 4500;
            //  再除以100,得到正常温度值
            SHT30.fTemperature = temp_float * 0.01;
        }

        // 计算湿度,精度1%RH
        if (CRC_8(&temp_array[3], 2) == temp_array[5]) // CRC-8 校验
        {
            // 取出16位的湿度值
            temp_uint = temp_array[3] * 256 + temp_array[4];
            // 根据手册公式计算
            temp_float = ((float)temp_uint) * 0.152590;
            temp_float = temp_float * 0.01;
            // 除以100,得到正常湿度值
            SHT30.ucHumidity = (unsigned char)temp_float;
        }
    }
}

/*
* @function: CRC_8
* @param: Crc_ptr -> 校验数据首地址 LEN -> 校验数据长度
* @retval: None
* @brief: CRC-8校验
*/
static uint8_t CRC_8(uint8_t *Crc_ptr,uint8_t LEN)
{
	uint8_t CRC_Value = 0xFF;
	uint8_t i = 0,j = 0;

	for(i=0; i<LEN; i++)
	{
		CRC_Value ^= *(Crc_ptr+i);
		for(j=0; j<8; j++)
		{
			if(CRC_Value & 0x80)
				CRC_Value = (CRC_Value << 1) ^ 0x31;
			else
				CRC_Value = (CRC_Value << 1);
		}
	}
	return CRC_Value;
}

ESP-01S

主要部分写在【协议通信学习】篇里

  • 创建自定义功能

ADC

  • MX配置

  • ADC程序编写
myADC.h
#ifndef __MYADC_H
#define __MYADC_H

typedef struct
{
    uint16_t usADC_Rx_Buff[2];    // [0]--CH10 [1]--CH11
    void (*ADC_Calibration_Start_DMA)(void);    // ADC校准+启动+DMA
}myADC_t;

extern myADC_t myADC;

#endif
myADC.c
/***************************************************************************
 * File: myADC.c
 * Author: Luckys.
 * Date: 2023/06/24
 * description: ADC
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/

static void ADC_Calibration_Start_DMA(void);

/*====================================static function declaration area   END====================================*/

myADC_t myADC = 
{
    {0},
    ADC_Calibration_Start_DMA
};

/*
* @function: ADC_Calibration_Start_DMA
* @param: None
* @retval: None
* @brief: ADC校准+启动+DMA
*/
static void ADC_Calibration_Start_DMA(void)
{
    HAL_ADCEx_Calibration_Start(&hadc1);    // ADC校准
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&myADC.usADC_Rx_Buff, 2);   // 开始ADC DMA转换
}

MQ-2

  • 模块资料
  • 硬件电路

  • MX配置

  • 程序编写

MQ_2.h
#ifndef __MQ_2_H
#define __MQ_2_H


typedef struct
{
    uint8_t ucMQ2_Value;
    void (*MQ2_Get_Value)(void);    // MQ2烟雾采集
}MQ2_t;

extern MQ2_t MQ2;

#endif
MQ_2.c
/***************************************************************************
 * File: MQ_2.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: MQ_2气体传感器
 -----------------------------------
接线:
    PC0(ADC123_IN10) ---> AO
    悬空 ---> DO
    GND ---> GND
    5V ---> VCC
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/

static void MQ2_Get_Value(void);

/*====================================static function declaration area   END====================================*/
MQ2_t MQ2 = 
{
    0,
    MQ2_Get_Value
};


/*
* @function: MQ2_Get_Value
* @param: None
* @retval: None
* @brief: MQ2烟雾采集
*/
static void MQ2_Get_Value(void)
{
    uint16_t Temp;

    Temp = myADC.usADC_Rx_Buff[0];

    MQ2.ucMQ2_Value = Temp * 99 / 4096; // 12位AD,把AD值转换成百分比0~99
    MQ2.ucMQ2_Value = MQ2.ucMQ2_Value >= 99 ? 99 : MQ2.ucMQ2_Value; // 最大值不能超过99,默认下10%以下是正常的
}

火光/火焰传感器

  • 模块资料
  • 可以检测火焰或者波长在 760 纳米~1100 纳米范围内的光源
  • 灵敏度可调(电位器)
  • 对火焰的探测距离:跟灵敏度和火焰强度有关,一般 1m 以内适用,传感器与火焰要保持一定距离,以免高温损坏传感器
  • 工作电压 3.3V-5V
  • 输出形式 — a:模拟量电压输出 b:数字开关量输出(0 和 1)

VCC 外接 3.3V-5V 电压(可以直接与 5v 单片机和 3.3v 单片机相连)

GND 外接 GND

DO 小板数字量输出接口(0 和 1)

AO 小板模拟量输出接口

模块在环境火焰光谱或者光源达不到设定阈值时, D0口输出高电平,当外界环境火焰光谱或者光源超过设定阈值时,模块 D0输出低电平,指示灯亮

模块数字量输出 D0 可以与单片机直接相连,通过单片机来检测高低电平,由此来检测环境的温度改变

将模块放置桌面上,在没有火焰光谱情况下,如板子开关指示灯亮,则调节调节蓝色电位器,直到开关指示灯灭,然后对着火焰传感器最前端约 30cm 处打开火机,会发现板上开关指示灯亮,然后然后熄灭打火机,则开关指示灯会灭

背后的理论是热的物体会发出红外辐射。对于火焰或火灾,这种辐射会很高。我们将使用红外光电二极管检测这种红外辐射。光电二极管的电导率将根据其检测到的红外辐射而变化。我们使用 LM393 来比较这种辐射,当达到阈值时,数字输出会发生变化。

  • 硬件电路

  • MX配置

  • 程序编写

Fire.h
#ifndef __FIRE_H
#define __FIRE_H

typedef struct
{
    float fFire_Value;    // 存储火焰传感器的值
    void (*Fire_Get_Value)(void);   // 火焰传感器采集
}Fire_t;


extern Fire_t Fire;

#endif
Fire.c
/***************************************************************************
 * File: Fire.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 火光/火焰传感器
 -----------------------------------
接线:
    PC1(ADC123_IN11) ---> AO
    悬空 ---> DO
    GND ---> GND
    3.3V ---> VCC
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/

static void Fire_Get_Value(void);

/*====================================static function declaration area   END====================================*/

Fire_t Fire = 
{
    0.0,
    Fire_Get_Value
};

/*
* @function: Fire_Get_Value
* @param: None
* @retval: None
* @brief: 火焰传感器采集
*/
static void Fire_Get_Value(void)
{
    uint16_t Temp;
    
    Temp = myADC.usADC_Rx_Buff[1];
    Fire.fFire_Value = Temp *3.3f / 4096; // 转换为电压0~3.3V,没有火时电压处于最大值
}

STM32F103ZET6系统板

  • 芯片资料

0.96OLED模块

  • 硬件电路

  • MX配置

  • 程序编写
OLED.h
#ifndef __OLED_H
#define __OLED_H

// OLED - SCL
#define OLED_SCL_PORT GPIOF
#define OLED_SCL_PIN GPIO_PIN_6
// OLED- SDA
#define OLED_SDA_PORT GPIOF
#define OLED_SDA_PIN GPIO_PIN_7

// OLED的IIC地址(SA0 = 0) --- 0111 1000
#define OLED_ADDR       0x78
// OLED参数(宽度,高度,页数量)
#define OLED_WIDTH      128
#define OLED_HEIGHT     64

// 字体大小
typedef enum
{
    ASCII_SIZE_16 = 16,
} ASCII_Size_t;

// 最大只能显示4行
typedef enum
{
    OLED_Line1 = 0,  
    OLED_Line2 = 2,
    OLED_Line3 = 4,
    OLED_Line4 = 6,
} OLED091_Line_t;

typedef struct
{
    void (*padString)(char *, int); // 补全空格
    void (*OLED_Init)(void);     // OLED初始化
    void (*OLED_Clear)(void);    // 清屏
    void (*OLED_Show_String)(uint8_t, OLED091_Line_t, const char*, ASCII_Size_t);       // OLED显示字符串
    void (*OLED_Show_CHN)(uint8_t, OLED091_Line_t, const char*);        // OLED显示单个汉字
}OLED_t;

extern OLED_t OLED;

#endif
OLED.c
/***************************************************************************
 * File: OLED.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 0.96寸OLED
 -----------------------------------
接线:
    PF6 ---> SCL
    PF7 ---> SDA
    GND ---> GND
    3.3V ---> VCC
 -----------------------------------
****************************************************************************/
#include "AllHead.h"
#include "Oled_Font.h"

/*====================================variable definition declaration area BEGIN===================================*/

// 定义注册IIC结构体
IIC_Data_t OLED_IIC;

/*====================================variable definition declaration area   END===================================*/

/*====================================static function declaration area BEGIN====================================*/

static void OLED_Init(void);     // OLED初始化
static void OLED_Clear(void);    // 清屏
static void OLED_Show_String(uint8_t, OLED091_Line_t, const char*, ASCII_Size_t);       // OLED显示字符串
static void OLED_Show_CHN(uint8_t, OLED091_Line_t, const char*);        // OLED显示单个汉字
static void padString(char *, int);

static void OLED_Set_Pos(uint8_t, uint8_t); // OLED设置坐标
static void OLED_Write_CMD(uint8_t);    // OLED写命令
static void OLED_Write_Data(uint8_t);    // OLED写数据

/*====================================static function declaration area   END====================================*/
OLED_t OLED =
{
    padString,
    OLED_Init,
    OLED_Clear,
    OLED_Show_String,
    OLED_Show_CHN
};

/*
* @function: OLED_Write_CMD
* @param: CMD -> 待写入命令
* @retval: None
* @brief: OLED写命令
*/
static void OLED_Write_CMD(uint8_t CMD)
{
    IIC_Soft.IIC_Start(&OLED_IIC);
    IIC_Soft.IIC_Write_Byte(&OLED_IIC, OLED_ADDR);   // R/W#=0
    IIC_Soft.IIC_Write_Byte(&OLED_IIC, 0x00);       // Co=0,D/C#=0
    IIC_Soft.IIC_Write_Byte(&OLED_IIC, CMD);
    IIC_Soft.IIC_Stop(&OLED_IIC);    
}

/*
* @function: OLED_Write_Data
* @param: Data -> 待写入数据
* @retval: None
* @brief: OLED写数据
*/
static void OLED_Write_Data(uint8_t Data)
{
    IIC_Soft.IIC_Start(&OLED_IIC);
    IIC_Soft.IIC_Write_Byte(&OLED_IIC, OLED_ADDR);        // R/W#=0
    IIC_Soft.IIC_Write_Byte(&OLED_IIC, 0x40);       // Co=0,D/C#=0
    IIC_Soft.IIC_Write_Byte(&OLED_IIC, Data);
    IIC_Soft.IIC_Stop(&OLED_IIC);
}

/*
* @function: OLED_Init
* @param: None
* @retval: None
* @brief: OLED初始化
*/
static void OLED_Init(void)
{
    IIC_Soft.IIC_Registered(&OLED_IIC, OLED_SCL_PORT, OLED_SCL_PIN, OLED_SDA_PORT, OLED_SDA_PIN);	// 注册IIC设备

    Public.Public_Delay_ms(200); // 上电延时
    OLED_Write_CMD(0xAE); // OLED休眠
    OLED_Write_CMD(0x00); // 设置低列地址
    OLED_Write_CMD(0x10); // 设置高列地址
    OLED_Write_CMD(0x40); // 设置起始地址线
    OLED_Write_CMD(0xB0); // set page address
    OLED_Write_CMD(0x81); // 设置对比度
    OLED_Write_CMD(0xFF); //--128
    OLED_Write_CMD(0xA1); // 0xa0左右反置 0xa1正常
    OLED_Write_CMD(0xA6); // normal / reverse
    OLED_Write_CMD(0xA8); // 设置多路复用(1 to 64)
    OLED_Write_CMD(0x3F); // 1/32 duty
    OLED_Write_CMD(0xC8); // Com scan direction
    OLED_Write_CMD(0xD3); // 设置显示的偏移映射内存计数器
    OLED_Write_CMD(0x00); //

    OLED_Write_CMD(0xD5); // 设置显示时钟分频比/振荡器频率
    OLED_Write_CMD(0x80); // 设置分频比例,时钟设置为100帧/秒

    OLED_Write_CMD(0xD8); // set area color mode off
    OLED_Write_CMD(0x05); //

    OLED_Write_CMD(0xD9); // 预充电时间
    OLED_Write_CMD(0xF1); // 预充电为15个脉冲,释放为1个脉冲

    OLED_Write_CMD(0xDA); // 引脚设置硬件配置
    OLED_Write_CMD(0x12); //

    OLED_Write_CMD(0xDB); // 设置VCOM电平
    OLED_Write_CMD(0x30); //
    // 唤醒
    OLED_Write_CMD(0x8D); // 设置电荷泵
    OLED_Write_CMD(0x14); // 开启电荷泵

    OLED_Write_CMD(0xAF); // OLED唤醒(AE是OLED休眠)

    OLED.OLED_Clear();    // 清屏      
}

/*
* @function: OLED_Clear
* @param: None
* @retval: None
* @brief: OLED清屏
*/
static void OLED_Clear(void)
{
    uint8_t Page, Seg;

    for (Page = 0; Page < 8; Page++)
    {
        OLED_Write_CMD(0xB0 + Page); // 一共8页(行)
        OLED_Write_CMD(0x00);        // 低
        OLED_Write_CMD(0x01);        // 高

        for (Seg = 0; Seg < 128; Seg++)
        {
            OLED_Write_Data(0x00);
        }
    }
}

/*
* @function     : OLED_Set_Pos
* @param        : Page -> 行 Seg -> 列
* @retval       : None
* @brief        : OLED设置坐标
*/
static void OLED_Set_Pos(uint8_t Page, uint8_t Seg)
{
    OLED_Write_CMD(0xB0 + Seg);
    OLED_Write_CMD(((Page & 0xF0) >> 4) | 0x10); // 高4位
    OLED_Write_CMD((Page & 0x0F));               // 低4位
}

/*
* @function: OLED_Show_String
* @param: x -> 列 y -> 行 p_Str -> 要显示的字符串 ch_size -> 字体大小
* @retval: None
* @brief: OLED显示字符串
*/
static void OLED_Show_String(uint8_t x, OLED091_Line_t y, const char *p_Str, ASCII_Size_t ch_size)
{
    uint8_t i = 0;
    uint8_t c = 0;

    if (ch_size == 16)
    {
        while (p_Str[i] != '\0')
        {
            c = p_Str[i++] - ' ';
            OLED_Set_Pos(x, y);
            for (uint8_t j = 0; j < 8; j++)
                OLED_Write_Data(ucASCII_16x8[c * 16 + j]);
            OLED_Set_Pos(x, y + 1);
            for (uint8_t j = 0; j < 8; j++)
                OLED_Write_Data(ucASCII_16x8[c * 16 + j + 8]);

            x += 8;
            if (x > 120)
            {
                x = 0;
                y += 2;
            }
        }
    }
}

/*
* @function: OLED_Show_CHN
* @param: x -> 列 y -> 行  p_Str -> 单个汉字字符串
* @retval: None
* @brief: // OLED显示单个汉字
*/
static void OLED_Show_CHN(uint8_t x, OLED091_Line_t y, const char *p_Str)
{
    uint16_t usCHN_Number; // 字库中汉字数量
    uint16_t usIndex;      // 字库中的汉字索引
    uint8_t i;

    // 统计汉字的位置
    usCHN_Number = sizeof(CHN_16x16) / sizeof(Oled_Font16x16_t);
    // 查找汉字的位置
    for (usIndex = 0; usIndex < usCHN_Number; usIndex++)
    {
        if ((CHN_16x16[usIndex].Index[0] == *p_Str) && (CHN_16x16[usIndex].Index[1] == *(p_Str + 1))) // 因为一个汉字占两个字节
        {
            OLED_Set_Pos(x, y);
            for (i = 0; i < 16; i++)
            {
                OLED_Write_Data(CHN_16x16[usIndex].CHN_code[i]);
            }

            OLED_Set_Pos(x, y + 1);
            for (i = 0; i < 16; i++)
            {
                OLED_Write_Data(CHN_16x16[usIndex].CHN_code[i + 16]);
            }
            break;
        }
    }
}

/*
* @function: Menu_Display
* @param: str -> 字串符 size -> 最大不能超过多少
* @retval: None
* @brief: 补全空格
*/
static __inline void padString(char *str, int size)
{
    int len = strlen(str);
    if (len >= size)
    {
        return; // 字符串已经够长了,不需要添加空格
    }
    for (int i = len; i < size; i++)
    {
        if (i < size - 1)
        {
            str[i] = ' '; // 在字符串末尾添加空格
        }
        else
        {
            str[i] = '\0'; // 添加字符串结束符
        }
    }
}

Oled_Font.h就省略了,跟之前驱动OLED文章一样

4驱带编码器电机

  • 模块资料

电机驱动板是TB6612FNG,电机型号是MG310直流减速电机,减速比20,电压范围7-13V,速度1.3m/s,编码器是13线霍尔

  • 引脚分配
STM32 IO 电机驱动 IO
3.3V STBY
  1. 后左轮
STM32 IO 电机驱动 IO
PD12(TIMER4_CH1) PWMB
PE2 BIN1
PE3 BIN2
  1. 后右轮
STM32 IO 电机驱动 IO
PD13(TIMER4_CH2) PWMA
PE4 AIN1
PE5 AIN2
  1. 前左轮
STM32 IO 电机驱动 IO
PD14(TIMER4_CH3) PWMA
PE0 AIN1
PE1 AIN2
  1. 前右轮
STM32 IO 电机驱动 IO
PD15(TIMER4_CH4) PWMB
PD6 BIN1
PD7 BIN2

然后AIN1设置为高电平,AIN2设置为低电平就正转,如果车的现象是反转就调转电平即可

  • 寻迹的几种情况

识别到黑线在中间 — 111 0 111

检测到终点线(也是起跑线) — 100 0 001

检测到十字路口 — 000 0 000

小车偏右程度1 — 110 0 111

小车偏左程度1 — 111 0 011

小车偏右程度2 — 110 1 111

小车偏左程度2 — 111 1 011

小车偏右程度3 — 100 1 111

小车偏左程度3 — 111 1 001

小车偏右程度4 — 101 1 111

小车偏左程度4 — 111 1 101

小车偏右程度5 — 001 1 111

小车偏左程度5 — 111 1 100

小车偏右程度6(最右!Car_Base_Speed_Status为最大值,减速最厉害) — 011 1 111

小车偏右程度6(最左!Car_Base_Speed_Status为最大值,减速最厉害) — 111 1 110

  • MX配置

  • 程序编写
Motor.h
#ifndef __MOTOR_H
#define __MOTOR_H

// 宏定义 管脚
/*后左*/
#define LATER_LEFT_PWMB GPIO_PIN_12
#define LATER_LEFT_BIN1 GPIO_PIN_2
#define LATER_LEFT_BIN2 GPIO_PIN_3
/*后右*/
#define LATER_RIGHT_PWMA GPIO_PIN_13
#define LATER_RIGHT_AIN1 GPIO_PIN_4
#define LATER_RIGHT_AIN2 GPIO_PIN_5
/*前左*/
#define FRONT_LEFT_PWMA GPIO_PIN_14
#define FRONT_LEFT_AIN1 GPIO_PIN_0
#define FRONT_LEFT_AIN2 GPIO_PIN_1
/*前右*/
#define FRONT_RIGHT_PWMB GPIO_PIN_15
#define FRONT_RIGHT_BIN1 GPIO_PIN_6
#define FRONT_RIGHT_BIN2 GPIO_PIN_7

typedef enum
{
    Motor_FRONT_LEFT = (uint8_t)0,  // 左前
    Motor_FRONT_RIGHT = (uint8_t)1, // 右前
    Motor_LATER_LEFT = (uint8_t)2,  // 左后
    Motor_LATER_RIGHT = (uint8_t)3, // 右后
}Motor_Mark_t;

typedef struct
{
    uint16_t usLater_Left_Duty; // 后左电机占空比
    uint16_t usLater_Right_Duty; // 后右电机占空比
    uint16_t usFront_Left_Duty; // 前左电机占空比
    uint16_t usFront_Right_Duty; // 前右电机占空比
    void (*Motor_Init)(void);
    void (*Motor_Front_Left_Set_Forward)(void); // 正转
    void (*Motor_Front_Right_Set_Forward)(void);
    void (*Motor_Later_Left_Set_Forward)(void);
    void (*Motor_Later_Right_Set_Forward)(void);

    void (*Motor_Front_Left_Set_Reverse)(void); // 反转
    void (*Motor_Front_Right_Set_Reverse)(void);
    void (*Motor_Later_Left_Set_Reverse)(void);
    void (*Motor_Later_Right_Set_Reverse)(void);   

    void (*Motor_Set_Duty)(Motor_Mark_t, uint8_t); // 设置占空比(速度)
	void (*Motor_Fre_And_Duty_compute)(void);   // 频率占空比计算(用于显示OLED)
    void (*Motor_Stop)(void);
    double (*Motor_Clamp)(double, double, double);   // 限幅 
}Motor_t;


extern Motor_t Motor;

#endif
Motor.c
/***************************************************************************
 * File: Motor.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 电机
 -----------------------------------
接线:
    后左轮:PD12 --- Timer4_CH1    PE2 --- BIN1 PE3 --- BIN2
    后右轮:PD13 --- Timer4_CH2    PE4 --- AIN1 PE5 --- AIN2
    前左轮:PD14 --- Timer4_CH3    PE0 --- AIN1 PE1 --- AIN2
    前右轮:PD15 --- Timer4_CH4    PD6 --- BIN1 PD7 --- BIN2
    STBY --- 3.3V    
频率:
    MX设置了ARR为7199,PSC为0  ---> 72000000 / (7199 + 1) / (0 + 1) = 10KHz    
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/

static void Motor_Init(void);

static void Motor_Front_Left_Set_Forward(void);
static void Motor_Front_Right_Set_Forward(void);
static void Motor_Later_Left_Set_Forward(void);
static void Motor_Later_Right_Set_Forward(void);

static void Motor_Front_Left_Set_Reverse(void);
static void Motor_Front_Right_Set_Reverse(void);
static void Motor_Later_Left_Set_Reverse(void);
static void Motor_Later_Right_Set_Reverse(void);

static void Motor_Set_Duty(Motor_Mark_t motor, uint8_t duty);
static void Motor_Fre_And_Duty_compute(void);
static void Motor_Stop(void);
static double Motor_Clamp(double value, double min_value, double max_value);

/*====================================static function declaration area   END====================================*/

Motor_t Motor = 
{
    0,
    0,
    0,
    0,
    Motor_Init,
    Motor_Front_Left_Set_Forward,
    Motor_Front_Right_Set_Forward,
    Motor_Later_Left_Set_Forward,
    Motor_Later_Right_Set_Forward,

    Motor_Front_Left_Set_Reverse,
    Motor_Front_Right_Set_Reverse,
    Motor_Later_Left_Set_Reverse,
    Motor_Later_Right_Set_Reverse,

    Motor_Set_Duty,
	Motor_Fre_And_Duty_compute,
    Motor_Stop,
    Motor_Clamp
};


/*
* @function: Motor_Init
* @param: None
* @retval: None
* @brief: 电机初始化
*/
static void Motor_Init(void)
{
    HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3);
    HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);
	
    // 初始化四个轮子正转
    Motor_Front_Left_Set_Forward();
    Motor_Front_Right_Set_Forward();
    Motor_Later_Left_Set_Forward();
    Motor_Later_Right_Set_Forward();
}

/*
* @function: Motor_Front_Left_Forward
* @param: None
* @retval: None
* @brief: 左前轮正转
*/
static void Motor_Front_Left_Set_Forward(void)
{
    HAL_GPIO_WritePin(GPIOE, FRONT_LEFT_AIN1, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOE, FRONT_LEFT_AIN2, GPIO_PIN_RESET);
}

/*
* @function: Motor_Front_Right_Set_Forward
* @param: None
* @retval: None
* @brief: 右前轮正转
*/
static void Motor_Front_Right_Set_Forward(void)
{
    HAL_GPIO_WritePin(GPIOD, FRONT_RIGHT_BIN1, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD, FRONT_RIGHT_BIN2, GPIO_PIN_RESET);    
}

/*
* @function: Motor_Later_Left_Set_Forward
* @param: None
* @retval: None
* @brief: 左后轮正转
*/
static void Motor_Later_Left_Set_Forward(void)
{
    HAL_GPIO_WritePin(GPIOE, LATER_LEFT_BIN1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOE, LATER_LEFT_BIN2, GPIO_PIN_SET);    
}

/*
* @function: Motor_Later_Right_Set_Forward
* @param: None
* @retval: None
* @brief: 右后轮正转
*/
static void Motor_Later_Right_Set_Forward(void)
{
    HAL_GPIO_WritePin(GPIOE, LATER_RIGHT_AIN1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOE, LATER_RIGHT_AIN2, GPIO_PIN_SET);     
}

/*
* @function: Motor_Front_Left_Set_Reverse
* @param: None
* @retval: None
* @brief: 左前轮反转
*/
static void Motor_Front_Left_Set_Reverse(void)
{
    HAL_GPIO_WritePin(GPIOE, FRONT_LEFT_AIN1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOE, FRONT_LEFT_AIN2, GPIO_PIN_SET);    
}

/*
* @function: Motor_Front_Right_Set_Reverse
* @param: None
* @retval: None
* @brief: 右前轮反转
*/
static void Motor_Front_Right_Set_Reverse(void)
{
    HAL_GPIO_WritePin(GPIOD, FRONT_RIGHT_BIN1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOD, FRONT_RIGHT_BIN2, GPIO_PIN_SET);    
}

/*
* @function: Motor_Later_Left_Set_Reverse
* @param: None
* @retval: None
* @brief: 左后轮反转
*/
static void Motor_Later_Left_Set_Reverse(void)
{
    HAL_GPIO_WritePin(GPIOE, LATER_LEFT_BIN1, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOE, LATER_LEFT_BIN2, GPIO_PIN_RESET);    
}

/*
* @function: Motor_Later_Right_Set_Reverse
* @param: None
* @retval: None
* @brief: 右后轮反转
*/
static void Motor_Later_Right_Set_Reverse(void)
{
    HAL_GPIO_WritePin(GPIOE, LATER_RIGHT_AIN1, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOE, LATER_RIGHT_AIN2, GPIO_PIN_RESET);     
}

/*
* @function: Motor_Stop
* @param: None
* @retval: None
* @brief: 电机停止
*/
static void Motor_Stop(void)
{
    HAL_GPIO_WritePin(GPIOE, FRONT_LEFT_AIN1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOE, FRONT_LEFT_AIN2, GPIO_PIN_RESET); 

    HAL_GPIO_WritePin(GPIOD, FRONT_RIGHT_BIN1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOD, FRONT_RIGHT_BIN2, GPIO_PIN_RESET);

    HAL_GPIO_WritePin(GPIOE, LATER_LEFT_BIN1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOE, LATER_LEFT_BIN2, GPIO_PIN_RESET); 

    HAL_GPIO_WritePin(GPIOE, LATER_RIGHT_AIN1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOE, LATER_RIGHT_AIN2, GPIO_PIN_RESET);     
}

/*
* @function: Motor_Set_Duty
* @param: motor -> 哪个电机 duty -> 占空比设置(范围0%~101%)
* @retval: None
* @brief: 设置电机占空比
*/
static void Motor_Set_Duty(Motor_Mark_t motor, uint8_t duty)
{
    switch(motor)
    {
        case Motor_FRONT_LEFT:
        {
            Motor.usFront_Left_Duty = (TIM4->ARR + 1) * (duty / 100.0f);
            TIM4->CCR3 = Motor.usFront_Left_Duty;
            break;
        }
        case Motor_FRONT_RIGHT:
        {
            Motor.usFront_Right_Duty = (TIM4->ARR + 1) * (duty / 100.0f);
            TIM4->CCR4 = Motor.usFront_Right_Duty;
            break;
        }
        case Motor_LATER_LEFT:
        {
            Motor.usLater_Left_Duty = (TIM4->ARR + 1) * (duty / 100.0f);
            TIM4->CCR1 = Motor.usLater_Left_Duty;
            break;
        }
        case Motor_LATER_RIGHT:
        {
            Motor.usLater_Right_Duty = (TIM4->ARR + 1) * (duty / 100.0f);
            TIM4->CCR2 = Motor.usLater_Right_Duty;
            break;
        }
        default:break;                        
    }
#if LOG_DEBUG
    printf("TIM4_CH1:%d  TIM4_CH2:%d\r\nTIM4_CH3:%d  TIM4_CH4:%d\r\n", TIM4->CCR1,TIM4->CCR2,TIM4->CCR3,TIM4->CCR4);
#endif
}

/*
* @function: Motor_Fre_And_Duty_compute
* @param: None
* @retval: None
* @brief: 实际电机频率占空比计算
*/
static inline void Motor_Fre_And_Duty_compute(void)
{
    uint16_t Timer4_fre = 0;
    float T4_CH1_Duty = 0, T4_CH2_Duty = 0, T4_CH3_Duty = 0, T4_CH4_Duty = 0;

    Timer4_fre = 72000000 / (TIM4->PSC + 1) / (TIM4->ARR + 1);  // 计算频率
    T4_CH1_Duty = ((float)TIM4->CCR1 / TIM4->ARR) * 100;    // 计算占空比
    T4_CH2_Duty = ((float)TIM4->CCR2 / TIM4->ARR) * 100;
    T4_CH3_Duty = ((float)TIM4->CCR3 / TIM4->ARR) * 100;
    T4_CH4_Duty = ((float)TIM4->CCR4 / TIM4->ARR) * 100;
    // 刷新
    sprintf((char *)Page6.OLED_Display_Buff[0], "Motor_Fre:%d", Timer4_fre);
    OLED.padString((char *)Page6.OLED_Display_Buff[0], 16);

    sprintf((char *)Page6.OLED_Display_Buff[1], "F_L:%.0f F_R:%.0f", T4_CH3_Duty, T4_CH4_Duty);
    OLED.padString((char *)Page6.OLED_Display_Buff[1], 16);

    sprintf((char *)Page6.OLED_Display_Buff[2], "L_L:%.0f L_R:%.0f", T4_CH1_Duty, T4_CH2_Duty);
    OLED.padString((char *)Page6.OLED_Display_Buff[2], 16);
#if LOG_DEBUG
    printf("CH1:%.0f  CH2:%.0f\r\n CH3:%.0f CH4:%.0f\r\n", T4_CH1_Duty, T4_CH2_Duty, T4_CH3_Duty, T4_CH4_Duty);
#endif
}

/*
* @function: Motor_Clamp
* @param: None
* @retval: None
* @brief: 限幅函数
*/
static double Motor_Clamp(double value, double min_value, double max_value)
{
    if (value < min_value)
    {
        return min_value;
    }
    else if (value > max_value)
    {
        return max_value;
    }
    return value;
}

HMI串口屏

  • 硬件电路

  • MX配置

  • 程序编写
HMI.h
#ifndef __HMI_H
#define __HMI_H

// HMI串口
#define huart_HMI	huart2

// HMI接收最大长度
#define HMI_Rec_Buffer_LENGTH	(uint8_t)20
// 键值信息是7个字节
#define Protocol_Data_LEN	(uint8_t)7

//显示屏页面
typedef enum
{
	Page_Main = (uint8_t)0x00,
	Page_Display = (uint8_t)0x01,
	Page_Step_Motor = (uint8_t)0x02,
} HMI_Page_t;

// 定义结构体类型
typedef struct
{
	HMI_Page_t Page;	// 显示屏页面
	uint8_t* pucRec_Buffer;	// 接收缓存
	uint8_t Page_Step_Motor_KEY_Flag;	// 按键标志位:用于区分按键中断与显示屏键值信息
	void (*HMI_Init)(void);	// HMI初始化
	void (*HMI_SendString)(uint8_t*);	// 发送字符串给HMI
	void (*HMI_Protocol)(void);	// 接口协议
    void (*HMI_Display)(void);  // 页面显示
}HMI_t;

extern HMI_t HMI;

#endif
HMI.c
/***************************************************************************
 * File: HMI.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: HMI串口屏
 -----------------------------------
接线:
    PA2(USART2_TX) ---> RXD
    PA3(USART2_RX) ---> TXD
    GND ---> GND
    5V ---> +5V
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/

static uint8_t ucHMI_Rec_Buffer[HMI_Rec_Buffer_LENGTH] = {0x00};
static uint8_t ucHMI_EndData[3] = {0xFF,0xFF,0xFF};

static void HMI_Init(void);
static void HMI_SendString(uint8_t*);
static void HMI_Protocol(void);
static void HMI_SendEndData(void);
static void HMI_Display(void);

static void Fun_Page_Main(void);
static void Fun_Page_Display(void);
static void Fun_Page_Step_Motor(void);

/*====================================static function declaration area   END====================================*/
HMI_t HMI = 
{
	Page_Main,
	ucHMI_Rec_Buffer,
	FALSE,
	HMI_Init,
	HMI_SendString,
	HMI_Protocol,
    HMI_Display
};

/*
* @function: HMI_Init
* @param: None
* @retval: None
* @brief: 屏幕初始化
*/
static void HMI_Init(void)
{
    __HAL_UART_ENABLE_IT(&huart_HMI, UART_IT_IDLE); // 使能串口空闲中断
    HAL_UART_Receive_DMA(&huart_HMI, HMI.pucRec_Buffer, (uint16_t)HMI_Rec_Buffer_LENGTH);   // 使能DMA接收

	HMI_SendEndData();
	// 显示屏默认显示主页面
	HMI.HMI_SendString((uint8_t*)"page 0");
}

/*
* @function: HMI_SendString
* @param: pucStr -> 指向待发送字符串首地址的指针
* @retval: None
* @brief: 发送字符串给HMI
*/
static void HMI_SendString(uint8_t* pucStr)
{
	HAL_UART_Transmit(&huart_HMI,pucStr,strlen((const char*)pucStr),100);
	HMI_SendEndData();
}

/*
* @function: xxx
* @param: None
* @retval: None
* @brief: 发送结束符
*/
static void HMI_SendEndData(void)
{
	//连续发送3个0xFF
	HAL_UART_Transmit(&huart_HMI,ucHMI_EndData,(uint8_t)3,0x0A);
}

/*
* @function: HMI_Protocol
* @param: None
* @retval: None
* @brief: 接口协议 - 处理HMI的键值信息
*/
static void HMI_Protocol(void)
{
    uint8_t Temp_Array[7] = {0x00};
    uint8_t i = 0, Index = 0;

    HAL_UART_DMAStop(&huart_HMI);   // 串口停止DMA接收

    for (i = 0; i < HMI_Rec_Buffer_LENGTH; i++) // 读取HMI缓存数据,共7字节起始值为0x65
    {
        if (0 == Index) // 检测键值起始数据0x65
        {
            if (*(HMI.pucRec_Buffer + i) != 0x65)
            {
                continue;
            }
        }
        Temp_Array[Index] = *(HMI.pucRec_Buffer + i);

        if (Protocol_Data_LEN == Index) // 已读取7字节
        {
            break;
        }
        Index++;
    }
    HAL_UART_Receive_DMA(&huart1, HMI.pucRec_Buffer, (uint16_t)20); // 串口开启DMA接收
    // 处理数据
    if (Protocol_Data_LEN == Index)
    {
        // 主页面的键值信息
        if (0x00 == Temp_Array[1])
        {
            // 控件t1弹起事件--数码管
            if ((0x02 == Temp_Array[2]) && (0x00 == Temp_Array[3]))
            {
                // 切换到数码管页面
                HMI.Page = Page_Display;
                // 操作...
            }
            // 控件t2弹起事件--步进电机
            if ((0x03 == Temp_Array[2]) && (0x00 == Temp_Array[3]))
            {
                // 切换到电机页面
                HMI.Page = Page_Step_Motor;
                // 操作...显示电机圈数
                // 显示电机速度
            }
        }
        // 数码管页面的键值信息
        if (0x01 == Temp_Array[1])
        {
            // 控件b0弹起事件--返回
            if ((0x08 == Temp_Array[2]) && (0x00 == Temp_Array[3]))
            {
                // 切换到主页面
                HMI.Page = Page_Main;
                // 操作...
            }
        }
        // 步进电机页面的键值信息
        if (0x02 == Temp_Array[1])
        {
            // 控件b2弹起事件--返回
            if ((0x0B == Temp_Array[2]) && (0x00 == Temp_Array[3]))
            {
                // 切换到主页面
                HMI.Page = Page_Main;
                // 操作...
                // 关闭步进电机
            }
            // 控件bt0弹起事件--开关
            if ((0x01 == Temp_Array[2]) && (0x00 == Temp_Array[3]))
            {
                HMI.Page_Step_Motor_KEY_Flag = TRUE;
                // 调用按键1按下函数(里面需要有下面这段,其他类似)
                if (FALSE == HMI.Page_Step_Motor_KEY_Flag)
                {
                    HMI.HMI_SendString((uint8_t *)"click bt0,1");
                    HMI.HMI_SendString((uint8_t *)"click bt0,0");
                }
                HMI.Page_Step_Motor_KEY_Flag = FALSE;
            }
            // 控件bt1弹起事件--正反
            if ((0x02 == Temp_Array[2]) && (0x00 == Temp_Array[3]))
            {
                HMI.Page_Step_Motor_KEY_Flag = TRUE;
                // 调用按键2按下函数(这里面会判断Page_Step_Motor_KEY_Flag是否为false,是就表示是单片机触发的不是触摸导致的,不是表示触摸了串口屏)
                if (FALSE == HMI.Page_Step_Motor_KEY_Flag)
                {
                    HMI.HMI_SendString((uint8_t *)"click bt1,1");
                    HMI.HMI_SendString((uint8_t *)"click bt1,0");
                }
                HMI.Page_Step_Motor_KEY_Flag = FALSE;
            }
            // 控件b0弹起事件--加速
            if ((0x03 == Temp_Array[2]) && (0x00 == Temp_Array[3]))
            {
                HMI.Page_Step_Motor_KEY_Flag = TRUE;
                // 调用按键3按下函数
                if (FALSE == HMI.Page_Step_Motor_KEY_Flag)
                {
                    HMI.HMI_SendString((uint8_t *)"click b0,1");
                    HMI.HMI_SendString((uint8_t *)"click b0,0");
                }
                HMI.Page_Step_Motor_KEY_Flag = FALSE;
            }
            // 控件b1弹起事件--减速
            if ((0x04 == Temp_Array[2]) && (0x00 == Temp_Array[3]))
            {
                HMI.Page_Step_Motor_KEY_Flag = TRUE;
                // 调用按键4按下函数
                if (FALSE == HMI.Page_Step_Motor_KEY_Flag)
                {
                    HMI.HMI_SendString((uint8_t *)"click b1,1");
                    HMI.HMI_SendString((uint8_t *)"click b1,0");
                }
                HMI.Page_Step_Motor_KEY_Flag = FALSE;
            }
        }
    }
}

/*
* @function: HMI_Display
* @param: None
* @retval: None
* @brief: HMI页面切换显示
*/
static void HMI_Display(void)
{
	//根据HMI智能串口屏的页面执行相应的功能
	switch(HMI.Page)
	{
		case Page_Main:       Fun_Page_Main();       break; //主页面
		case Page_Display:    Fun_Page_Display();    break; //数码管显示页面
		case Page_Step_Motor: Fun_Page_Step_Motor(); break; //单极性步进电机页面
		default: HMI.Page = Page_Main;
	}    
}

/*
* @function: Fun_Page_Main
* @param: None
* @retval: None
* @brief: 描述
*/
static void Fun_Page_Main(void)
{
    
}

/*
* @function: Fun_Page_Display
* @param: None
* @retval: None
* @brief: 描述
*/
static void Fun_Page_Display(void)
{
    
}

/*
* @function: Fun_Page_Step_Motor
* @param: None
* @retval: None
* @brief: 描述
*/
static void Fun_Page_Step_Motor(void)
{
    
}

七路灰度光电寻迹模块

  • 模块资料

推荐探头离地 15-20mm,不能低于1cm,需要先确定好高度后再去调节那个,调好再杜邦线接到单片机进行程序编写

5V供电状态下的参考电流值:七路 — 白光72mA 红光88mA

7个引脚设置为输入上拉模式即可

  • 硬件电路

  • MX配置

  • 程序编写
Track.h
#ifndef __TRACK_H
#define __TRACK_H

// 引脚定义
#define TRACK_R1 GPIO_PIN_2 
#define TRACK_R2 GPIO_PIN_1 
#define TRACK_R3 GPIO_PIN_0
#define TRACK_M GPIO_PIN_3 
#define TRACK_L1 GPIO_PIN_4
#define TRACK_L2 GPIO_PIN_5
#define TRACK_L3 GPIO_PIN_6

// 测试
#define SPEED_1 4
#define SSPEED_2 10
#define SPEED_3 15
#define SPEED_4 30

// 最大速度
#define MAX_SPEED 70
// 普通速度
#define MAX_COMMON_SPEED 30

typedef enum
{
    R1_Status = (uint8_t)0,
    R2_Status = (uint8_t)1,
    R3_Status = (uint8_t)2,
    M_Status = (uint8_t)3,
    L1_Status = (uint8_t)4,
    L2_Status = (uint8_t)5,
    L3_Status = (uint8_t)6,
}Track_Status_t;

typedef struct
{
    uint8_t Car_Stop_Flag;  // 到达终点线停止标志位
    uint8_t Prestop_Start_Flag;   // 开始检测终点线(起跑线)标志位
    signed char Car_Base_Speed_Status;    // 小车基础速度状态,值越小时,基础速度越大
	int Car_Error_Status;	// 小车的位置偏移量 误差(error),位置偏移越大,误差越大,偏移越小,误差越小(偏右是负偏左是正)
    uint8_t ucTrack_Status_Buff[7]; // 存储7路状态
    void (*Track_Read_Status)(void);    // 读取状态
    void (*Track_Read_Sensor)(void);    // 读取寻迹状态与偏离置标志位
    void (*Track_Handler)(void);    // 寻迹处理
    void (*Track_Straight_Line)(void);
}Track_t;

extern Track_t Track;


#endif
Track.c
/***************************************************************************
 * File: Track.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 光电灰度7路寻迹模块
 -----------------------------------
接线:
    PG0 ---> R3
    PG1 ---> R2
    PG2 ---> R1
    PG3 ---> M
    PG4 ---> L1
    PG5 ---> L2
    PG6 ---> L3   
    5V ---> VCC
    GND ---> GND
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/*====================================variable definition declaration area BEGIN===================================*/

uint8_t go_right_Flag = 0;  // 转右

/*====================================variable definition declaration area   END===================================*/

/*====================================static function declaration area BEGIN====================================*/

static void Track_Read_Status(void);
static void Track_Read_Sensor(void);
static void Track_Handler(void);
static void Track_Straight_Line(void);
/*====================================static function declaration area   END====================================*/
Track_t Track = 
{
    FALSE,
    FALSE,
    0,
	0,
    {0},
    Track_Read_Status,
    Track_Read_Sensor,
    Track_Handler,
    Track_Straight_Line
};

/*
* @function: Track_Read_Status
* @param: None
* @retval: None
* @brief: 读取寻迹灯状态
*/
static void Track_Read_Status(void)
{
    // 从左到右排序 传感器返回的数字信号依次存入
    Track.ucTrack_Status_Buff[L3_Status] = HAL_GPIO_ReadPin(GPIOG, TRACK_L3) ? 0 : 1;
    Track.ucTrack_Status_Buff[L2_Status] = HAL_GPIO_ReadPin(GPIOG, TRACK_L2) ? 0 : 1;
    Track.ucTrack_Status_Buff[L1_Status] = HAL_GPIO_ReadPin(GPIOG, TRACK_L1) ? 0 : 1;
    Track.ucTrack_Status_Buff[M_Status] = HAL_GPIO_ReadPin(GPIOG, TRACK_M) ? 0 : 1;
    Track.ucTrack_Status_Buff[R1_Status] = HAL_GPIO_ReadPin(GPIOG, TRACK_R1) ? 0 : 1;
    Track.ucTrack_Status_Buff[R2_Status] = HAL_GPIO_ReadPin(GPIOG, TRACK_R2) ? 0 : 1;
    Track.ucTrack_Status_Buff[R3_Status] = HAL_GPIO_ReadPin(GPIOG, TRACK_R3) ? 0 : 1;
#if LOG_DEBUG
    printf("R1-%d R2-%d R3-%d\r\nM-%d\r\nL1-%d L2-%d L3-%d\r\n\r\n", Track.ucTrack_Status_Buff[R1_Status],Track.ucTrack_Status_Buff[R2_Status],Track.ucTrack_Status_Buff[R3_Status],Track.ucTrack_Status_Buff[M_Status],Track.ucTrack_Status_Buff[L1_Status],Track.ucTrack_Status_Buff[L2_Status],Track.ucTrack_Status_Buff[L3_Status]);
#endif    
}

/*
* @function: Track_Handler
* @param: None
* @retval: None
* @brief: 寻迹处理
*/
static void Track_Handler(void)
{
    if (FALSE == Track.Car_Stop_Flag) // 停止标志位等于0表示小车正常向前行驶
    {
        // Motor.Motor_Front_Left_Set_Forward();
        // Motor.Motor_Front_Right_Set_Forward();
        // Motor.Motor_Later_Left_Set_Forward();
        // Motor.Motor_Later_Right_Set_Forward();
        Track.Track_Straight_Line();
    }
    else // 检测到终点线,此时小车刹车
    {
        /* 小车电机向后 */
        Motor.Motor_Front_Left_Set_Reverse();
        Motor.Motor_Front_Right_Set_Reverse();
        Motor.Motor_Later_Left_Set_Reverse();
        Motor.Motor_Later_Right_Set_Reverse();
        Public.Public_Delay_ms(80);
        Motor.Motor_Stop();
        while (1)
        {
            ;
        }
    }
}

/*
* @function: Track_Read_Sensor
* @param: None
* @retval: None
* @brief: 读取传感器偏离(识别到黑线返回数字信号低电平0,未识别到黑线返回高电平1)
*/
static void Track_Read_Sensor(void)
{
    Track_Read_Status();

    // 识别到黑线在中间 --- 111 0 111
    if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status]  == 1 && Track.ucTrack_Status_Buff[L1_Status]  == 1 &&
        Track.ucTrack_Status_Buff[M_Status] == 0 &&
        Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status]  == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    {
        Track.Car_Error_Status = 0;
        return;
    }   
    // 小车偏右程度1 --- 110 1 111
    else if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status] == 1 && Track.ucTrack_Status_Buff[L1_Status] == 0 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    {
        Track.Car_Error_Status = -1;
        Track.Car_Base_Speed_Status = 0;
        return;
    }  
    // 小车偏左程度1 --- 111 1 011
    else if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status] == 1 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 0 && Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    {
        Track.Car_Error_Status = 1;
        Track.Car_Base_Speed_Status = 0;
        return;
    } 
    // 小车偏右程度2(小幅度左转) --- 101 1 111
    else if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status] == 0 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    {
        Track.Car_Error_Status = -2;
        Track.Car_Base_Speed_Status = 2;
        return;
    } 
    // 小车偏左程度2(小幅度右转) --- 111 1 101
    else if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status] == 1 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 0 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    {
        Track.Car_Error_Status = 2;
        Track.Car_Base_Speed_Status = 2;
        return;
    } 
    // 小车偏右程度3(中幅度左转) --- 001 1 111
    else if (Track.ucTrack_Status_Buff[L3_Status] == 0 && Track.ucTrack_Status_Buff[L2_Status] == 0 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    {
        Track.Car_Error_Status = -3;
        Track.Car_Base_Speed_Status = 3;
        return;
    } 
    // 小车偏左程度3(中幅度右转) --- 111 1 100
    else if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status] == 1 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 0 && Track.ucTrack_Status_Buff[R3_Status] == 0)
    {
        Track.Car_Error_Status = 3;
        Track.Car_Base_Speed_Status = 3;
        return;
    } 
    // 全亮 --- 111 1 111     
    else if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status] == 1 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    {
        Track.Car_Stop_Flag = TRUE; // 停止标志位置1
        Track.Car_Error_Status = 4;
        return;
    } 
    // 全灭 --- 000 0 000   
    else if (Track.ucTrack_Status_Buff[L3_Status] == 0 && Track.ucTrack_Status_Buff[L2_Status] == 0 && Track.ucTrack_Status_Buff[L1_Status] == 0 &&
            Track.ucTrack_Status_Buff[M_Status] == 0 &&
            Track.ucTrack_Status_Buff[R1_Status] == 0 && Track.ucTrack_Status_Buff[R2_Status] == 0 && Track.ucTrack_Status_Buff[R3_Status] == 0)
    {
        // Track.Car_Stop_Flag = TRUE; // 停止标志位置1
        Track.Car_Error_Status = 5;
        return;
    }     
    // 小车偏右 --- 000 1 111
    else if (Track.ucTrack_Status_Buff[L3_Status] == 0 && Track.ucTrack_Status_Buff[L2_Status] == 0 && Track.ucTrack_Status_Buff[L1_Status] == 0 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    {
        Track.Car_Error_Status = -6;
        Track.Car_Base_Speed_Status = 3;
        return;
    }     
    // 小车偏左 --- 111 1 000
    else if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status] == 1 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
            Track.ucTrack_Status_Buff[M_Status] == 1 &&
            Track.ucTrack_Status_Buff[R1_Status] == 0 && Track.ucTrack_Status_Buff[R2_Status] == 0 && Track.ucTrack_Status_Buff[R3_Status] == 0)
    {
        Track.Car_Error_Status = 6;
        Track.Car_Base_Speed_Status = 3;
        return;
    }     
    // 特殊1--- 000 1 111
    // else if (Track.ucTrack_Status_Buff[L3_Status] == 0 && Track.ucTrack_Status_Buff[L2_Status] == 0 && Track.ucTrack_Status_Buff[L1_Status] == 0 &&
    //     Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    // {
    //     Track.Car_Error_Status = 5;
    //     Track.Car_Base_Speed_Status = 3;
    //     return;
    // }         
    // // 小车偏右程度6(最右!Car_Base_Speed_Status为最大值,减速最厉害) --- 011 1 111
    // else if (Track.ucTrack_Status_Buff[L3_Status] == 0 && Track.ucTrack_Status_Buff[L2_Status] == 1 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
    //         Track.ucTrack_Status_Buff[M_Status] == 1 &&
    //         Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 1)
    // {
    //     Track.Car_Error_Status = -11;
    //     Track.Car_Base_Speed_Status = 4;
    // }  
    // // 小车偏右程度6(最左!Car_Base_Speed_Status为最大值,减速最厉害) --- 111 1 110
    // else if (Track.ucTrack_Status_Buff[L3_Status] == 1 && Track.ucTrack_Status_Buff[L2_Status] == 1 && Track.ucTrack_Status_Buff[L1_Status] == 1 &&
    //         Track.ucTrack_Status_Buff[M_Status] == 1 &&
    //         Track.ucTrack_Status_Buff[R1_Status] == 1 && Track.ucTrack_Status_Buff[R2_Status] == 1 && Track.ucTrack_Status_Buff[R3_Status] == 0)
    // {
    //     Track.Car_Error_Status = 11;
    //     Track.Car_Base_Speed_Status = 4;
    // }                         
}

/*
* @function: Track_Straight_Line
* @para
m: None
* @retval: None
* @brief: 寻迹直线
*/
static void Track_Straight_Line(void)
{    
        Motor.usLater_Left_Duty = MAX_COMMON_SPEED;
        Motor.usFront_Left_Duty = MAX_COMMON_SPEED;
        Motor.usFront_Right_Duty = MAX_COMMON_SPEED;
        Motor.usLater_Right_Duty = MAX_COMMON_SPEED;    
    if (0 == Track.Car_Error_Status)    // 正常
    {
        Motor.usFront_Left_Duty = MAX_COMMON_SPEED;
        Motor.usFront_Right_Duty = MAX_COMMON_SPEED;
        Motor.usLater_Left_Duty = MAX_COMMON_SPEED;
        Motor.usLater_Right_Duty = MAX_COMMON_SPEED;
    }
    else if (1 == Track.Car_Error_Status)   // 偏左2
    {
        Motor.usFront_Left_Duty += SSPEED_2;
        Motor.usFront_Right_Duty -= SSPEED_2;
        Motor.usLater_Left_Duty += SSPEED_2;
        Motor.usLater_Right_Duty -= SSPEED_2;
    }     
    else if (-1 == Track.Car_Error_Status)   // 偏右2
    {
        Motor.usFront_Left_Duty -= SSPEED_2;
        Motor.usFront_Right_Duty += SSPEED_2;
        Motor.usLater_Left_Duty -= SSPEED_2;
        Motor.usLater_Right_Duty += SSPEED_2;
    }
    else if (2 == Track.Car_Error_Status)   // 小车偏左程度4(小幅度右转)
    {	
        Motor.usFront_Left_Duty += SPEED_3;
        Motor.usFront_Right_Duty -= SPEED_3;
        Motor.usLater_Left_Duty += SPEED_3;
        Motor.usLater_Right_Duty -= SPEED_3;		
    }     
    else if (-2 == Track.Car_Error_Status)   // 小车偏右程度4(小幅度左转)
    {
        Motor.usFront_Left_Duty -= SPEED_3; // SPEED_3
        Motor.usFront_Right_Duty += SPEED_3;
        Motor.usLater_Left_Duty -= SPEED_3;
        Motor.usLater_Right_Duty += SPEED_3;  
    }  
    else if (3 == Track.Car_Error_Status)   // 小车偏左程度4(中幅度右转)
    {
        Motor.usFront_Left_Duty += SPEED_4;   // += SPEED_4
        Motor.usFront_Right_Duty -= SPEED_4;  // -= SPEED_4
        Motor.usLater_Left_Duty += SPEED_4;
        Motor.usLater_Right_Duty -= SPEED_4;
        while(Track.ucTrack_Status_Buff[M_Status] != 0)
        {
            Public.Public_Delay_ms(1);
        }
    }     
    else if (-3 == Track.Car_Error_Status)   // 小车偏右程度4(中幅度左转)
    {
        Motor.usFront_Left_Duty -= SPEED_4;  // -= SPEED_4
        Motor.usFront_Right_Duty += SPEED_4;
        Motor.usLater_Left_Duty -= SPEED_4;
        Motor.usLater_Right_Duty += SPEED_4;
        while(Track.ucTrack_Status_Buff[M_Status] != 0)
        {
            Public.Public_Delay_ms(1);
        }
    } 
    else if (6 == Track.Car_Error_Status)   // 小车偏左程度
    {
        Motor.usFront_Left_Duty += SPEED_4;   // += SPEED_4
        Motor.usFront_Right_Duty -= SPEED_4;  // -= SPEED_4
        Motor.usLater_Left_Duty += SPEED_4;
        Motor.usLater_Right_Duty -= SPEED_4;
        while(Track.ucTrack_Status_Buff[M_Status] != 0)
        {
            Public.Public_Delay_ms(50);
        }
    }     
    else if (-6 == Track.Car_Error_Status)   // 小车偏右程度
    {
        Motor.usFront_Left_Duty -= SPEED_4;  // -= SPEED_4
        Motor.usFront_Right_Duty += SPEED_4;
        Motor.usLater_Left_Duty -= SPEED_4;
        Motor.usLater_Right_Duty += SPEED_4;
        while(Track.ucTrack_Status_Buff[M_Status] != 0)
        {
            Public.Public_Delay_ms(50);
        }
    } 
    Motor.Motor_Clamp(Motor.usFront_Left_Duty, 0, MAX_SPEED);
    Motor.Motor_Clamp(Motor.usFront_Right_Duty, 0, MAX_SPEED);
    Motor.Motor_Clamp(Motor.usLater_Left_Duty, 0, MAX_SPEED);
    Motor.Motor_Clamp(Motor.usLater_Right_Duty, 0, MAX_SPEED);

    Motor.Motor_Set_Duty(Motor_FRONT_LEFT,Motor.usFront_Left_Duty);  
    Motor.Motor_Set_Duty(Motor_FRONT_RIGHT,Motor.usFront_Right_Duty); 
    Motor.Motor_Set_Duty(Motor_LATER_LEFT,Motor.usLater_Left_Duty); 
    Motor.Motor_Set_Duty(Motor_LATER_RIGHT,Motor.usLater_Right_Duty);                                       
}

VL53L0X

  • 模块资料

VL53L0X 传感器提供了 3 种测量模式, Single ranging(单次测量)Continuous ranging(连续测量)、以及 Timed ranging(定时测量)

VCC:5V

GND:参考地

SCL:I2C通信的时钟线

SDA:I2C通信的数据线

XSH:芯片的使能管脚,电平1代表有效,电平0代表无效

INT:芯片的中断管脚

这个挺麻烦的,需要移植,注意头文件的包含,找半天原来是为了删之前固件库的头文件,因为我工程是HAL库的,移植完主要用到两个函数:vl53l0x_initvl53l0x_start_single_test

  • 硬件电路

  • MX配置

  • 程序编写
VL53L0XV1.h
#ifndef __VL53L0XV1_H
#define __VL53L0XV1_H
#include "IIC.h"


// VL53L0X - SCL
#define VL53L0X_SCL_PORT GPIOF
#define VL53L0X_SCL_PIN GPIO_PIN_8
// VL53L0X - SDA
#define VL53L0X_SDA_PORT GPIOF
#define VL53L0X_SDA_PIN GPIO_PIN_9

typedef struct
{
		uint16_t usVL53_Distance;	// 测的距离(单位:mm)
    void (*VL53L0XV1_Init)(void); // VL53L0X初始化
	void (*VL53L0XV1_Get_Distance)(void);	// VL53L0X获取距离
}VL53L0XV1_t;

extern VL53L0XV1_t VL53L0XV1;
extern IIC_Data_t VL53L0X_IIC;

#endif
VL53L0XV1.c
/***************************************************************************
 * File: VL53L0XV1.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: VL53L0X V2 激光测距模块
 -----------------------------------
接线:
    PF8 ---> SCL
    PF9 ---> SDA
    GND ---> GND
    3.3V ---> VIN
    悬空 ---> GPIO1
    悬空 ---> XSHUT
 -----------------------------------
****************************************************************************/
#include "AllHead.h"
#include "vl53l0x.h"
/*====================================variable definition declaration area BEGIN===================================*/
// 注册IIC结构体
IIC_Data_t VL53L0X_IIC;
extern VL53L0X_Error vl53l0x_status;
extern VL53L0X_RangingMeasurementData_t vl53l0x_data;
extern VL53L0X_Dev_t vl53l0x_dev;
/*====================================variable definition declaration area   END===================================*/

/*====================================static function declaration area BEGIN====================================*/

static void VL53L0XV1_Init(void);
static void VL53L0XV1_Get_Distance(void);

/*====================================static function declaration area   END====================================*/

VL53L0XV1_t VL53L0XV1 = 
{
	0,
    VL53L0XV1_Init,
	VL53L0XV1_Get_Distance
};

/*
* @function: VL53L0X_Init
* @param: None
* @retval: None
* @brief: VL53L0X初始化
*/
static void VL53L0XV1_Init(void)
{
    IIC_Soft.IIC_Registered(&VL53L0X_IIC, VL53L0X_SCL_PORT, VL53L0X_SCL_PIN, VL53L0X_SDA_PORT, VL53L0X_SDA_PIN);	// 注册IIC设备
	
    vl53l0x_init();	// API初始化
}

/*
* @function: VL53L0XV1_Get_Distance
* @param: None
* @retval: None
* @brief: VL53L0X获取距离
*/
static void VL53L0XV1_Get_Distance(void)
{
    if (vl53l0x_status == VL53L0X_ERROR_NONE)
    {
        vl53l0x_start_single_test(&vl53l0x_dev, &vl53l0x_data, &VL53L0XV1.usVL53_Distance); // 获取距离
    }
}
CallBack.c
/*
* @function: USART2_IRQHandler
* @param: None
* @retval: None
* @brief: 串口2中断函数
*/
void USART2_IRQHandler(void)
{
	if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != 0x00u)
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart2);
		HMI.HMI_Protocol(); // 协议解析
	}    

    HAL_UART_IRQHandler(&huart2);
}

LED

  • 硬件电路

下面的LED是最小系统板上的,引脚是PC13

  • 程序编写
led.h
#ifndef __LED_H
#define __LED_H

// 管脚 LED1(绿)--PF0 LED2(红)--PF1 LED3(蓝)--PF2
#define Led_Green_Pin    GPIO_PIN_0
#define Led_Red_Pin    GPIO_PIN_1
#define Led_Blue_Pin    GPIO_PIN_2

// 定义枚举类型
typedef enum
{
    LED_GREEN = (uint8_t)0x01,
    LED_RED = (uint8_t)0x02,
    LED_BLUE = (uint8_t)0x03,
} Led_Num_t;

typedef struct
{
    void (*Led_Init)(void); // LED初始化
    void (*Led_ON)(Led_Num_t);   // 打开
    void (*Led_OFF)(Led_Num_t);  // 关闭
    void (*Led_Flip)(Led_Num_t); // 翻转
} Led_t;

extern Led_t Led;

#endif
led.c
/***************************************************************************
 * File: Led.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: LED
 -----------------------------------
接线:
    PF0 ---> LED_1(绿色)
    PF1 ---> LED_2(红色)
    PF2 ---> LED_3(蓝色)
    (低电平亮 高电平灭)
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/

static void Led_Init(void);
static void Led_ON(Led_Num_t);
static void Led_OFF(Led_Num_t);
static void Led_Flip(Led_Num_t);

/*====================================static function declaration area   END====================================*/

Led_t Led = 
{
    Led_Init,
    Led_ON,
    Led_OFF,
    Led_Flip,
};

/*
* @function: Led_Init
* @param: None
* @retval: None
* @brief: LED初始化
*/
static void Led_Init(void)
{
    // 初始化全灭
    Led.Led_OFF(LED_GREEN);
    Led.Led_OFF(LED_RED);
    Led.Led_OFF(LED_BLUE);
}

/*
* @function: Led_ON
* @param: LEDx -> LED_GREEN,LED_RED,LED_BLUE
* @retval: None
* @brief: LED打开
*/
static void Led_ON(Led_Num_t LEDx)
{
    switch(LEDx)
    {
        case LED_GREEN:
        {
            HAL_GPIO_WritePin(GPIOF, Led_Green_Pin, GPIO_PIN_RESET);
            break;
        }
        case LED_RED:
        {
            HAL_GPIO_WritePin(GPIOF, Led_Red_Pin, GPIO_PIN_RESET);
            break;
        }
        case LED_BLUE:
        {
            HAL_GPIO_WritePin(GPIOF, Led_Blue_Pin, GPIO_PIN_RESET);
            break;
        }
        default: break;
    }
}

/*
* @function: Led_OFF
* @param: LEDx -> LED_GREEN,LED_RED,LED_BLUE
* @retval: None
* @brief: LED关闭
*/
static void Led_OFF(Led_Num_t LEDx)
{
    switch(LEDx)
    {
        case LED_GREEN:
        {
            HAL_GPIO_WritePin(GPIOF, Led_Green_Pin, GPIO_PIN_SET);
            break;
        }
        case LED_RED:
        {
            HAL_GPIO_WritePin(GPIOF, Led_Red_Pin, GPIO_PIN_SET);
            break;
        }
        case LED_BLUE:
        {
            HAL_GPIO_WritePin(GPIOF, Led_Blue_Pin, GPIO_PIN_SET);
            break;
        }
        default: break;
    }  
}

/*
* @function: Led_Flip
* @param: LEDx -> LED_GREEN,LED_RED,LED_BLUE
* @retval: None
* @brief: LED翻转
*/
static void Led_Flip(Led_Num_t LEDx)
{
    switch(LEDx)
    {
        case LED_GREEN:
        {
            HAL_GPIO_TogglePin(GPIOF, Led_Green_Pin);
            break;
        }
        case LED_RED:
        {
            HAL_GPIO_TogglePin(GPIOF, Led_Red_Pin);
            break;
        }
        case LED_BLUE:
        {
            HAL_GPIO_TogglePin(GPIOF, Led_Blue_Pin);
            break;
        }
        default: break;
    }   
}

BUZZER

  • 硬件电路

  • MX配置

  • 程序编写
Buzzer.h
#ifndef __BUZZER_H
#define __BUZZER_H

//  buzzer--PC2
#define Buzzer_Pin GPIO_PIN_2

typedef enum
{
    Buzzer_Status_ON = (uint8_t)0x01,  
    Buzzer_Status_OFF = (uint8_t)0x00,  
}Buzzer_Status_t;

typedef struct
{
    Buzzer_Status_t Buzzer_Status;    
    void (*Buzzer_Init)(void); // Buzzer 初始化  
    void (*Buzzer_ON)(void);    // 打开Buzzer
    void (*Buzzer_OFF)(void);   // 关闭Buzzer
}Buzzer_t;

extern Buzzer_t Buzzer;

#endif
Buzzer.c
/***************************************************************************
 * File: Buzzer.c
 * Author: Luckys.
 * Date: 2023/06/24
 * description: 蜂鸣器
接线:
    PC2 ---> BUZZER (NPN高电平导通)
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/

static void Buzzer_Init(void);  // 蜂鸣器初始化
static void Buzzer_ON(void);    // 蜂鸣器打开
static void Buzzer_OFF(void);   // 蜂鸣器关闭

/*====================================static function declaration area   END====================================*/
Buzzer_t Buzzer = 
{
    Buzzer_Status_OFF,
    Buzzer_Init,
    Buzzer_ON,
    Buzzer_OFF,
};

/*
* @function: Buzzer_Init
* @param: None
* @retval: None
* @brief: 蜂鸣器初始化
*/
static void Buzzer_Init(void)
{
    HAL_GPIO_WritePin(GPIOC,Buzzer_Pin,GPIO_PIN_RESET); // 默认蜂鸣器关闭
}

/*
* @function: Buzzer_ON
* @param: None
* @retval: None
* @brief: 蜂鸣器打开
*/
static void Buzzer_ON(void)
{
    HAL_GPIO_WritePin(GPIOC,Buzzer_Pin,GPIO_PIN_SET);
    Buzzer.Buzzer_Status = Buzzer_Status_ON;
}

/*
* @function: Buzzer_OFF
* @param: None
* @retval: None
* @brief: 蜂鸣器关闭
*/
static void Buzzer_OFF(void)
{
    HAL_GPIO_WritePin(GPIOC,Buzzer_Pin,GPIO_PIN_RESET);
    Buzzer.Buzzer_Status = Buzzer_Status_OFF;
}

KEY

  • 硬件电路

下面的按键是最小系统板上的,引脚是PA15

  • MX配置

  • 程序编写
Key.h
#ifndef __KEY_H
#define __KEY_H

// 管脚 K1--PF3 K2--PF4 K3--PF5
#define Key1_Pin    GPIO_PIN_3
#define Key2_Pin    GPIO_PIN_4
#define Key3_Pin    GPIO_PIN_5

// 读取按键电平
#define READ_KEY1    HAL_GPIO_ReadPin(GPIOF,Key1_Pin)
#define READ_KEY2    HAL_GPIO_ReadPin(GPIOF,Key2_Pin)
#define READ_KEY3    HAL_GPIO_ReadPin(GPIOF,Key3_Pin)

typedef enum
{
    KEY_NULL  = (uint8_t)0x00,  // 无按键按下键值
    KEY1_DOWN = (uint8_t)0x01,  // 按键1按下键值  
    KEY2_DOWN = (uint8_t)0x02,  // 按键2按下键值
    KEY3_DOWN = (uint8_t)0x03,  // 按键3按下键值
}Key_1_Status_t;

typedef struct
{
    uint16_t volatile vusKey_Timer_Count; // 长按计数
    uint8_t volatile vucKey_Flag_Arr[6];  // 按键标志位(短长按)
    void (*Key_Scan)(void);   // 按键三行消抖---按键扫描
    void (*Key_Handler)(void);    // 按键处理
}Key_t;

extern Key_t Key;


#endif
Key.c
/***************************************************************************
 * File: Key.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 按键
 -----------------------------------
接线:
    PF3 ---> KEY1(SW2)
    PF4 ---> KEY2(SW3)
    PF5 ---> KEY3(SW4)
    (上拉,按下低电平)
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/*====================================variable definition declaration area BEGIN===================================*/
// 按键键值、抬起一瞬间、按下一瞬间
static uint8_t ucKey_Value,ucKey_Up,ucKey_Down;

/*====================================variable definition declaration area   END===================================*/

/*====================================static function declaration area BEGIN====================================*/

static uint8_t Key_Return_Value(void); // 返回键值
static void Key_Scan(void);   // 按键三行消抖---按键扫描
static void Key_Handler(void);    // 按键处理
/*====================================static function declaration area   END====================================*/

Key_t Key = 
{
    0,
    {FALSE},
    Key_Scan,
    Key_Handler,
};


/*
* @function: Key_Return_Value
* @param: None
* @retval: None
* @brief: 返回键值
*/
static uint8_t Key_Return_Value(void)
{
    if ((!READ_KEY1) || (!READ_KEY2) || (!READ_KEY3))
    {
        if (!READ_KEY1)
        {
            return KEY1_DOWN;
        }
        else if (!READ_KEY2)
        {
            return KEY2_DOWN;
        }
        else if (!READ_KEY3)
        {
            return KEY3_DOWN;
        }                
    }

    return KEY_NULL;
}

/*
* @function: Key_Scan
* @param: None
* @retval: None
* @brief: 按键三行消抖---按键扫描
*/
static void Key_Scan(void)
{
    static uint8_t uckey_old;

    ucKey_Value = Key_Return_Value();                      // 读取按键的键值
    ucKey_Up = ~ucKey_Value & (uckey_old ^ ucKey_Value); // 按键的上升沿检测 只在按键抬起的瞬间有效 其他时刻都为零无效
    ucKey_Down = ucKey_Value & (uckey_old ^ ucKey_Value); // 按键的下降沿检测 只在按键按下的瞬间有效 其他时刻都为零无效
    uckey_old = ucKey_Value;                          // 记录上一次按键按下后的键值

    if (ucKey_Down) // 当有按键按下时
    {
        Key.vusKey_Timer_Count = 0; // 将计时器清零 从零开始计时 此处使用了基础定时器用于计时
    }

    if (Key.vusKey_Timer_Count < 10) // 如果计时时间小于1s 短按
    {
        switch (ucKey_Up) // 判断按键是否抬起 选择键值执行短按的相应程序
        {
            case KEY1_DOWN:Key.vucKey_Flag_Arr[0] = TRUE;break;
            case KEY2_DOWN:Key.vucKey_Flag_Arr[1] = TRUE;break;
            case KEY3_DOWN:Key.vucKey_Flag_Arr[2] = TRUE;break;
            default:break;
        }
    }
    else // 长按 计时时间超过1s
    {
        switch (ucKey_Value) // 判断按键是否抬起 选择键值执行短按的相应程序
        {
            case KEY1_DOWN:Key.vucKey_Flag_Arr[3] = TRUE;break;
            case KEY2_DOWN:Key.vucKey_Flag_Arr[4] = TRUE;break;
            case KEY3_DOWN:Key.vucKey_Flag_Arr[5] = TRUE;break;
            default:break;
        }
    }
}

/*
* @function: Key_Handler
* @param: None
* @retval: None
* @brief: 按键处理
*/
static void Key_Handler(void)
{
    if (Key.vucKey_Flag_Arr[0]) // K1短按
    {
        Key.vucKey_Flag_Arr[0] = FALSE;
    }
    else if (Key.vucKey_Flag_Arr[1])    // K2短按
    {
        Key.vucKey_Flag_Arr[1] = FALSE;
    }
    else if (Key.vucKey_Flag_Arr[2])    //K3短按
    {
        Key.vucKey_Flag_Arr[2] = FALSE;
    }
    else if (Key.vucKey_Flag_Arr[3])    // K1长按
    {
        Key.vucKey_Flag_Arr[3] = FALSE;
    }
    else if (Key.vucKey_Flag_Arr[4])    // K2长按
    {
        Key.vucKey_Flag_Arr[4] = FALSE;
    }  
    else if (Key.vucKey_Flag_Arr[5])    // K3长按
    {
        Key.vucKey_Flag_Arr[5] = FALSE;
    }      
}

TIMER6

此定时器用作系统运行计数,还有任务标记

  • MX配置

  • 程序编写
Timer6.h
#ifndef __TIMER6_H
#define __TIMER6_H

//定义枚举类型
typedef enum
{
	TIMER6_10mS = (uint16_t)10,
	TIMER6_50mS = (uint16_t)50,
	TIMER6_100mS = (uint16_t)100,
	TIMER6_200mS = (uint16_t)200,
	TIMER6_500mS = (uint16_t)500,
	TIMER6_1S = (uint16_t)1000,
	TIMER6_2S = (uint16_t)2000,
	TIMER6_3S = (uint16_t)3000,
	TIMER6_5S = (uint16_t)5000,
	TIMER6_10S = (uint16_t)10000
} TIMER6_Value_t;

// 定义结构体类型
typedef struct
{
	uint16_t volatile usMCU_Run_Timer;		 // 系统运行定时器
	uint16_t volatile SHT30_Measure_Timeout; // SHT30超时时间

	void (*Timer6_Start_IT)(void); // 定时器6以中断模式启动
} Timer6_t;

extern Timer6_t Timer6;

#endif
Timer6.c
/***************************************************************************
 * File: Timer6.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: TIM6
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/

static void Timer6_Start_IT(void);

/*====================================static function declaration area   END====================================*/

Timer6_t Timer6 = 
{
    0,
    0,
    Timer6_Start_IT
};

/*
* @function: Timer6_Start_IT
* @param: None
* @retval: None
* @brief: 打开定时器6+中断
*/
static void Timer6_Start_IT(void)
{
    HAL_TIM_Base_Start_IT(&htim6);
}

通用IIC

IIC.h
#ifndef __IIC_H
#define __IIC_H
#include "gpio.h"
#include "Public.h"

// 置1 / 清0 SCL管脚
#define SET_SCL(PIN_PORT, PIN) HAL_GPIO_WritePin(PIN_PORT, PIN, GPIO_PIN_SET)
#define CLR_SCL(PIN_PORT, PIN) HAL_GPIO_WritePin(PIN_PORT, PIN, GPIO_PIN_RESET)
// 置1 / 清0 SDA管脚
#define SET_SDA(PIN_PORT, PIN) HAL_GPIO_WritePin(PIN_PORT, PIN, GPIO_PIN_SET)
#define CLR_SDA(PIN_PORT, PIN) HAL_GPIO_WritePin(PIN_PORT, PIN, GPIO_PIN_RESET)
// 读取SDA管脚状态
#define READ_SDA(PIN_PORT, PIN) HAL_GPIO_ReadPin(PIN_PORT, PIN)

// 定义枚举类型
typedef enum
{
	ACK = GPIO_PIN_RESET,
	NACK = GPIO_PIN_SET,
} ACK_Value_t;

// IIC注册结构体
typedef struct
{
	GPIO_TypeDef *SCL_GPIOx; // SCL端口组
	uint16_t SCL_GPIO_Pin;	 // SCL引脚
	GPIO_TypeDef *SDA_GPIOx; // SDA端口组
	uint16_t SDA_GPIO_Pin;	 // SDA引脚
} IIC_Data_t;

typedef struct
{
	void (*IIC_Registered)(IIC_Data_t *, GPIO_TypeDef *, uint16_t, GPIO_TypeDef *, uint16_t); // IIC注册
	void (*IIC_Start)(IIC_Data_t *);														  // IIC起始信号
	void (*IIC_Stop)(IIC_Data_t *);															  // IIC停止信号
	ACK_Value_t (*IIC_Write_Byte)(IIC_Data_t *, uint8_t);									  // IIC写字节
	uint8_t (*IIC_Read_Byte)(IIC_Data_t *, ACK_Value_t);									  // IIC读字节
	uint8_t (*IIC_Write_Buffer)(IIC_Data_t *, uint8_t, uint8_t, uint32_t, uint8_t *);		  // IIC写一串数据
	uint8_t (*IIC_Read_Buffer)(IIC_Data_t *, uint8_t, uint8_t, uint32_t, uint8_t *);		  // IIC读一串数据
} IIC_Soft_t;

extern IIC_Soft_t IIC_Soft;

#endif
IIC.c
/***************************************************************************
 * File: IIC.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: I2C通用
****************************************************************************/
#include "AllHead.h"
/*====================================static function declaration area BEGIN====================================*/

static void IIC_Registered(IIC_Data_t *dev, GPIO_TypeDef *SCL_GPIOx, uint16_t SCL_GPIO_Pin,  GPIO_TypeDef *SDA_GPIOx, uint16_t SDA_GPIO_Pin);
static void IIC_Init(IIC_Data_t * dev);
static void IIC_Start(IIC_Data_t *dev);
static void IIC_Stop(IIC_Data_t *dev);
static ACK_Value_t IIC_Write_Byte(IIC_Data_t *dev, uint8_t WR_Byte);
static uint8_t IIC_Read_Byte(IIC_Data_t *dev, ACK_Value_t ACK_Value);

static uint8_t IIC_Write_Buffer(IIC_Data_t *dev, uint8_t addr, uint8_t reg, uint32_t len, uint8_t *data);
static uint8_t IIC_Read_Buffer(IIC_Data_t *dev, uint8_t addr, uint8_t reg, uint32_t len, uint8_t *buf);

/*====================================static function declaration area   END====================================*/
IIC_Soft_t IIC_Soft = 
{
	IIC_Registered,
	IIC_Start,
	IIC_Stop,
	IIC_Write_Byte,
	IIC_Read_Byte,
	IIC_Write_Buffer,
	IIC_Read_Buffer
};

/*
* @function: IIC_Registered
* @param: dev -> 待注册的结构体指针 SCL_GPIOx -> SCL引脚端口组 SCL_GPIOx -> SCL引脚 SDA_GPIOx -> SDA端口组 SDA_GPIO_Pin -> SDA引脚
* @retval: None
* @brief: IIC注册
*/
static void IIC_Registered(IIC_Data_t *dev, GPIO_TypeDef *SCL_GPIOx, uint16_t SCL_GPIO_Pin,  GPIO_TypeDef *SDA_GPIOx, uint16_t SDA_GPIO_Pin)
{
	dev->SCL_GPIOx = SCL_GPIOx;	
	dev->SCL_GPIO_Pin = SCL_GPIO_Pin;
	dev->SDA_GPIOx = SDA_GPIOx;
	dev->SDA_GPIO_Pin = SDA_GPIO_Pin;
	
	IIC_Init(dev);
}

/*
* @function: IIC_Init
* @param: dev -> 结构体指针
* @retval: None
* @brief: IIC初始化
*/
static void IIC_Init(IIC_Data_t *dev)
{
	// 拉高SCL,SDA
	SET_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
	SET_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
}

/*
* @function: IIC_Start
* @param: dev -> 结构体指针
* @retval: None
* @brief: IIC起始信号
*/
static void IIC_Start(IIC_Data_t *dev)
{
	// SCL为高电平,SDA的下降沿为I2C起始信号
	SET_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
	SET_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
	Public.Public_Delay_us(1);
	
	CLR_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
	Public.Public_Delay_us(10);
	
	CLR_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
	Public.Public_Delay_us(1);
}

/*
* @function: IIC_Stop
* @param: dev -> 结构体指针
* @retval: None
* @brief: IIC停止信号
*/
static void IIC_Stop(IIC_Data_t *dev)
{
	// SCL为高电平,SDA的上升沿为I2C停止信号
	CLR_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
	SET_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
	Public.Public_Delay_us(1);
	
	Public.Public_Delay_us(10);
	SET_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
}

/*
* @function: IIC_Stop
* @param: dev -> 结构体指针 WR_Byte -> 待写入字节
* @retval: ACK_Value_t -> 从机应答值
* @brief: IIC写字节
*/
static ACK_Value_t IIC_Write_Byte(IIC_Data_t *dev, uint8_t WR_Byte)
{
	uint8_t i;
	ACK_Value_t  ACK_Rspond;
	
	// SCL为低电平时,SDA准备数据,接着SCL为高电平,读取SDA数据
	// 数据按8位传输,高位在前,利用for循环逐个接收
	for(i=0; i<8; i++)
	{
		// SCL清零,主机SDA准备数据
		CLR_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
		Public.Public_Delay_us(1);
		if ( (WR_Byte & BIT7) == BIT7 )
		{
			SET_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
		}
		else
		{
			CLR_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
		}
		Public.Public_Delay_us(1);
		// SCL置高,传输数据
		SET_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
		Public.Public_Delay_us(10);
		
		//准备发送下一比特位
		WR_Byte <<= 1;
	}
	
	CLR_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
	//释放SDA,等待从机应答
	SET_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
	Public.Public_Delay_us(1);
	
	SET_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
	Public.Public_Delay_us(10);
	
	ACK_Rspond = (ACK_Value_t)READ_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
	
	CLR_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
	Public.Public_Delay_us(1);
	
	//返回从机的应答信号
	return ACK_Rspond;	
}

/*
* @function: IIC_Read
* @param: dev -> 结构体指针 ACK_Value -> 主机回应值(控制是否继续读下去)
* @retval: 从机返回值
* @brief: IIC读字节
*/
static uint8_t IIC_Read_Byte(IIC_Data_t *dev, ACK_Value_t ACK_Value)
{
	uint8_t RD_Byte = 0,i;
		
	// 接收数据
	// SCL为低电平时,SDA准备数据,接着SCL为高电平,读取SDA数据
	// 数据按8位传输,高位在前,利用for循环逐个接收
	for(i=0; i<8; i++)
	{
		// 准备接收下一比特位
		RD_Byte <<= 1;
		
		// SCL清零,从机SDA准备数据
		CLR_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
		Public.Public_Delay_us(10);
		
		// SCL置高,获取数据
		SET_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
		Public.Public_Delay_us(10);

		RD_Byte |= READ_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);		
	}
	
	
	// SCL清零,主机准备应答信号
	CLR_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
	Public.Public_Delay_us(1);
	
	// 主机发送应答信号	
	if (ACK == ACK_Value)
	{
		CLR_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
	}
	else
	{
		SET_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
  }	
	Public.Public_Delay_us(1);
	
	SET_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin); 	
	Public.Public_Delay_us(10);
	
  // 释放SDA数据线
	// SCL先清零,再释放SDA,防止连续传输数据时,从机错将SDA释放信号当成NACk信号
	CLR_SCL(dev->SCL_GPIOx, dev->SCL_GPIO_Pin);
  SET_SDA(dev->SDA_GPIOx, dev->SDA_GPIO_Pin);
	Public.Public_Delay_us(1);

	// 返回数据
	return RD_Byte;	
}

/*
* @function: IIC_Write_Buffer
* @param: dev -> 结构体指针 addr -> 目标设备地址 reg -> 寄存器地址 len -> 要发送的数据长度 data -> 指向数据缓冲区的指针
* @retval: 0 --- 成功
* @brief: IIC写一串数据
*/
static uint8_t IIC_Write_Buffer(IIC_Data_t *dev, uint8_t addr, uint8_t reg, uint32_t len, uint8_t *data)
{
    uint32_t i;
    
    // 开始信号
		IIC_Soft.IIC_Start(dev);
    IIC_Soft.IIC_Write_Byte(dev, addr | 0x00);	// 写操作
    IIC_Soft.IIC_Write_Byte(dev, reg);	// 发送寄存器地址

    for (i = 0; i < len; i++) 
	 {
        IIC_Soft.IIC_Write_Byte(dev, data[i]);
    }
	 IIC_Soft.IIC_Stop(dev);
		
    return 0;
}

/*
* @function: IIC_Read_Buffer
* @param: dev -> 结构体指针 addr -> 目标设备地址 reg -> 寄存器地址 len -> 要读取的数据长度 data -> 指向数据缓冲区的指针
* @retval: 0 --- 成功
* @brief: IIC读一串数据
*/
static uint8_t IIC_Read_Buffer(IIC_Data_t *dev, uint8_t addr, uint8_t reg, uint32_t len, uint8_t *buf)
{
    // 开始信号
		IIC_Soft.IIC_Start(dev);
    IIC_Soft.IIC_Write_Byte(dev, addr | 0x00);
    IIC_Soft.IIC_Write_Byte(dev, reg);
    Public.Public_Delay_us(5);
    
    IIC_Soft.IIC_Start(dev);
    IIC_Soft.IIC_Write_Byte(dev, addr | 0x01);	// 读操作

    while(len)
		{
        if (len > 1)
				{
					*buf = IIC_Soft.IIC_Read_Byte(dev, ACK);
				}
				else
				{
					*buf = IIC_Soft.IIC_Read_Byte(dev, NACK);
				}
        buf++;
        len--;
    }
    IIC_Soft.IIC_Stop(dev);
    return 0;
}

UART1

  • MX配置

  • 程序编写

暂时无

独立看门狗

独立看门狗超时时间计算:

LSI = 40kHz

超出时间 = (1/(LSI/PSC)) * X = 4s,X就是我们需要的装载值不能超过4095,PSC就是分频值看MX

  • MX配置

问题

打印函数是适用于多串口的,需要包含头文件:

#include <stdarg.h>
#include <string.h>
#include <stdio.h>

static void UsartPrintf(UART_HandleTypeDef USARTx, char *fmt,...)
{
 
	uint8_t UsartPrintfBuf[296];
	va_list ap;
	uint8_t *pStr = UsartPrintfBuf;
	
	va_start(ap, fmt);
	vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);		//格式化
	va_end(ap);
	
	while(*pStr != NULL)
	{
        HAL_UART_Transmit(&USARTx ,(uint8_t *)pStr++,1,HAL_MAX_DELAY);		
	}
}

废了,LM2596T发热严重,只能把电源部分元器件全部拆除

LED蓝色的不亮,红色有点亮,绿色最亮,原因是电阻选太大了,一般510欧姆就够了【我焊接时把蓝灯换成绿灯,电阻从5.1K换成1K,红色还是有点暗,绿色有点亮瞎眼】

WiFi模块也画错了,还是得用杜邦线

在上传状态到阿里云时发现一直心跳失败,后面找了原因,原来是数组溢出了,一开始设定的数组大小为200,但是我上传的字串符超出了所以造成溢出异常,后面改成400就正常了