US-016模拟量超声波传感器与TI TMS320F28P550开发板ADC驱动移植实战
US-016模拟量超声波传感器与TI TMS320F28P550开发板ADC驱动移植实战最近在做一个智能小车的项目需要用到超声波测距来避障。市面上常见的超声波模块大多是数字接口比如SR04那种触发-回响模式调试起来时序要求比较严格。后来发现了US-016这个模块它直接输出模拟电压距离越远电压越高用起来就像读一个电位器一样简单特别适合咱们这种需要快速上手的场景。今天我就手把手带大家把US-016超声波传感器接到TI的TMS320F28P550这块DSP开发板上从硬件连接到软件驱动把整个流程走一遍。就算你之前没怎么用过C2000系列的ADC跟着这篇教程做也能轻松搞定。1. 认识我们的“主角”US-016超声波模块在动手接线写代码之前咱们先花几分钟了解一下US-016这个传感器。它和常见的超声波模块不太一样核心就四个字模拟输出。它怎么工作的模块上电后会自己不停地发射超声波并接收回波然后根据测量到的距离在它的Out引脚输出一个对应的模拟电压。这个电压值和距离是成正比的也就是说物体离得越远输出电压越高。咱们的DSP开发板只需要用一个ADC模数转换器引脚去读取这个电压值再通过一个简单的公式换算一下就能得到距离了省去了处理复杂时序的麻烦。关键参数一览为了后续设计和调试心里有数咱们先把模块的主要规格列出来参数规格工作电压3.3V - 5V (和咱们开发板的3.3V系统兼容)工作电流约3.8mA (非常省电)测量范围2厘米 ~ 3米测量精度0.3厘米 测量值的1%输出信号模拟电压 (0V ~ Vcc)感应角度 15度 (指向性比较好)引脚数量4个 (VCC, GND, Out, Range)这里要特别关注一下Range引脚它决定了模块的量程Range悬空不接或者接高电平量程为3米。这是最常用的模式。Range接低电平GND量程变为1米。量程变小后在1米内的测量分辨率会更高一些。咱们这次实验就让Range引脚悬空使用3米量程。资料从哪里找原始资料里提到了模块的采购链接和资料下载网盘。建议大家拿到模块后最好也找卖家要一下最新的资料里面通常有更详细的数据手册和应用说明。2. 硬件连接把传感器“插”到开发板上硬件连接非常简单只有4根线。咱们使用开发板上的3.3V供电这样传感器输出信号的电平就和DSP的ADC输入范围匹配了不需要电平转换。连接关系表US-016模块引脚连接到 TMS320F28P550 开发板说明VCC3V3引脚提供3.3V电源GNDGND引脚共地非常重要OutGPIO_A6引脚模拟信号输出接ADC输入通道Range不连接 (NC)悬空设置为3米量程注意一定要确保GND连接可靠这是所有信号参考的基准。如果地线没接好ADC读到的值会乱跳。为什么选 GPIO_A6TMS320F28P550的很多GPIO引脚都复用了ADC输入功能。我选择GPIO_A6是因为它在开发板的引脚排针上比较容易找到并且对应的ADC通道是ADCINA6。你完全可以根据自己板子的布线情况选择其他带有ADC功能的引脚比如GPIO_A0、GPIO_B0等等只要在软件配置里改一下就行。3. 软件配置让开发板认识这个传感器硬件接好了接下来就要在代码里告诉开发板“请把GPIO_A6引脚当成ADC输入来用”。对于TI C2000系列芯片我强烈推荐使用SysConfig图形化工具来配置非常直观不容易出错。SysConfig配置步骤打开工程在你的CCS工程里找到并双击c2000.syscfg这个文件。它会自动打开SysConfig配置界面。添加ADC配置在界面里找到“PinMux”引脚复用或“Peripherals”外设相关的部分点击ADD来添加一个新的配置项。配置引脚功能找到GPIO_A6这个引脚。将其功能从默认的“GPIO”修改为“ADC”。指定其作为ADCINA6ADC-A组的第6通道使用。保存并生成代码按下Ctrl S保存配置。系统可能会自动或在你手动构建工程Ctrl B时根据你的配置生成或更新代码。提示第一次构建时可能会有些关于驱动库版本的警告只要不是错误Error通常可以忽略。我们的目的是让配置生效。配置完成后你会发现在工程里生成了一个board.h文件。这个文件里就包含了GPIO_A6作为ADCINA6的所有宏定义。因为我们的工程模板已经包含了tjx_init.h而tjx_init.h又包含了board.h所以后续我们直接在代码里包含tjx_init.h就能使用这些定义非常方便。4. 驱动代码编写核心ADC读取与距离换算配置搞定重头戏来了——写驱动代码。咱们在工程里新建一个文件夹module_driver专门放各种传感器驱动。在里面创建两个文件bsp_us016.c和bsp_us016.h。4.1 头文件定义 (bsp_us016.h)头文件很简单主要定义采样次数和声明函数。#ifndef _BSP_US016_H_ #define _BSP_US016_H_ #include tjx_init.h // 采样次数为了抗干扰我们取5次平均值 #define SAMPLES 5 // 函数声明获取距离单位毫米 float Get_US016_Distance(void); #endif4.2 源文件实现 (bsp_us016.c)这里包含了三个核心函数咱们一个一个看。第一步读取一次ADC原始值这个函数负责触发一次ADC转换并读取结果。这是最底层的数据获取。#include bsp_us016.h #include board.h #include stdio.h // 根据硬件连接Range悬空所以我们使用3米量程 #define RANGE 0 /* [0]量程3米 [1]量程1米 */ /** * brief 读取一次ADC数据 * param 无 * retval ADC原始值 (0-4095) * note 使用软件触发SOC0进行转换 */ static uint16_t ADC_GET(void) { uint16_t gAdcResult 0; uint16_t timeOut 1000; // 超时计数器 // 1. 软件触发ADC开始转换使用SOC0 ADC_forceMultipleSOC(Module_ADC_BASE, Module_ADC_FORCE_SOC0); // 2. 等待ADC转换完成 while(ADC_isBusy(Module_ADC_BASE) timeOut--) { delay_us(100); // 短暂延时 } // 3. 检查是否超时 if(!timeOut) { lc_printf(ADC_GET Failed!!!\r\n); return 0; } // 4. 读取SOC0通道的转换结果 gAdcResult ADC_readResult(Module_ADC_RESULT_BASE, Module_ADC_SOC0); return gAdcResult; }第二步获取稳定的ADC值传感器输出可能会有微小波动直接读一次值不准确。我们采用最简单的办法——多次采样取平均。/** * brief 获取US-016的ADC平均值 * param 无 * retval 多次采样平均后的ADC值 */ unsigned int Get_US016_Value(void) { uint16_t Data 0; int i; // 循环采样SAMPLES次这里是5次 for(i 0; i SAMPLES; i) { Data ADC_GET(); // 累加每次的ADC值 delay_ms(5); // 每次采样间隔5ms让传感器有足够时间更新 } Data Data / SAMPLES; // 计算平均值 return Data; }第三步将ADC值换算为实际距离核心算法这是最关键的一步。US-016的输出电压Vout与距离L成正比公式在资料里给出了3米量程L (mm) 3096 * Vout / Vcc1米量程L (mm) 1024 * Vout / Vcc其中Vcc是模块供电电压我们接的3.3VVout是输出电压。我们的ADC是12位的参考电压Vref也是3.3V。所以对于ADC来说它读到的数字量A满足A / 4096 Vout / Vref。因为Vref Vcc 3.3V所以公式可以简化。把Vout (A * Vref) / 4096代入距离公式以3米量程为例L 3096 * (A * Vref / 4096) / Vcc 3096 * A / 4096计算3096 / 4096 ≈ 0.75586代码里我用0.75f这是一个很好的近似计算更快。/** * brief 读取US-016测量的距离 * param 无 * retval 距离值单位毫米 */ float Get_US016_Distance(void) { float distance 0; unsigned int d Get_US016_Value(); // 获取平均ADC值 // 根据量程选择换算系数 #if !RANGE // RANGE为03米量程 distance (float)d * 0.75f; // 3096/4096 ≈ 0.75 #else // RANGE为11米量程 distance (float)d * 0.25f; // 1024/4096 0.25 #endif return distance; // 返回的单位是毫米(mm) }5. 上机测试让代码跑起来看效果驱动写好了最后就是把它用起来。我们在主函数里调用它并通过串口打印出距离信息。#include driverlib.h #include device.h #include board.h #include c2000ware_libraries.h #include tjx_init.h #include bsp_us016.h // 包含我们刚写的驱动头文件 void main(void) { // 以下是CCS自动生成的系统初始化代码 Device_init(); Device_initGPIO(); Interrupt_initModule(); Interrupt_initVectorTable(); Board_init(); C2000Ware_libraries_init(); EINT; ERTM; lc_printf(\r\n US-016 Ultrasonic Sensor Test \r\n); while(1) { // 调用函数获取距离单位毫米 float distance_mm Get_US016_Distance(); // 将毫米转换为厘米和毫米两部分打印更直观 int cm (int)distance_mm / 10; int mm (int)distance_mm % 10; lc_printf(Distance: %d.%d cm\r\n, cm, mm); // 这里可以加一些LED闪烁指示表示程序在运行 delay_ms(500); // 每500ms测量一次 } }下载并运行将代码编译下载到TMS320F28P550开发板打开串口助手波特率通常为115200你就能看到不断打印的距离信息了。用手在传感器前方移动观察输出的距离值是否随之变化。调试小技巧数值不对首先检查硬件连接尤其是地和电源。然后用万用表测量一下传感器Out引脚和开发板3V3引脚的电压是否正常。数值跳动大可以尝试增加bsp_us016.h中的SAMPLES采样次数比如从5改成10。或者在Get_US016_Value函数里将delay_ms(5)适当延长。量程切换测试如果想测试1米量程可以把模块的Range引脚接到开发板的GND然后把bsp_us016.c文件开头的#define RANGE 0改成#define RANGE 1重新编译测试。你会发现近距离的数值变化更灵敏了。好了整个移植过程就是这样。从硬件连接到软件驱动再到最后的测试验证咱们一步步走下来是不是发现用模拟量超声波模块也没那么复杂关键在于理解ADC采样和那个距离换算公式。希望这篇教程能帮你快速把US-016用起来。