内容参考于《抽象接口技术和组件开发规范及其思想》、《面向ametal框架和接口的C编程》

六.面向对象的嵌入式底层开发-LED

Ametal中LED的实现

资源图

1. demo_std_led.c

demo属于应用层。am_led_on和am_led_off属于通用接口,通用接口的第一个参数表示要操作的具体对象。一个系统可能有多颗LED,为每一颗LED分配一个ID号。通过ID形式隐藏了底层的对象和指针,对APP层更加友好,不被非业务的逻辑所影响。通过ID能查找到对象,但是,ID和对象并非是一对一的,一个对象可以拥有几个ID,可以理解为,主设备号(对象)下拥有这多个次设备号(ID)

#include "ametal.h"
#include "am_led.h"
#include "am_delay.h"void demo_std_led_entry (int led_id)
{while (1) {am_led_on(led_id);am_mdelay(500);am_led_off(led_id);am_mdelay(500);}
}

2. am_led.h(LED标准接口)

demo_std_led.c依赖于am_led.h

#ifndef __AM_LED_H
#define __AM_LED_H#ifdef __cplusplus
extern "C" {#endif#include "am_common.h"int am_led_set(int led_id, am_bool_t state);
int am_led_on(int led_id);
int am_led_off(int led_id);
int am_led_toggle(int led_id);#ifdef __cplusplus
}
#endif#endif /* __AM_LED_H */

3. am_led_dev.c(通用LED设备管理器)

通用接口的实现

  1. 先看看int am_led_on (int led_id)和int am_led_off (int led_id),发现其实整个的核心代码是int am_led_set (int led_id, am_bool_t state)int am_led_toggle (int led_id),这两个函数屏蔽了底层的差异,无论底层如何变化(GPIO或者HC595),其使用p_dev->p_funcs->pfn_led_setp_dev->p_funcs->pfn_led_toggle即可,这部分下面再进行细讲。
  2. am_led_dev_t * __led_dev_find_with_id(int id),其通过ID号查找到对象,将对象屏蔽在底层。
  3. 一般来说,在嵌入式底层,对象都是确定的,有几个LED,有几个UART,在硬件设计那一刻就确定了,代码通过宏进行声明。应用层启动前会对对象进程初始化,再进入应用。把LED对象,挂在 *static am_led_dev_t __gp_head 这个链表下面,这部分下面再进行细讲。
#include "ametal.h"
#include "am_led.h"
#include "am_led_dev.h"
#include "am_int.h"static am_led_dev_t *__gp_head;static am_led_dev_t * __led_dev_find_with_id (int id)
{am_led_dev_t *p_cur   = __gp_head;int key = am_int_cpu_lock();while (p_cur != NULL) {if ((id >= p_cur->p_info->start_id) &&(id <= p_cur->p_info->end_id)) {break;}p_cur = p_cur->p_next;}am_int_cpu_unlock(key);return p_cur;
}static int __led_dev_add (am_led_dev_t *p_dd)
{int key = am_int_cpu_lock();/* add the device to device list */p_dd->p_next = __gp_head;__gp_head    = p_dd;am_int_cpu_unlock(key);return AM_OK;
}static int __led_dev_del (am_led_dev_t *p_dd)
{int            ret     = -AM_ENODEV;am_led_dev_t **pp_head = &__gp_head;am_led_dev_t  *p_prev  = AM_CONTAINER_OF(pp_head, am_led_dev_t, p_next);am_led_dev_t  *p_cur   = __gp_head;int key = am_int_cpu_lock();while (p_cur != NULL) {if (p_cur == p_dd) {p_prev->p_next = p_dd->p_next;p_dd->p_next   = NULL;ret            = AM_OK;break;}p_prev = p_cur;p_cur  = p_cur->p_next;}am_int_cpu_unlock(key);return ret;
}int am_led_dev_lib_init (void)
{__gp_head = NULL;return AM_OK;
}int am_led_dev_add (am_led_dev_t             *p_dev,const am_led_servinfo_t  *p_info,const am_led_drv_funcs_t *p_funcs,void                     *p_cookie)
{if ((p_dev == NULL) || (p_funcs == NULL) || (p_info == NULL)) {return -AM_EINVAL;}if (__led_dev_find_with_id(p_info->start_id) != NULL) {return -AM_EPERM;}if (__led_dev_find_with_id(p_info->end_id) != NULL) {return -AM_EPERM;}p_dev->p_info   = p_info;p_dev->p_funcs  = p_funcs;p_dev->p_next   = NULL;p_dev->p_cookie = p_cookie;return __led_dev_add(p_dev);
}int am_led_dev_del (am_led_dev_t *p_dd)
{if (p_dd == NULL) {return -AM_EINVAL;}return __led_dev_del(p_dd);
}int am_led_set (int led_id, am_bool_t state)
{am_led_dev_t *p_dev = __led_dev_find_with_id(led_id);if (p_dev == NULL) {return -AM_ENODEV;}if (p_dev->p_funcs->pfn_led_set) {return p_dev->p_funcs->pfn_led_set(p_dev->p_cookie, led_id, state);}return -AM_ENOTSUP;
}int am_led_toggle (int led_id)
{am_led_dev_t *p_dev = __led_dev_find_with_id(led_id);if (p_dev == NULL) {return -AM_ENODEV;}if (p_dev->p_funcs->pfn_led_toggle) {return p_dev->p_funcs->pfn_led_toggle(p_dev->p_cookie, led_id);}return -AM_ENOTSUP;
}int am_led_on (int led_id)
{return am_led_set(led_id, AM_TRUE);
}int am_led_off (int led_id)
{return am_led_set(led_id, AM_FALSE);
}

4. am_led_dev.h(通用LED设备管理器)

  1. 让我们来看看led的抽象类。其实am_led_dev.h和am_led.h本该属于同一个H文件,这里分为2个文件,一个对内,一个对外,am_led.h是提供给应用层的,称为LED标准接口。am_led_dev是LED类的通用LED设备管理器,因为其不仅仅包含了抽象类,还包含了抽象类的链表。
  2. am_led_dev_t就是LED类,这里称为通用LED设备
  3. am_led_dev_t第一个组成是抽象方法const am_led_drv_funcs_t,这里称为驱动函数,注意是const类型,驱动函数从硬件确定便已经确定了。
  4. am_led_dev_t第二个组成是成员void *p_cookie,驱动函数参数,通常指向对象本身,其作用下面再细讲。
  5. am_led_dev_t第三个组成是成员const am_led_servinfo_t *p_info,其记录着这个对象的基本消息,LED编号(一个LED设备下有几路LED)。
  6. am_led_dev_t第四个组成是成员struct am_led_dev *p_next,把所有的LED设备链起来,那么就能够统一管理所有的设备,具有设备管理器的功能。
#ifndef __AM_LED_DEV_H
#define __AM_LED_DEV_H#include "ametal.h"
#include "am_led.h"#ifdef __cplusplus
extern "C" {#endif/*** \brief LED驱动函数*/
typedef struct am_led_drv_funcs {/* 设置LED的状态 */int (*pfn_led_set)   (void *p_cookie, int id, am_bool_t on);/* 翻转LED的状态 */int (*pfn_led_toggle)(void *p_cookie, int id);} am_led_drv_funcs_t;/*** \brief 通用LED服务信息*/
typedef struct am_led_servinfo {int         start_id;        /**< \brief 本设备提供的LED服务的起始编号 */int         end_id;          /**< \brief 本设备提供的LED服务的结束编号 */
} am_led_servinfo_t;/*** \brief 通用LED设备*/
typedef struct am_led_dev {const am_led_drv_funcs_t     *p_funcs;   /**< \brief 驱动函数            */void                         *p_cookie;  /**< \brief 驱动函数参数  */const am_led_servinfo_t      *p_info;    /**< \brief LED服务信息     */struct am_led_dev            *p_next;    /**< \brief 下一个LED设备 */
} am_led_dev_t;/** * \brief LED设备库管理初始化* \retval AM_OK : LED设备库管理初始化库初始化成功*/
int am_led_dev_lib_init (void);/*** \brief 添加一个LED设备** \param[in] p_dev    : LED设备实例* \param[in] p_info   :LED设备服务信息* \param[in] p_funcs  : LED设备的驱动函数* \param[in] p_cookie : 驱动函数的参数** \retval AM_OK      : 添加成功* \retval -AM_EINVAL :添加失败,参数存在错误* \retval -AM_EPERM  : 添加失败,该设备对应的LED编号已经存在*/
int am_led_dev_add (am_led_dev_t             *p_dev,const am_led_servinfo_t  *p_info,const am_led_drv_funcs_t *p_funcs,void                     *p_cookie);
/*** \brief 删除一个LED设备** \param[in] p_dev     : LED设备实例** \retval AM_OK      : 删除成功* \retval -AM_EINVAL :删除失败,参数存在错误* \retval -AM_ENODEV : 删除失败,无此参数*/
int am_led_dev_del (am_led_dev_t *p_dev);/* @} */#ifdef __cplusplus
}
#endif#endif /* __AM_LED_DEV_H */

5. __gp_head

  1. 通过上面的代码,我们知道,最终操作的是static am_led_dev_t *__gp_head这个链表下的各个设备(对象),那么从am_led_dev_add开始入手。
  2. am_led_dev_add第一个参数要求输入一个设备(对象),第二个参数要求输入这个设备的信息(LED的ID范围,次设备号),第三个参数要求输入这个设备的驱动函数am_led_drv_funcs_t,第三个参数要求输入驱动函数的参数void *p_cookie。

以上描述了接口的使用、设备的抽象接口内容、设备管理器,下面描述其底层实现


通过gpio控制led的接口实现

6. am_led_gpio.h

  1. am_led_gpio_dev是实现接口后的类,其还包含自己的成员变量
    const am_led_gpio_info_t *p_info,方法(构造函数和析构函数) am_led_gpio_initam_led_gpio_deinit
  2. am_led_gpio_info_t成员包含着,gpio特有的一些数据。
#ifndef __AM_LED_GPIO_H
#define __AM_LED_GPIO_H#include "ametal.h"
#include "am_led_dev.h"#ifdef __cplusplus
extern "C" {#endif/*** \brief LED信息(GPIO驱动)*/
typedef struct am_led_gpio_info {/** \brief LED基础服务信息 ,包含起始编号和结束编号     */am_led_servinfo_t  serv_info;/** \brief 使用的GPIO引脚,引脚数目应该为 (结束编号 - 起始编号 + 1)   */const int         *p_pins;/** \brief LED是否是低电平点亮  */am_bool_t          active_low;} am_led_gpio_info_t;/*** \brief LED设备(GPIO驱动)*/
typedef struct am_led_gpio_dev {am_led_dev_t               isa;const am_led_gpio_info_t  *p_info;
} am_led_gpio_dev_t;/*** \brief LED设备初始化(GPIO驱动)** \param[in] p_dev  : LED设备* \param[in] p_info : LED设备信息** \retval AM_OK      : 初始化成功* \retval -AM_EINVAL : 初始化失败*/
int am_led_gpio_init (am_led_gpio_dev_t         *p_dev,const am_led_gpio_info_t  *p_info);/*** \brief LED设备解初始化(GPIO驱动)** \param[in] p_dev  : LED设备** \retval AM_OK      : 解初始化成功* \retval -AM_EINVAL : 解初始化失败*/
int am_led_gpio_deinit (am_led_gpio_dev_t *p_dev);#ifdef __cplusplus
}
#endif#endif /* __AM_LED_GPIO_H */

7. am_led_gpio.c

#include "ametal.h"
#include "am_led_gpio.h"
#include "am_gpio.h"static int __led_gpio_set (void *p_cookie, int led_id, am_bool_t state)
{am_led_gpio_dev_t *p_dev = (am_led_gpio_dev_t *)p_cookie;led_id = led_id - p_dev->p_info->serv_info.start_id;am_gpio_set(p_dev->p_info->p_pins[led_id], state ^ p_dev->p_info->active_low);return AM_OK;
}static int __led_gpio_toggle (void *p_cookie, int led_id)
{am_led_gpio_dev_t *p_dev = (am_led_gpio_dev_t *)p_cookie;led_id = led_id - p_dev->p_info->serv_info.start_id;am_gpio_toggle(p_dev->p_info->p_pins[led_id]);return AM_OK;
}static const am_led_drv_funcs_t __g_led_gpio_drv_funcs = {__led_gpio_set,__led_gpio_toggle
};int am_led_gpio_init (am_led_gpio_dev_t         *p_dev,const am_led_gpio_info_t  *p_info)
{int i;int num;if ((p_dev == NULL) || (p_info == NULL)) {return -AM_EINVAL;}num = p_info->serv_info.end_id - p_info->serv_info.start_id + 1;if (num <= 0) {return -AM_EINVAL;}p_dev->p_info = p_info;for (i = 0; i < num; i++) {if (p_info->active_low) {am_gpio_pin_cfg(p_info->p_pins[i], AM_GPIO_OUTPUT_INIT_HIGH);} else {am_gpio_pin_cfg(p_info->p_pins[i], AM_GPIO_OUTPUT_INIT_LOW);}}return am_led_dev_add(&p_dev->isa,&p_info->serv_info,&__g_led_gpio_drv_funcs,p_dev);
}int am_led_gpio_deinit (am_led_gpio_dev_t *p_dev)
{int i;int num;if (p_dev == NULL) {return -AM_EINVAL;}num = p_dev->p_info->serv_info.end_id - p_dev->p_info->serv_info.start_id + 1;if (num <= 0) {return -AM_EINVAL;}for (i = 0; i < num; i++) {am_gpio_pin_cfg(p_dev->p_info->p_pins[i], AM_GPIO_INPUT | AM_GPIO_FLOAT);}return am_led_dev_del(&p_dev->isa);
}

8. am_hwconf_xx.c

应用启动前的硬件初始化,这里我们称为板级初始化,姑且这么认为(涉及到框架),这个路径就很有趣board\am217_core\project_template\user_config\am_hwconf_usrcfg。

/* GPIO形式 */
static am_led_gpio_dev_t  __g_miniport_led;
static const int __g_miniport_led_pins[] = {PIOB_7, PIOB_6, PIOB_15, PIOB_13, PIOC_10, PIOB_14, PIOB_12, PIOC_14
};static const am_led_gpio_info_t __g_miniport_led_info = {{0,                            /* 起始编号0 */7                             /* 结束编号7,共计8个LED */},__g_miniport_led_pins,AM_TRUE
};int am_miniport_led_inst_init (void)
{return am_led_gpio_init(&__g_miniport_led, &__g_miniport_led_info);
}/* HC595形式 */
static am_led_hc595_dev_t  __g_miniport_led_595;
static uint8_t __g_miniport_led_595_buf[1];
static const am_led_hc595_info_t __g_miniport_led_595_info = {{8,                            /* 起始编号8 */9                             /* 结束编号9,共计2个LED */},1,__g_miniport_led_595_buf,AM_TRUE
};int am_miniport_led_595_inst_init (void)
{return am_led_hc595_init(&__g_miniport_led_595,&__g_miniport_led_595_info,am_miniport_595_inst_init());
}

通过hc595控制led的接口实现

9. am_led_hc595.h

#ifndef __AM_LED_HC595_H
#define __AM_LED_HC595_H#include "ametal.h"
#include "am_led_dev.h"
#include "am_hc595.h"#ifdef __cplusplus
extern "C" {#endif/*** \brief LED信息(HC595驱动)*/
typedef struct am_led_hc595_info {/** \brief LED基础服务信息 ,包含起始编号和结束编号     */am_led_servinfo_t  serv_info;/** \brief HC595的级连个数  */int                hc595_num;/** \brief 数据缓存,大小与HC595的级连个数相同 */uint8_t           *p_buf;/** \brief LED是否是低电平点亮  */am_bool_t          active_low;} am_led_hc595_info_t;/*** \brief LED设备(GPIO驱动)*/
typedef struct am_led_hc595_dev {am_led_dev_t                isa;am_hc595_handle_t           handle;const am_led_hc595_info_t  *p_info;
} am_led_hc595_dev_t;/*** \brief LED设备初始化(HC595驱动)** \param[in] p_dev  : LED设备* \param[in] p_info : LED设备信息* \param[in] handle : HC595句柄** \retval AM_OK      : 初始化成功* \retval -AM_EINVAL : 初始化失败*/
int am_led_hc595_init (am_led_hc595_dev_t         *p_dev,const am_led_hc595_info_t  *p_info,am_hc595_handle_t           handle);/*** \brief LED设备解初始化(HC595驱动)** \param[in] p_dev  : LED设备** \retval AM_OK      : 解初始化成功* \retval -AM_EINVAL : 解初始化失败*/
int am_led_hc595_deinit (am_led_hc595_dev_t *p_dev);#ifdef __cplusplus
}
#endif#endif /* __AM_LED_HC595_H */

10. am_led_hc595.c

#include "ametal.h"
#include "am_hc595.h"
#include "string.h"
#include "am_led_hc595.h"static int __led_hc595_set (void *p_cookie, int led_id, am_bool_t state)
{am_led_hc595_dev_t *p_dev = (am_led_hc595_dev_t *)p_cookie;led_id = led_id - p_dev->p_info->serv_info.start_id;if (state ^ p_dev->p_info->active_low) {p_dev->p_info->p_buf[led_id >> 3] |= (1 << (led_id & 0x07));} else {p_dev->p_info->p_buf[led_id >> 3] &= ~(1 << (led_id & 0x07));}am_hc595_send(p_dev->handle,p_dev->p_info->p_buf,p_dev->p_info->hc595_num);return AM_OK;
}static int __led_hc595_toggle (void *p_cookie, int led_id)
{am_led_hc595_dev_t *p_dev = (am_led_hc595_dev_t *)p_cookie;led_id = led_id - p_dev->p_info->serv_info.start_id;p_dev->p_info->p_buf[led_id >> 3] ^= (1 << (led_id & 0x07));am_hc595_send(p_dev->handle,p_dev->p_info->p_buf,p_dev->p_info->hc595_num);return AM_OK;
}static const am_led_drv_funcs_t __g_led_hc595_drv_funcs = {__led_hc595_set,__led_hc595_toggle
};int am_led_hc595_init (am_led_hc595_dev_t         *p_dev,const am_led_hc595_info_t  *p_info,am_hc595_handle_t           handle)
{if ((p_dev == NULL) || (p_info == NULL)) {return -AM_EINVAL;}p_dev->p_info = p_info;if (p_info->active_low) {memset(p_info->p_buf, 0xFF, p_info->hc595_num);} else {memset(p_info->p_buf, 0x00, p_info->hc595_num);}am_hc595_send(handle, p_info->p_buf, p_info->hc595_num);return am_led_dev_add(&p_dev->isa,&p_info->serv_info,&__g_led_hc595_drv_funcs,p_dev);
}int am_led_hc595_deinit (am_led_hc595_dev_t *p_dev)
{const am_led_hc595_info_t  *p_info;if (p_dev == NULL) {return -AM_EINVAL;}p_info = p_dev->p_info;if (p_info->active_low) {memset(p_info->p_buf, 0xFF, p_info->hc595_num);} else {memset(p_info->p_buf, 0x00, p_info->hc595_num);}am_hc595_send(p_dev->handle, p_info->p_buf, p_info->hc595_num);return am_led_dev_del(&p_dev->isa);
}

六.面向对象的嵌入式底层开发-LED(C面向对象开发)相关推荐

  1. 南京邮电大学嵌入式系统开发实验5:嵌入式Linux下LED报警灯驱动设计及编程

    实验5  嵌入式Linux下LED报警灯驱动设计及编程 一.实验目的 理解驱动本质,掌握嵌入式Linux系统下驱动开发相关知识,包括端口寄存器访问.接口函数编写.和文件系统挂接.注册及相关应用编程等知 ...

  2. 嵌入式入门学习笔记4:[转]什么是嵌入式底层驱动开发和嵌入式底层软件开发...

    我们知道嵌入式操作系统(Embedded System)是指以应用为中心.以计算机技术为基础,软件硬件可裁剪.适应应用系统对功能.可靠性.成本.体积.功耗严格要求的专用计算机系统.嵌入式开发分两种,一 ...

  3. 【嵌入式Linux(基础篇)】嵌入式Linux底层系统开发流程和应用开发流程

    1.嵌入式Linux系统体系架构 一个完整的嵌入式Linux系统体系架构如图,大概可以分为三步: 硬件PCB板设计 底层系统开发 应用开发 2.硬件PCB设计流程 ① 获取所用芯片芯片手册: ② 建立 ...

  4. 我与《深入浅出嵌入式底层软件开发》

    ++++++++++++++++++++++++++++++++++++++++++ 本文系本站原创,欢迎转载! 转载请注明出处: http://blog.csdn.net/mr_raptor/art ...

  5. 嵌入式底层开发为什么选择C语言

    嵌入式底层开发为什么选择C语言 嵌入式系统的编写语言主要是C语言,部分底层代码会用到汇编语言. 嵌入式(C/C++):在软件(嵌入式应用开发)和硬件(嵌入式硬件开发)中嵌入操作系统(嵌入式底层开发). ...

  6. android嵌入式底层开发教程

    android嵌入式底层开发教程 课程针对人群 熟悉.NET,J2EE应用开发,希望往嵌入式底层学习的工程师 熟悉Android应用和框架开发,希望从上到下走通Android系统的工程师 不希望局限在 ...

  7. 嵌入式学习:裸机开发_L4_官方SDK开发LED实验

    裸机开发_L4_官方SDK开发LED实验 1. 硬件层电路 1.1 正点原子 i.MX6ULL ALPHA V2.2 开发板 1.2 飞凌i.MX6UL-C开发板 2. 软件编写 2.1. 正点原子 ...

  8. OO开发思想:面向对象的开发方法(Object oriented,OO)

    面向对象的开发方法(Object oriented,OO)认为是好文章吧,拿来分享一下(转载) 面向对象的开发方法(Object oriented,OO) 从事软件开发的工程 师们常常有这样 的体会: ...

  9. 前端笔记(Html+CSS+JS+DOM+网页特效+jQuery+HTML5+CSS3+canvas 标签+web开发重难点+面向对象+AJAX)

    第1章Html Html:超级文本标记语言(HyperText Markup Language),在浏览器上运行的一种标记语言. 就是给文本加上含有语义的标签. 接下来应该学习更多具体语义标签: 一. ...

最新文章

  1. Spring 运用 pointcut 和 advisor 对特定的方法进行切面编程
  2. python 单例模式的实现方法_python中单例模式的四种实现方式
  3. Python--DBUtil
  4. PS教程第十五课:图层是最基本的要求
  5. Feature selection using SelectFromModel
  6. vue 如何清除浏览器的内存_浏览器垃圾回收机制与 Vue 项目内存泄漏场景分析
  7. SpringMVC学习指南【笔记4】数据绑定、表单标签库、转换器、格式化、验证器
  8. rest_framework 权限功能
  9. python爬虫——爬取汽车之家新闻
  10. python的小程序分析_Python小程序,红楼梦关键词分析
  11. 如何获取伪装ip下的真实ip地址
  12. 静态html使用js发送邮件,科技常识:html实现邮箱发送邮件_js发送邮件至指定邮箱功能...
  13. 安装lux:推荐一款网页视频下载工具。并简单使用。(win)
  14. Word 无法打开该文件,因为文件格式与文件扩展名不匹配。
  15. 薛定谔 | 小分子叠合
  16. 网络流最大流----EK算法
  17. python过京东app图形验证勾股定理_泰尔实验室检测结果公布,京东金融app安全性得以验证...
  18. iOS音频系列(一)--音频基础
  19. 程序人生——CSAPP大作业
  20. 3D【8】鸟类重建:Learning Category-Specific Mesh Reconstruction from Image Collections阅读笔记

热门文章

  1. 服务器被ddos攻击了怎么处理
  2. Activity java工作流引擎 maven依赖
  3. 基于 GraphQL 平台化 BFF 构建及微服务治理
  4. 在Debian11 基础上安装 Proxmox VE 7 虚拟化平台
  5. 【设计模式】装饰器模式
  6. 微信小程序腾讯地图定位、调用关键词提示接口,搜索关键词的附近关键词地址
  7. Android旅游景点美食点评系统app
  8. 四种求最大公约数算法
  9. 图像处理基础操作二(边缘检测、轮廓检测、光流估计)
  10. jquery项目移动端适配