作者:鸿蒙元气
团队:坚果派
欢迎通过主页或者私信联系我或者联系坚果,加入坚果派,一起学习 OpenHarmony/HarmonyOS 应用开发。
今天在学习设备开发的时候,也是遇到一些问题,按照我的风格,肯定是要记录一下的,那么我们就开始今天的内容
有时候,接口不够用,针对 GPIO 口复用,
这时候就需要使用 hi_io_set_func 函数;以 GPIO9 为例,设为普通 IO;
#define TEST_GPIO 9 // for hispark_pegasus
#define GPIO_FUNC 0 // for hispark_pegasus
hi_io_set_func(TEST_GPIO,GPIO_FUNC);
复制代码
1.基本 IO 功能
输出
分两步,使能、配置输出值;
IoTGpioInit(GPIO_idx);
IoTGpioSetDir(GPIO_idx,1);//1-输出 0-输入
IoTGpioSetOutputVal(GPIO_idx,1); //0-低 1-高
复制代码
输入
分三步,使能、配置输入方式(上下拉)、获取 io 电平;
IoTGpioInit(GPIO_idx);
IoTGpioSetDir(GPIO_idx,0);//1-输出 0-输入
hi_io_set_pull(GPIO_idx, HI_IO_PULL_UP); //==上拉输入
IoSetPull(GPIO_idx, HI_IO_PULL_UP); //自行封装在iot_gpio.h
IoTGpioGetInputVal(GPIO_idx,1); //0-低 1-高
复制代码
系统延时
系统延时函数比较多,具体区别看使用效果。常用的有
usleep(us); // delay us
osDelay(us);// delay us
hi_udelay(us);// delay_us int32
复制代码
2.PWM 输出
Hi3861 共有 6 路 PWM。Hi3861-2.0 版本 PWM 使用流程:
初始化 io
io 复用为 pwm
使能 pwm 通道
开始输出 pwm
停止输出 pwm
//==初始化 GPIO9
IoTGpioInit(PWM0_TEST_GPIO);
//==初始化 GPIO9 的pwm复用功能
hi_io_set_func(PWM0_TEST_GPIO,HI_IO_FUNC_GPIO_9_PWM0_OUT);
//==初始化pwm通道0
IoTPwmInit(PWM0);
//==配置pwm0输出参数:占空比50%、频率160M/80000=2KHz
IoTPwmStart(PWM0,50,80000);
IoTPwmStop(PWM0); //以上pwm操作基于iot_pwm.h 封装的函数,实质是封装hi_pwm.h
hi_pwm_init(hi_pwm_port port);
hi_pwm_start(hi_pwm_port port, hi_u16 duty, hi_u16 freq);
hi_pwm_stop(hi_pwm_port port); //以上pwm操作基于hi_pwm.h 封装的函数
复制代码
2.1 PWM 常用 API
iot_pwm.h 封装的函数中,与 hi_pwm.h 封装的函数入口参数不同;
iot_pwm.h 中定义的 IoTPwmStart();
unsigned int IoTPwmStart(unsigned int port, unsigned short duty, unsigned int freq)
port:0-5 pwm通道号
duty:1-99 占空比,不可为0\100
freq:>160M/65535 = 2441 与pwm频率有关,不是直接设置
unsigned int IoTPwmStart(unsigned int port, unsigned short duty, unsigned int freq)
{
unsigned short hiDuty;
unsigned short hiFreq;
hiFreq = (unsigned short)(CLK_160M / freq);
hiDuty = (duty * hiFreq) / DUTY_MAX;
return hi_pwm_start((hi_pwm_port)port, hiDuty, freq);
// 源码: return hi_pwm_start((hi_pwm_port)port, hiDuty, hiFreq);
//最后一个参数为分频系数,传入时不能先使用160M/freq 这样不对, 2021.09.26 自信修正
}
复制代码
hi_pwm.h 中定义的 hi_pwm_start();
按配置的参数输出PWM信号。PWM信号占空比=duty/freq。频率=时钟源频率/freq。
* @param port [IN] PWM端口号。
* @param duty [IN] PWM占空比计数值。取值范围为:[1, 65535]。默认值为750。CNend
* @param freq [IN] 分频倍数。取值范围为:[1, 65535]。默认值为1500。
hi_u32 hi_pwm_start(hi_pwm_port port, hi_u16 duty, hi_u16 freq);
复制代码
可以看出,函数封装后可更方便的使用PWM,直接设置占空比(1-99)。但发现iot_pwm.h封装的IoTPwmStart()频率有错误,导致pwm不正常,原因是在IoTPwmStart()中,hiFreq已经是计算后的实际频率,不是分频系数。给hi_pwm_start()传入数据时,不应该传入hiFreq,应该还是最初的freq。我已修正该封装,顺利驱动了蜂鸣器。
1.
复制代码
pwm 总结,无论使用 IoTPwmStart()还是 hi_pwm_start(),实际输出频率 hiFreq:
hiFreq = (unsigned short)(CLK_160M / freq);
//注:hi_u32 hi_pwm_set_clock(hi_pwm_clk_source clk_type); //==设置时钟源,默认160M
复制代码
pwm 总结,使用 IoTPwmStart(port,duty,freq)实际占空比 hiDuty:
hiFreq = (unsigned short)(CLK_160M / freq);
hiDuty = (duty * hiFreq) / 100; //所占总周期的百分比
复制代码
pwm 总结,使用 hi_pwm_start(port,duty,freq)实际占空比 hiDuty:
hiDuty = (duty) / 65535;
复制代码
2.2 如何控制舵机
hi3861-PWM 其他信息 –理论频率 [1, 65.535]KHz (CLK_160M / freq) < 0xFFFF(65535) 分频系数 freq [2442,160M] –实际频率不会很低,具体没测,没示波器, 数字舵机(T=20ms)无法控制。 模拟低频 pwm: 延时+io 输出(0-1)模拟实现 控制精度要求不是很高可以这么做。期待官方出解决方案。
3.ADC 实现
Hi3861 共有 7 路 ADC , 各通道见 hi3861 手册表 6.2(截取如下) 一共 8 个 ADC 通道,ADC0–ADC7 但通道 7 为参考电压,不能 adc 转换。
3.1 ADC 常用 API
Hi3861-2.0 版本 ADC 使用流程:1.初始化 io、2. io 为输入、3. 配置输入模式、4. 读取 AD 数据。
IoTGpioInit(pinID);
IoTGpioSetDir(pinID,IOT_GPIO_DIR_IN);
hi_io_set_func(pinID,HI_IO_FUNC_GPIO_pinID_GPIO);
//可选,上拉等:
//IoSetPull(pinID, HI_IO_PULL_UP);
hi_io_set_pull(pinID,HI_IO_PULL_UP);
hi_adc_read(HI_ADC_CHANNEL_0,&data,HI_ADC_EQU_MODEL_1,HI_ADC_CUR_BAIS_DEFAULT, 0);
复制代码
3.2 ADC 使用注意事项
Hi3861-2.0 版本 i2c hi_adc_read()参数解析
hi_u32 hi_adc_read(hi_adc_channel_index channel, hi_u16 *data,
hi_adc_equ_model_sel equ_model, hi_adc_cur_bais cur_bais, hi_u16 delay_cnt);
channel: 0-7,通道0-7
*data :采集的AD数据
equ_model:平均算法模式,将采集后的数据平均再使用,取1就是原始数据,不平均。
cur_bais: 模拟电源控制,一般设置为默认
delay_cnt:从配置采样到启动采样的延时时间计数,一次计数是334ns,其值需在0~0xFF0之间
复制代码
获取数据是指针传值。
4. 驱动 i2c
4.1 i2c API
Hi3861-2.0 版本 i2c 使用流程(两路 i2c):1.初始化 io、2. 复用 io 为 i2c、3. 初始化对应 i2c 通道、4. 读取/发送数据。
//==i2c1
IoTGpioInit(I2C1_SDA_IO13);
IoTGpioInit(I2C1_SCL_IO14); //初始化io接口
hi_io_set_func(I2C1_SDA_IO13, HI_IO_FUNC_GPIO_13_I2C0_SDA);
hi_io_set_func(I2C1_SCL_IO14 , HI_IO_FUNC_GPIO_14_I2C0_SCL); //==GPIO 复用为I2C0
hi_i2c_init(OLED_I2C_IDX, OLED_I2C_BAUDRATE); //==使能i2c1设置传输速率 OLED_I2C_IDX值为1
hi_i2c_write((hi_i2c_idx)id, OLED_I2C_ADDR, &i2cData);
//==i2c0
IoTGpioInit(MPU_SDA_IO0);
IoTGpioInit(MPU_SCL_IO1);
hi_io_set_func(MPU_SDA_IO0, HI_IO_FUNC_GPIO_13_I2C0_SDA);
hi_io_set_func(MPU_SCL_IO1, HI_IO_FUNC_GPIO_14_I2C0_SCL);
hi_i2c_init(MPU_I2C_IDX, MPU_I2C_BAUDRATE); //==使能i2c1设置传输速率 OLED_I2C_IDX值为0
hi_i2c_write((hi_i2c_idx)id, ((MPU_ADDR<<1)|0), &i2cData); //==发送器件地址+写命令+目标寄存器reg + 写入的data
hi_i2c_read((hi_i2c_idx)id,((MPU_ADDR<<1)|1), &i2cData); //==发送器件地址+读命令+目标寄存器reg + 读取的data
复制代码
4.2 i2c 使用提示
Hi3861-2.0 版本 i2c 一些重要定义:
读取、发送的数据是一个结构体; 数据均使用指针的形式传值; 定义的函数每次只能收、发 8 位数据;
i2c 函数的一些思考:
如果读取一个寄存器地址为 16bit 的器件怎么办?----截断,分两次发送; 对 mpu6050 发、收数据一次需要发送两个地址(设备地址、目标寄存器地址),如何实现?---把第二个地址当作数据发送。
小总结
基本 IO 功能
I2C 相关 API
PWM 相关 API
ADC 相关 API 常用的头文件
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h" //==系统依赖 包括usleep() osdelay()
#include "iot_gpio.h" //==IoTGpioInit()、IoTGpioSetDir()、IoTGpioSetOutPutVal()
#include "iot_pwm.h" //==IoTPwmStart()、IoTPwmInit()
#include "hi_io.h" //==hi_io_set_func()、hi_io_set_pull()
#include "hi_pwm.h" //==hi_pwm_start()
#include "hi_adc.h" //==hi_adc_read()
#include "hi_time.h" //==hi_udelay()
复制代码
评论