【FFH】BearPi_Micro业务和驱动代码_GPIO口使用 原创 精华
业务代码and驱动代码框架
业务代码
-
.c文件编写业务代码
-
BUILD.gn把第一步中的业务代码打包成可执行文件,再把可执行文件打包成小组件
-
applications.json将小组件打包成名为my_sample组件
-
/vendor/…/config.json编译构建组件
驱动的使用
业务代码
1.获取驱动
HdfIoServiceBind(驱动名称)
#驱动对外发布服务的名称,在device_info.hcs里查询
2. 申请内存
HdfSBufObtainDefaultSize()
申请发送数据data和接受数据reply的内存
3.将业务代码的数据翻译成驱动代码的数据(翻译数据)
HdfSbufWriteUint8(1,2)
将参数二写入参数一,参数一将被步骤四带到驱动代码中
4. 发送和接受驱动的数据
serv->dispatcher->Dispatch(&serv->object,操作确认符, data, reply)
数据通过Dispatch发送接收数据
*serv是第一步HdfIoServiceBind()
的返回值
5. 翻译驱动代码发来的数据(翻译数据)
HdfSbufReadInt32(1, &2)
将参数一(驱动在步骤四发送来的数据)写入参数二,参数二为业务代码可以查看的数据(参数二为地址)
6. 释放申请的内存
HdfSBufRecycle()
将第二步申请的data和reply内存释放
代码如下:
#define LED_WRITE_READ 1
#define LED_SERVICE "hdf_led"
static int SendEvent(struct HdfIoService *serv, uint8_t eventData)
{
int ret = 0;
//用HdfSBufObtainDefaultSize申请内存,这种类型的内存才能用于和驱动程序交换数据
struct HdfSBuf *data = HdfSBufObtainDefaultSize();
if (data == NULL)
{ //报错情况
printf("fail to obtain sbuf data!\r\n");
return 1;
}
struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
if (reply == NULL)
{ //报错情况
printf("fail to obtain sbuf reply!\r\n");
ret = HDF_DEV_ERR_NO_MEMORY;
goto out;
}
/* 写入数据,将参数二的数据写入参数一中*/
//将eventData内的数据写入到data里面
if (!HdfSbufWriteUint8(data, eventData))
{ //报错情况
printf("fail to write sbuf!\r\n");
ret = HDF_FAILURE;
goto out;
}
/* 参数二为1,判断读写操作码;参数三data通过Dispatch发送到驱动;同时驱动会把数据写入到参数四reply */
ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE_READ, data, reply);
if (ret != HDF_SUCCESS)
{ //报错情况
printf("fail to send service call!\r\n");
goto out;
}
int replyData = 0;
/* 读取驱动的回复数据 */
if (!HdfSbufReadInt32(reply, &replyData))
{
printf("fail to get service call reply!\r\n");
ret = HDF_ERR_INVALID_OBJECT;
goto out;
}
printf("\r\nGet reply is: %d\r\n", replyData);
out: //回收先前为和驱动交换数据而申请的内存
HdfSBufRecycle(data);
HdfSBufRecycle(reply);
return ret;
}
int main(int argc, char **argv)
{
int i;
/* 获取服务 */
//获取驱动的服务,获取到服务后通过服务中的Dispatch方法向驱动发送消息。
//在HDF框架定义的device_info.hcs配置文件中有对应的设备描述,每一个设备驱动都有对外发布服务的唯一名称
struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE);
if (serv == NULL) //当获取服务不成功时
{
printf("fail to get service %s!\r\n", LED_SERVICE);
return HDF_FAILURE; //返回-1
}
for (i = 0; i < argc; i++) //打印输入的数据
{
printf("\r\nArgument %d is %s.\r\n", i, argv[i]);
}
//给驱动发送数据
SendEvent(serv, atoi(argv[1]));
HdfIoServiceRecycle(serv); // 回收IoService对象(退订服务)
printf("exit");
return HDF_SUCCESS; //返回0
}
驱动代码
新建代码:device\st\drivers\新建代码文件夹\驱动代码+BUILD.gn
- 将驱动入口注册到HDF框架中
HDF_INIT(驱动入口的对象)
驱动入口的对象必须为HdfDriverEntry类型的全局变量
struct HdfDriverEntry g_ledDriverEntry = {
.moduleVersion = 1, //版本号
.moduleName = "HDF_LED", //驱动名称
.Bind = HdfLedDriverBind, //驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
.Init = HdfLedDriverInit, // 驱动自身业务初始的接口
.Release = HdfLedDriverRelease, // 驱动资源释放的接口
};
-
在Dispatch中处理用户态发下来的消息
LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
对应参数为业务代码中的第四步- 翻译业务代码带来的数据(翻译数据)
HdfSbufReadUint8(data,&2);
将data数据转换成驱动代码可以使用的数据,完成相应动作(参数二为地址) - 将驱动代码的数据发送到业务代码(翻译数据)
HdfSbufWriteInt32(reply,2)
将参数2写入参数1的reply中,reply可被带至业务程序
PS:==当需要翻译的数据为字符串时,用函数HdfSbufWriteString(1, 2);
和2=HdfSbufReadString(1)
==
- 翻译业务代码带来的数据(翻译数据)
-
驱动的BUILD.gn把驱动代码编译成可执行文件
-
系统驱动的BUILD.gn中的依赖库添加入==驱动代码的所在文件夹==
/device/st/drivers/BUILD.gn -
驱动配置(LED灯为例子)
- 驱动设备描述
device\st\bearpi_hm_micro\liteos_a\hdf_config\device_info\device_info.hcs
device_led :: device { // 设备节点 device0 :: deviceNode { // 驱动的DeviceNode节点 policy = 2; // policy字段是驱动服务发布的策略 priority = 10; // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序 preload = 1; // 驱动按需加载字段 permission = 0777; // 驱动创建设备节点权限 moduleName = "HDF_LED"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 serviceName = "hdf_led"; // 驱动对外发布服务的名称,必须唯一,驱动代码将以此名字启用驱动 deviceMatchAttr = "st_stm32mp157_led"; // 驱动私有数据名称 } }
- 驱动的私有配置信息
驱动的私有(只有该驱动使用)配置可以添加一个配置文件,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 中的property里面,通过Bind和Init传递给驱动,在device\st\bearpi_hm_micro\liteos_a\hdf_config\led\led_config.hcs中添加有配置描述。
root { LedDriverConfig { led_gpio_num = 13; match_attr = "st_stm32mp157_led"; //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致,以便于该私有配置可以被调用 } }
配置信息定义之后,需要将该配置文件添加到板级配置入口文件device\st\bearpi_hm_micro\liteos_a\hdf_config\hdf.hcs,示例如下:
#include "led/led_config.hcs"
- 驱动设备描述
GPIO口使用
GPIO口写入操作
==GPIO口一组16个,按顺序排列。==
例如当使用到GPIOA0时,IO口设置为16*0+0=0;当使用GPIOB1时,IO口设置为16*1+1=17
在驱动代码中添加以下代码:
//位置:bearpi-hm_micro_small\drivers\framework\support\platform\src\gpio_if.c
#include "gpio_if.h"
/*1*/
//IO口设置函数
GpioSetDir(uint16_t gpio, uint16_t dir); //dir的值查询GPIO口模式设置
/*2*/
//GPIO口操作函数
GpioWrite(uint16_t gpio, uint16_t val);
GpioRead(uint16_t gpio, uint16_t *val);
IO口设置和操作函数的调用
//IO口设置调用的函数
GpioCntlrSetDir(GpioGetCntlr(gpio), GpioToLocal(gpio), dir); //调用函数
GpioCntlrSetDir(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t dir); //函数头
cntlr->ops->setDir(cntlr, local, dir); //操作
//IO写入值
GpioCntlrWrite(GpioGetCntlr(gpio), GpioToLocal(gpio), val); //调用函数
GpioCntlrWrite(struct GpioCntlr *cntlr, uint16_t local, uint16_t val); //函数头
cntlr->ops->write(cntlr, local, val); //操作
GPIO口模式设置
GpioSetDir(uint16_t gpio, uint16_t dir);
typedef enum {
E53_GPIO_Out_PullUp = 0,
E53_GPIO_Out_PullDown,
E53_GPIO_Out_PullNone,
E53_GPIO_Out_DrainUp,
E53_GPIO_Out_DrainDown,
E53_GPIO_In_Floating,
E53_GPIO_In_Up,
E53_GPIO_In_Down,
}E53_GPIO_Mode;