目 录
第一章 绪论 .................................................................................2
1.1设计目的及要求 .................................................................. 2
1.2 设计任务 ........................................................................... 2
1.3 项目实施计划........................................................................5
第二章 原理图绘制 .....................................................................8
2.1 系统结构和接口设计 ............................................. .............3
2.2 各电路的原理图设计和原理分析 ........................ ......... ...... 8
第三章 整体模块设计.....................................................................18
3.1 系统框图 .................................................... ................. ....18
3.2 各模块源代码设计和分析 ...................................................20
3.3 设计步骤........................................................................ 28
3.3 模块仿真结果和分析 .........................................................32
第四章 总 结.................................................................................38
一、 绪论
1.1 设计目的及要求
目的:
1、 掌握电子线路板设计方法;
2、 掌握嵌入式SF32LB52用于智能电子系统的设计技术和方法;
3、 掌握GPIO\I2C控制应用方法;
4、 学习掌握智能嵌入式微处理器集成开发环境SiFli-SDK的使用方法。
要求:
1、 设计超声波发射接收模块,要求
a) 控制超时波发射频率,每秒1次
b) MCU计算距离,通过超声波发射接收时间间隔计算实际距离
c) 计算公式 distance = time * 343m/s / 2
2、 设计4位数码管显示
a) 使用I2C通信写入数据
b) 控制4位数码管显示
1.2 设计任务
使用思澈科技SF32LB52-Devkit-LCD开发板,驱动TM1650 I2C接口控制的数码管,和HC-SR04超声波模块完成。其中SF32LB52-Devkit-LCD开发板 作为主控MCU控制HC-SR04超声波模块发射超声波,并读取Echo引脚记录超声波从发射到接收的时间间隔,根据公司 Distance = 343m/s * time / 2 得到距离值,然后通过MCU I2C外设将数据发送到TM1650 四位共阴极数码管中进行显示。最终完成超声波测距装置的实现。
1.3项目实施计划
1、 实现MCU I2C 驱动配置
2、 完成 I2C 通信写入TM1650 驱动点亮4位数码管
3、 实现超声波模块驱动配置
4、 计算超时波测量距离并写入数码管
二、 原理图绘制
2.1 系统结构和接口设计
系统结构:

接口设计:

2.2 各电路的原理图设计和原理分析
SF32LB52:

超声波:

数码管:

三、 整体模块设计
3.1 系统框图

3.2 各模块源代码设计和分析
C-SR04 模块源代码设计/分析
源代码
c
#include "rtthread.h"
#include "bf0_hal.h"
#include "drv_io.h"
#include "stdio.h"
#include "string.h"
#include "board.h"
#include "drv_gpio.h"
#include "ulog.h"
#define DBG_TAG "HC-SR04"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#define HC_SR04_TRIG GET_PIN(1,39)
#define HC_SR04_ECHO GET_PIN(1,40)
#define SET_TRIG(x) rt_pin_write(HC_SR04_TRIG, x)
#define SET_ECHO(x) rt_pin_write(HC_SR04_ECHO, x)
#define READ_ECHO rt_pin_read(HC_SR04_ECHO)
GPT_HandleTypeDef TIM_Handle = {0};
int GPTIM1_CORE = 0;
void HC_SR04_init(void)
{
/* Initialize GPIO pins */
rt_pin_mode(HC_SR04_TRIG, PIN_MODE_OUTPUT);
rt_pin_mode(HC_SR04_ECHO, PIN_MODE_INPUT);
/* Set initial state */
rt_pin_write(HC_SR04_TRIG, PIN_LOW);
rt_pin_write(HC_SR04_ECHO, PIN_HIGH);
TIM_Handle.Instance = GPTIM1; // Use GPTIM1
TIM_Handle.Init.Prescaler = 12000 - 1; // Set prescaler
TIM_Handle.core = HAL_RCC_GetPCLKFreq(GPTIM1_CORE, 1); // HAL_RCC_GetPCLKFreq(GPTIM1_CORE, 1) // Clock source is from GPTIM1_CORE
TIM_Handle.Init.CounterMode = GPT_COUNTERMODE_UP; // Count up
TIM_Handle.Init.Period = 60000 - 1; // Max period
HAL_GPT_Base_Init(&TIM_Handle); // Initialize Timer
HAL_GPT_Base_Stop(&TIM_Handle); // Stop Timer
GPTIM1->CNT = 0;
LOG_D("Initialized for HC-SR04 communication.");
}
void HC_SR04_Trigger(void)
{
SET_TRIG(1);
rt_thread_mdelay(0.015); // 15 microseconds
SET_TRIG(0);
LOG_D("HC-SR04 Triggered.");
}
uint8_t LAST_CNT = 0;
uint8_t NOW_CNT = 0;
float HC_SR04_Measure(void)
{
uint32_t start_time, end_time, duration;
HC_SR04_Trigger();
// Wait for ECHO to go high
while(!READ_ECHO);
HAL_GPT_Base_Start(&TIM_Handle); // Start timer
// Wait for ECHO to go low
while(READ_ECHO);
HAL_GPT_Base_Stop(&TIM_Handle); // Ensure timer is stopped
LOG_I("%d",GPTIM1->CNT);
LAST_CNT = GPTIM1->CNT;
NOW_CNT = 0.7 * LAST_CNT + 0.3 * GPTIM1->CNT;
float distance = NOW_CNT * 1.7; // Distance in cm
GPTIM1->CNT = 0; // Reset counter for next measurement
LOG_I("Measured Distance: %.2f cm", distance);
return distance;
}
分析
这一段属于超声波模块C-SR04驱动代码,包括模块ECHO、TRIG相关IO口配置和,计数用定时器GPTIM配置,以及一个距离计算处理函数。
其中:
HC_SR04_init()
此函数为初始化函数,初始化了两个控制IO口,ECHO -- PAD_PA40 配置为输入模式 并初始设高电平,TRIG -- PAD_PA39 配置为输出模式 并初始设置低电平。同时初始化配置GPTIM定时器用于计数,记录时间。这里配置定时器时钟为HAL_RCC_GetPCLKFreq(GPTIM1_Core,1),这里时钟即为120Mhz,配置定时器预分频器为12000-1,将时钟预先分频为100000,配置计数模式为向上计数,同时计数周期为60000 -- 1,其余配置默认即可,此处定时器仅用到其中计数的功能,随后读取其CNT寄存器中的值即可计算出时间,初始化关闭定时器,并设CNT寄存器为0。
HC_SR04_Trigger()
此函数为超声波模块TRIG引脚控制函数。根据数据手册:

需要给TRIG引脚至少10us的高电平信号以触发超声波发射。因此使用SET_TRIG(1) 拉高引脚,并延时15us,然后拉低引脚SET_TRIG(0),即发送了10us以上高电平信号,超声波模块会自动发出超声波信号。
HC-SR04_Measure()
这是距离测量计算函数,先触发TRIG引脚使模块发射出超声波信号。根据数据手册可知,超声波信号发出时,模块自动拉高ECHO引脚,当超声波模块返回并被接收时,模块自动拉低引脚,计算其中高电平时间即可计算出时间并由此计算出距离。并更加GPTIM定时器配置可知,当超声波信号发出时,打开定时器,定时器开始CNT计算;当检测到超声波信号返回时,关闭定时器计算,此时读取CNT的值x。PCLK时钟为120000000 ,预分频为12000,因此计数器每秒计数100000个。由此
Distance = CNT * 340 * 1.0 * 100 / 100000 / 2 cm (单位转换为cm)
函数最终返回 两位浮点型小数。
TM1650 4位数码管驱动显示代码/分析
代码
c
#define TM1650_SCL GET_PIN(1,41)
#define TM1650_SDA GET_PIN(1,42)
#define SET_SCL(x) rt_pin_write(TM1650_SCL, x)
#define SET_SDA(x) rt_pin_write(TM1650_SDA, x)
#define READ_SDA rt_pin_read(TM1650_SDA)
void I2C_Start(void)
{
SET_SDA(1);
SET_SCL(1);
rt_thread_mdelay(1);
SET_SDA(0);
rt_thread_mdelay(1);
SET_SCL(0);
LOG_I("I2C Start Condition");
}
void I2C_ACK(void)
{
uint8_t timeout = 0;
SET_SCL(1);
rt_thread_mdelay(1);
SET_SCL(0);
//rt_pin_mode(TM1650_SDA, PIN_MODE_INPUT);
while(READ_SDA&&timeout<=100)
{
timeout++;
LOG_I("I2C ACK Waiting...");
}
//rt_pin_mode(TM1650_SDA, PIN_MODE_OUTPUT);
rt_thread_mdelay(1);
SET_SCL(0);
LOG_I("I2C ACK Received");
}
void I2C_Stop(void)
{
SET_SDA(0);
SET_SCL(1);
rt_thread_mdelay(1);
SET_SDA(1);
rt_thread_mdelay(1);
LOG_I("I2C Stop Condition");
}
void I2C_Send_Byte(uint8_t byte)
{
SET_SCL(0);
rt_thread_mdelay(1);
for(int i=0;i<8;i++)
{
if(byte&0x80)
SET_SDA(1);
else
SET_SDA(0);
rt_thread_mdelay(1);
byte <<= 1;
SET_SCL(1);
rt_thread_mdelay(1);
SET_SCL(0);
rt_thread_mdelay(1);
}
LOG_I("I2C Sent Byte: 0x%02X", byte);
}
void TM1650_GPIO_init(void)
{
/* Initialize GPIO pins */
rt_pin_mode(TM1650_SCL, PIN_MODE_OUTPUT);
rt_pin_mode(TM1650_SDA, PIN_MODE_OUTPUT);
/* Set initial state */
rt_pin_write(TM1650_SCL, PIN_LOW);//
rt_pin_write(TM1650_SDA, PIN_HIGH);//
LOG_D("GPIO initialized for TM1650 I2C communication.");
}
void TM1650_Brightness_Set(uint8_t data)
{
I2C_Start();
I2C_Send_Byte(0x48);
I2C_ACK();
I2C_Send_Byte(data);
I2C_ACK();
I2C_Stop();
LOG_D("TM1650 Displayed data 0x%02X at address 0x%02X", data, 0x24);
}
void TM1650_Display(uint8_t DIG, uint8_t data)
{
I2C_Start();
I2C_Send_Byte(0x68 + DIG);
I2C_ACK();
I2C_Send_Byte(data);
I2C_ACK();
I2C_Stop();
LOG_D("TM1650 Displayed data 0x%02X at address 0x%02X", data, 0x34 + DIG);
}
uint8_t DATA_NUM[10]=
{
0x3F, //0
0x06, //1
0x5B, //2
0x4F, //3
0x66, //4
0x6D, //5
0x7D, //6
0x07, //7
0x7F, //8
0x6F //9
};
#define TM1650_DOT 0x80
//15.45
void TM1650_display_float(float num)
{
int int_part = (int)num; //15
int frac_part = (int)((num - int_part) * 100); // 45
TM1650_Display(0, DATA_NUM[int_part / 10]);
TM1650_Display(2, DATA_NUM[int_part % 10] | TM1650_DOT);
TM1650_Display(4, DATA_NUM[frac_part / 10]);
TM1650_Display(6, DATA_NUM[frac_part % 10]);
}
分析
(数据波形图)

SF32LB52支持硬件I2C、软件I2C。这里选择写出软件I2C,以直观的显示出时序波形控制。
TM1650_GPIO_init()
模拟I2C的引脚配置,配置PAD_PA41为SCL引脚,配置PAD_PA42为SDA引脚。
I2C_Start()

这是I2C起始信号函数。根据数据手册中的波形控制图可知,I2C起始信号需要先将SDA:SET_SDA(1);
,SCL:SET_SCL(1);拉高,在SCL为高电平期间,拉低SDA:SET_SDA(0);认为是开始信号。
I2C_Stop()

这是I2C停止信号函数。根据数据手册中的波形控制图可知,I2C停止信号需要SDA:SET_SDA(0);
为低电平,SCL:SET_SCL(1);为高电平,在SCL为高电平期间,拉高SDA:SET_SDA(1);认为是停止信号。
I2C_ACK()

等待接收ACK信号函数。当一次数据发送完成后从机会返回一个ACK应答信号。对于主机来说,当SDA为低电平时,SCL由低电平转为高电平后,从机释放SDA为输入。
I2C_Send_Byte()

写入8bit数据函数。写入8bit 函数实际为循环写入8次1bit数据。在发出start信号后,每来一次SCL高电平信号时,视为写入此时SDA电平数据,当SET_SDA(1);视为写入1,当SET_SDA(0);视为写入0。循环8次后为写入1byte数据。
TM1650_Brightness_Set()

根据数据手册可知,写入地址0x48为控制命令设置,写入0x48指令后紧跟的数据为控制亮度段数,开关控制数据。
TM1650_Display()

根据数据手册,写入地址0x68为显示数据存储地址,对应第一个数码管显示,0x6A对应第二个数码管的显存地址,输入地址数据后紧接一个8位数据,即可点亮对应数码管显示。
DATA_NUM/TM1650_DOT
数码管显示0~9的数据显示/数码管显示小数点。
TM1650_display_float()
4位数码管显示2位小数浮点型数据,用来显示测量的距离。输入浮点型参数,处理后显示在4位数码管上。
超声波测距代码
代码
c
/**
* @brief Main program
* @param None
* @retval 0 if success, otherwise failure number
*/
int main(void)
{
HC_SR04_init();
TM1650_GPIO_init();
TM1650_Brightness_Set(0x01); // Set brightness to maximum
while (1)
{
TM1650_display_float(HC_SR04_Measure());
rt_thread_mdelay(1000);
}
return RT_EOK;
}
分析
Main 函数
超声波测距主函数,先初始化HC-SR04超声波模块和TM1650 4位数码管模块。初始化完成设置数码管亮度等级为8级,显示段数为8段,并开启显示,只有这样数码管才能显示。在while循环中反复执行HC_SR04_Measure 超声波测距代码,并用TM1650_display_float函数显示在数码管上。
3.3 设计步骤
-
- 4位数码管显示驱动
-
- HC-SR04 超声波驱动
-
- 距离计算
-
- 数据显示
-
- 结果分析
3.4 模块仿真结果和分析

结果分析:分析显示和测量结果,测量数据显示接近实际距离。
- 结果分析
四、 总结
本次课程设计项目:超声波测距模块。使用主控MCU为SFLB52系列开发板。与以为直接使用共阴极数码管显示模块显示,这里使用了由TM1650芯片控制的数码管模块,两者之间的差别是,以往数码管模块需要多条数据线控制,本模块使用I2C主线来控制显示数据。配合超声波模块,由MCU计算距离,完成整体模块设计。其中原定使用SF32LB52硬件I2C驱动模块该数码管显示模块,但是硬件I2C驱动无法完整显示出TM1650 4位数码管I2C的时序控制,因此后续改为模拟I2C,使用软件模拟I2C的形式,更加直观的展示出数据手册中的数据传输时序。最终超声波测距装置完成,以超声波模块、主控MCU、数码管显示模块三部分组成。