1# 小凌派-RK2206开发板E53模块开发——智慧车载 2 3本示例将演示如何在小凌派-RK2206开发板上实现智慧车载的应用案例。 4 5 6 7## 硬件资源 8 9硬件资源图如下所示: 10 11 12EEPROM 24C02的设备地址为:0x1010001* ; 13 14## 硬件接口说明 15 16引脚名称开发者可在硬件资源图中查看,也可在智慧车载模块背面查看。 17 18| 引脚名称 | 功能描述 | 19| :---------- | :------------------------------------------- | 20| ECHO | 测距脉宽输出,高电平的宽度代表超声波往返时间 | 21| TRIG | 测距触发,输入10us的高电平脉冲,开始测距 | 22| BUZZER | 蜂鸣器控制线,推荐3KHz的方波信号 | 23| LED_Warning | LED控制线,低电平有效 | 24| I2C_SCL | I2C时钟信号线 | 25| I2C-SDA | I2C数据信号线 | 26| GND | 电源地引脚 | 27| 3V3 | 3.3V电源输入引脚 | 28| GND | 电源地引脚 | 29 30## 硬件设计 31 32硬件电路如下图所示: 33 34 35模块整体硬件电路如上图所示,电路中包含了E53接口连接器,EEPROM存储器、超声波处理电路,声光报警电路。 36 37超声波测距芯片,选用CS-100A,其是一款工业级超声波测距芯片,内部集成超声波发射电路,超声波接收电路,数字处理电路等,单片即可完成超声波测距,测距结果通过脉宽的方式进行输出。 38 39CS100A配合使用40KHz的开放式超声波探头,在超声波发射端并联一个电阻R2到地和8MHz的晶振,即可实现高性能的测距功能,电阻R2的大小决定了超声波测量的距离。 40 41三极管Q1为NPN管,基极为高电平时,三极管才能够导通,蜂鸣器需PWM波驱动,人耳可识别的频率范围为20Hz-20KHz,故PWM频率需在该范围内,我们默认使用3KHz的PWM波驱动。 42 43### 硬件连接 44 45小凌派开发板与模块均带有防呆设计,故很容易区分安装方向,直接将模块插入到开发板的E53母座接口上即可,安装图如下所示: 46 47 48## 程序设计 49 50### API分析 51 52### GPIO接口 53 54[GPIO接口文档](/device/rockchip/hardware/docs/GPIO.md) 55 56### PWM接口 57 58[PWM接口文档](/device/rockchip/hardware/docs/PWM.md) 59 60### LiteOS信号量 61 62[LiteOS信号量接口文档](/vendor/lockzhiner/lingpi/samples/a2_kernel_semaphore/README_zh.md) 63 64## 主要代码分析 65 66### 初始化代码分析 67 68**初始化GPIO** 69 70初始化GPIO0_PC4和GPIO0_PA5为GPIO。首先通过 `LzGpioInit()`初始化GPIO引脚,其次通过 `LzGpioSetDir()`设置GPIO为输出模式,最后通过 `LzGpioSetVal()`设置输出高电平/低电平。 71 72```c 73/*************************************************************** 74 * 函数名称: e53_iv01_init_gpio 75 * 说 明: 初始化GPIO 76 * 参 数: 无 77 * 返 回 值: 无 78 ***************************************************************/ 79static void e53_iv01_init_gpio() 80{ 81 /* Trig引脚设置为GPIO输出模式 */ 82 PinctrlSet(E53_IV01_TRIG_GPIO, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP); 83 LzGpioInit(E53_IV01_TRIG_GPIO); 84 LzGpioSetDir(E53_IV01_TRIG_GPIO, LZGPIO_DIR_OUT); 85 E53_IV01_TRIG_Clr(); 86 87 /* LED_WARNING灯引脚设置为GPIO输出模式 */ 88 PinctrlSet(E53_IV01_LED_WARNING_GPIO, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP); 89 LzGpioInit(E53_IV01_LED_WARNING_GPIO); 90 LzGpioSetDir(E53_IV01_LED_WARNING_GPIO, LZGPIO_DIR_OUT); 91 e53_iv01_led_warning_set(0); 92} 93``` 94 95**初始化GPIO中断** 96 97初始化GPIO0_PA2为GPIO。首先通过 `LzGpioInit()`初始化GPIO引脚,其次通过 `LzGpioSetDir()`设置GPIO为输入模式,最后通过 `LzGpioRegisterIsrFunc()`设置该引脚为中断模式,触发模式为边沿触发(即上升沿+下降沿都可触发)。 98 99```c 100/*************************************************************** 101 * 函数名称: e53_iv01_init_interrupt 102 * 说 明: 初始化GPIO中断 103 * 参 数: 无 104 * 返 回 值: 无 105 ***************************************************************/ 106static void e53_iv01_init_interrupt() 107{ 108 LzI2cInit(0, 400000); 109 110 /* 创建信号量 */ 111 LOS_SemCreate(0, &m_task_sem); 112 113 /* Echo引脚设置为GPIO输出模式 */ 114 PinctrlSet(E53_IV01_ECHO0_GPIO, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP); 115 LzGpioInit(E53_IV01_ECHO0_GPIO); 116 LzGpioSetDir(E53_IV01_ECHO0_GPIO, LZGPIO_DIR_IN); 117 LzGpioRegisterIsrFunc(E53_IV01_ECHO0_GPIO, GPIO_INT_EDGE_BOTH, e53_iv01_interrupt_echo_func, NULL); 118} 119``` 120 121**初始化PWM** 122 123初始化GPIO0_PC7为GPIO。首先通过 `PwmIoInit()`初始化PWM配置,最后通过 `LzPwmInit()`初始化PWM配置id。 124 125```c 126/*************************************************************** 127 * 函数名称: e53_iv01_init_pwm 128 * 说 明: 初始化PWM 129 * 参 数: 无 130 * 返 回 值: 无 131 ***************************************************************/ 132static unsigned int e53_iv01_init_pwm() 133{ 134 /* 初始化pwm */ 135 PinctrlSet(E53_IV01_BUZZER_GPIO, MUX_FUNC2, PULL_DOWN, DRIVE_KEEP); 136 if (PwmIoInit(m_buzzer_config) != LZ_HARDWARE_SUCCESS) 137 { 138 printf("%s, %d: PwmIoInit failed\n", __func__, __LINE__); 139 return __LINE__; 140 } 141 if (LzPwmInit(E53_IV01_PWM_IO) != LZ_HARDWARE_SUCCESS) 142 { 143 printf("%s, %d: LzPwmInit failed\n", __func__, __LINE__); 144 return __LINE__; 145 } 146 147 return 0; 148} 149``` 150 151### 车载测距流程代码分析 152 153首先,初始化相关变量,使能GPIO中断,并控制TRIG引脚往E53模块发送一个至少10usec的高电平,通知E53模块开始工作。具体代码如下所示: 154 155```c 156/* 初始化采集相关信息 */ 157m_echo_info.flag = EECHO_FLAG_CAPTURE_RISE; 158/* 使能GPIO中断 */ 159LzGpioEnableIsr(E53_IV01_ECHO0_GPIO); 160/* 等待1msec */ 161LOS_Msleep(1); 162/* 发送10Usec的高电平 */ 163e53_iv01_send_trig(); 164``` 165 166其次,等待信号量(该信号量最长等待时间为200msec)。具体代码如下所示: 167 168```c 169/* 等待200msec(最长等待时间),整个测距最长为66msec */ 170LOS_SemPend(m_task_sem, LOS_MS2Tick(200)); 171``` 172 173在GPIO中断处理函数中记录E53模块用ECHO引脚发送过来的一个高电平时间宽度(该高电平的时间宽度为超声波来回的时间宽度)。具体实现方式为: 174 175* 上升沿触发时,记录系统时钟(即定时器5)的当前节拍数,修改触发状态; 176* 下降沿出发时,记录系统时钟(即定时器5)的当前节拍数,修改触发状态,释放信号量。 177 178具体代码如下: 179 180```c 181/*************************************************************** 182 * 函数名称: e53_iv01_interrupt_echo_func 183 * 说 明: GPIO中断处理函数,通过中断捕获定时器计数器当前值 184 * 参 数: 185 * @arg, 186 * 返 回 值: 无 187 ***************************************************************/ 188static VOID e53_iv01_interrupt_echo_func(VOID *arg) 189{ 190 if (m_echo_info.flag == EECHO_FLAG_CAPTURE_RISE) 191 { 192 /* 获取上升沿的定时器计数器当前值 */ 193 m_echo_info.time_rise = *m_ptimer5_current_value_low; 194 m_echo_info.flag = EECHO_FLAG_CAPTURE_FALL; 195 } 196 else if (m_echo_info.flag == EECHO_FLAG_CAPTURE_FALL) 197 { 198 /* 获取下降沿的定时器计数器当前值 */ 199 m_echo_info.time_fall = *m_ptimer5_current_value_low; 200 m_echo_info.flag = EECHO_FLAG_CAPTURE_SUCCESS; 201 LOS_SemPost(m_task_sem); 202 } 203 else 204 { 205 /* 什么都不做 */ 206 } 207} 208``` 209 210然后禁用GPIO中断,判断中断状态判断E53模块是否采集成功。具体代码如下: 211 212```c 213/* 禁用GPIO中断 */ 214LzGpioDisableIsr(E53_IV01_ECHO0_GPIO); 215 216if (m_echo_info.flag == EECHO_FLAG_CAPTURE_SUCCESS) 217{/* 如果是采集成功,则计算距离 */ 218 if (m_echo_info.time_rise <= m_echo_info.time_fall) 219 { 220 time_diff = m_echo_info.time_fall - m_echo_info.time_rise; 221 } 222 else 223 { 224 time_diff = 0xFFFFFFFF - m_echo_info.time_rise + m_echo_info.time_fall + 1; 225 } 226 227 e53_iv01_calc_cm(time_diff, ECHO_TIMER_FREQ, distance_cm); 228 return 1; 229} 230else 231{ 232 printf("%s, %d: flag(%d) is error!\n", __func__, __LINE__, m_echo_info.flag); 233 return 0; 234} 235``` 236 237最后,根据上升沿和下降沿的节拍数差计算距离。其中,系统时钟为40MHz,超声波速度为340米/秒,高电平时间宽度为超声波的往返之和,所以实际距离 = 节拍数差 / 40MHz / 340(米/秒) / 2(往返2次)。具体计算代码如下: 238 239```c 240/*************************************************************** 241 * 函数名称: e53_iv01_calc_cm 242 * 说 明: 计算超声波测距 243 * 参 数: 244 * @time:节拍数 245 * @freq:节拍频率,以Hz为单位 246 * @meter:距离差,以厘米为单位 247 * 返 回 值: 无 248 ***************************************************************/ 249static inline void e53_iv01_calc_cm(uint32_t time, uint32_t freq, float *cmeter) 250{ 251 float f_time = (float)time; 252 float f_freq = (float)freq; 253 254 /* 距离 = 时间差 * 340米/秒 / 2(超时波来回2次) * 100厘米/米 */ 255 *cmeter = f_time / f_freq * 170.0 * 100.0; 256} 257``` 258 259## 编译调试 260 261### 修改 BUILD.gn 文件 262 263修改 `vendor/lockzhiner/lingpi/sample` 路径下 BUILD.gn 文件,指定 `e53_iv01_example` 参与编译。 264 265```r 266"e53_intelligent_vehicle_01", 267``` 268 269在主目录下输入编译命令。 270 271```shell 272hb build -f 273``` 274 275### 运行结果 276 277示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,并请使用带有LCD屏幕显示如下: 278 279```c 280========== E53 IV Example ========== 281distance cm: 23.92 282========== E53 IV Example ========== 283distance cm: 23.89 284========== E53 IV Example ========== 285distance cm: 23.90 286``` 287