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

内容参考于《抽象接口技术和组件开发规范及其思想》、《面向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);
}
  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值