圆梦杯笔记
前言
参考文章/博主
我的工程
准备阶段
- 用到的模块
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 |
- 后左轮
| STM32 IO | 电机驱动 IO |
|---|---|
| PD12(TIMER4_CH1) | PWMB |
| PE2 | BIN1 |
| PE3 | BIN2 |
- 后右轮
| STM32 IO | 电机驱动 IO |
|---|---|
| PD13(TIMER4_CH2) | PWMA |
| PE4 | AIN1 |
| PE5 | AIN2 |
- 前左轮
| STM32 IO | 电机驱动 IO |
|---|---|
| PD14(TIMER4_CH3) | PWMA |
| PE0 | AIN1 |
| PE1 | AIN2 |
- 前右轮
| 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_init,vl53l0x_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就正常了









































