天启集团
专注智能硬件研发创新,提供智慧场景解决方案
提供全面的服务器产品及高效的解决方案
思特森私有云电子消费类产品及服务

FireBLE

FireBLE 是一个面向于打造智能生活的开源平台,以BLE(Bluetooth Low Energy)技术为核心,拥有超低的功耗、不俗的处理能力和广泛的应用场合,专注于更智能、高效率的工作模式,让生活在科技中更安全、方便、快捷。也许您一个不经意的想法与FireBLE擦出的火花,会在这片原野上燎出火焰,甚至燃烧整个世界。

源码移植

更新时间:2017-08-08 阅读:6624

前言

昆天科源代码基于昆天科公司生产的开发套件。

昆天科的开发套件分为 QBlue DK 和 MiniDK ,QBlue DK 又分为 MotherBoard 和 Evaluation Board 两部分,在QBlue1.3.5中均有介绍。

QBlue DK外设较多,并且使用了NXP LPC1768作为主控;Mini DK则外设较少。

昆天科开发套件可以直接运行昆天科提供的所有源代码,但是,也存在如下几个缺点:

  1. 开发套件中使用的是QN9020,由于QN9021相对QN9020拥有跟小的封装,所以IO分布也不一样,恰好开发套件里面使用的IO与QN9021是冲突的。如果考虑到各种因素需要使用QN9021开发产品,在开发套件上开发的程序并不能直接适用于产品。
  2. 开发套件价格方面比较高,官方零售价为69美元。
  3. 昆天科只提供了SDK中英文开发手册供开发者使用,并不做开源化管理,学习环境比较单一。

FireBLE与DK的区别:

  1. QN9021单芯片解决方案,无须外加MCU。
  2. 外设丰富,Joysticks摇杆、OLED屏接口、板载MPU6050 6轴G-Sensor。
  3. 开源化管理,提供中文学习教程、论坛、社区以及开源项目开发。

准备工作

获取源代码

使用git工具可以从远程服务器上克隆FireBLE源代码,该源代码基于官方SDK做了移植,移植方法在本篇后面会详细介绍。小编非常希望用户能够用上并且喜欢上用git工具来管理和维护代码,更希望用户能参与到开源项目的开发与维护中去,在其中学习到更多的东西,开阔自己的视野,而不是自己一个人孤军奋战。下面我来介绍一下什么是git,虽然很是粗浅,但是小编已经尽全力,请不要介意。

官方下载界面获取源代码

源代码基于QBlue1.3.5,其中主要修改了button、led的引脚,增加了OLED屏的驱动和MPU6050的驱动。

在bitbucket上用git工具克隆代码

使用以下命令可以直接将代码克隆到本地。(克隆前请先在命令行中切换到需要放置代码的目录,再克隆,如果不切换目录,直接在~目录下克隆,那么克隆下来的文件夹将会存放在C:/Users/xxx下).

 git clone https://TeeFirefly@bitbucket.org/T-Firefly/FireBLE.git

先介绍一下什么是git,为什么要使用它。

git代码管理工具介绍

Git是一款免费、开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。

从一般开发者的角度来看,git有以下功能:

  1. 从服务器上克隆数据库(包括代码和版本信息)到单机上。
  2. 在自己的机器上创建分支,修改代码。
  3. 在单机上自己创建的分支上提交代码。
  4. 在单机上合并分支。
  5. 新建一个分支,把服务器上最新版的代码fetch下来,然后跟自己的主分支合并。
  6. 生成补丁(patch),把补丁发送给主开发者。
  7. 看主开发者的反馈,如果主开发者发现两个一般开发者之间有冲突(他们之间可以合作解决的冲突),就会要求他们先解决冲 突,然后再由其中一个人提交。如果主开发者可以自己解决,或者没有冲突,就通过。
  8. 一般开发者之间解决冲突的方法,开发者之间可以使用pull 命令解决冲突,解决完冲突之后再向主开发者提交补丁。

从主开发者的角度(假设主开发者不用开发代码)看,git有以下功能:

  1. 查看邮件或者通过其它方式查看一般开发者的提交状态。
  2. 打上补丁,解决冲突(可以自己解决,也可以要求开发者之间解决以后再重新提交,如果是开源项目,还要决定哪些补丁有用,哪些不用)。
  3. 向公共服务器提交结果,然后通知所有开发人员

简单的说,方便公布代码给其他开发者下载和开发、方便在本机上对代码的修改做详细记录和备份(理论上git不被破坏,可以回退任意时刻的提交记录的版本)、方便分享代码给其他开发者、方便对代码任意修改而不怕破坏代码(只要是在分支上折腾就行了)、方便在源代码基础上用多种方法实现多种功能,而不至于混乱。

总而言之,git对开发者来说是一个十分方便,能提升不少效率的工具,优点多多。

文资料太少太少,学习周期长。但是一旦学会,你会喜欢并且依赖上它的。

关于更多git的说明和学习,推荐此网站,学习git so easy:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000

git管理代码的习惯建议

  1. 用git做代码维护的时候要养成完成一个小功能就提交一次的习惯,尽量不要做了一大堆的事情然后再提交,除非你能保证每一次都是完美成功的,否则一旦出现差错,你想回到某个时间点的时候才发现自己没有那个时间点的提交,完全reset又会删掉之前完成的一些小功能,然后纠结七天七夜而亡。
  2. reset虽然很危险,但是也很好用,所以这部分有必要仔细阅读,灵活运用。
  3. commit的名称尽量使用英文(没办法,linux平台的外国软件,既跨平台又跨国界,编码问题实在蛋疼),而且尽量清晰、统一格式的描述这一次提交所做的内容。
  4. 命令行与图形界面同时使用,交替使用,既有Linux的高效,也有windows的友好,个人觉得这样上手快……
  5. 完成一个功能之前,先开一个分支,不要懒惰直接在master上做开发,貌似的捷径必然充满陷阱,你不信?纠结的反正不是我。
  6. 不抛弃,不放弃,我现在对git80%以上的功能还不熟悉,但是,现在没有git我已经很不习惯了,我觉得只要把上文中廖雪峰大神的网站多看几遍,基本掌握了,就差不多了。

源代码结构分析

git clone成功后,在当前目录下将会看到一个FireBLE命名的文件夹,这就是刚才克隆下来的工程。

打开工程可以看到,里面有许多以prj_xxx命名的文件夹,每一个工程都是独立的,而且如果需要新建工程,建议只在此目录下创建,因为在keil的配置中,已经配置好了相对路径,如果任意改变路径,需要对keil工程进行重新配置,否则会出错无法编译。

另外还有一个src命名的文件夹,主要包含了各个工程中通用的一些文件,例如app、profile、driver等等。

group 分类说明 group 分类说明
startup 汇编启动配置代码 profile profile是把蓝牙中标准的服务实现规范成一个特定服务实现,app与profile通过msg通信协同工作,实现特定功能。基本不会改动,在有需求,并且对此非常熟悉的时候,可以自己定制。
main 主函数,一般无须修改。 drivers 系统底层驱动。
usr 用户定义文件,与工程要实现的功能息息相关。一般只需改动此文件夹下的代码。 lib 系统封装库,封装昆天科保密的代码。
app BLE的app端,调用不同的服务组成和实现一个特定的功能,可直接使用昆天科实现的app,也可自己定制。 qnevb 系统外设驱动文件。
retarget 实现了重定义getchar()等等,实现串口标准化输出。
  • 对于主机设备来说,我们更多的是在app目录下做开发;对于从机设备来说,我们更多的在usr目录下做开发。

源代码移植

源代码的移植指的是从昆天科原厂的代码修改为FireBLE开发板能够运行的代码,移植的主要内容的是底层的外设驱动。昆天科源码中的最底层外设有三个:
  • LED
  • Button
  • Buzz

以prj_proxr为例对移植做说明。

移植说明

以按键驱动为例,昆天科源码中对按键有如下引脚定义(FireBLE/BLE/src/qnevb/button.h):

  #if !defined(QN_9021_MINIDK)
      #define LED1_PIN    (GPIO_P05)
      #define LED2_PIN    (GPIO_P04)
      #define LED3_PIN    (GPIO_P03)
      #define LED4_PIN    (GPIO_P02)
      #define LED5_PIN    (GPIO_P01)
  #else
      #define LED1_PIN    (GPIO_P03)
      #define LED2_PIN    (GPIO_P13) 
      #define LED3_PIN    (GPIO_P02)  // no pin in QN9021
      #define LED4_PIN    (GPIO_P02)  // no pin in QN9021
      #define LED5_PIN    (GPIO_P02)  // no pin in QN9021
  #endif

只需打开QN_9021_MINIDK宏定义即可更改引脚配置,这是程序配置中一般使用方法。找到QN_9021_MINIDK的宏定义: FireBLE/BLE/src/app/app_config.h

  /// Evaluation Board Indication
  #if (defined(CFG_9021_MINIDK))
    #define QN_9021_MINIDK
  #endif


看来打开QN_9021_MINIDK宏定义必须要先定义CFG_9021_MINIDK.查找CFG_9021_MINIDK: FireBLE/BLE/pro_xxx/src/usr_config.h

  /// Evaluation board indication
  // The GPIOs used for QN9021 miniDK's LED and button are different from QN9020 miniDK.
  // If the QN9021 miniDK is used, the following macro shall be defined.
  #define CFG_9021_MINIDK

以上是对QN_9021_MINIDK的适配,我们可以依葫芦画瓢,在 FireBLE/BLE/src/app/app_config.h 下面做如下定义

  /// FireBLE Board Indication
  #if	(defined(CFG_FireBLE))
    #define	FireBLE_platform
  #endif

在 FireBLE/BLE/pro_xxx/src/usr_config.h 中做如下定义

  /// Evaluation board indication
  // The GPIOs used for FireBLE's LED and button are different from FireBLE.
  // If the FireBLE is used, the following macro shall be defined.
  #define CFG_FireBLE

这样,我们定义了一个FireBLE_platform的开发平台,在昨晚本章所有操作后,以后我们只需打开 CFG_FireBLE 的宏定义就可以在工程中使用源码中的底层驱动了。

现在我们来看,为什么在定义QN_9021_MINIDK之前要定义CFG_9021_MINIDK,这不是多次一举吗?不然,其实这正是为了更好的维护代码,分隔代码相关性。

app_config.h是属于公共配置文件,存放在多个工程公用的src文件夹中的app文件夹内,每一个工程使用的都是同一份代码。从理论上来说,为了代码的移植性考虑,我们希望只要修改工程内的usr_config.h就能实现对开发平台不同的切换并且不影响到其他开发平台的使用。也就是说,在不这样设计的情况下,如果我在A工程中使用的是FireBLE,B工程中是用的是QBlue 9021 MINI DK,如果我因为要在B中使用QBlue 9021 MINI DK,我需要在app_config中关闭了FireBLE_platform的宏定义,开启QBlue 9021 MINI DK,那么,A工程就无法正常运行了,反之,则两个工程十分独立,完全无影响。

添加配置定义

在FireBLE/BLE/pro_xxx/src/usr_config.h中添加如下定义

/// Evaluation board indication
// The GPIOs used for FireBLE's LED and button are different from FireBLE.
// If the FireBLE is used, the following macro shall be defined.
#define CFG_FireBLE
 
///defined it when used SWD Debug,and LED2/LED3 will do not work.
#define  CFG_SWD
 
///FireBLE Joystick button
#define	CFG_JOYSTICKS

其中打开宏CFG_FireBLE表示进入FireBLE平台,代码可以适用于FireBLE开发板。打开CFG_SWD表示需要使用SWD进行硬件仿真,此时LED2和LED3不能使用,指示灯微亮是因为作为仿真接口使用时该引脚被配置为下拉状态。打开CFG_JOYSTICKS宏表示需要使用到五向按键。

在FireBLE/BLE/src/app/app_config.h文件中添加如下定义

/// FireBLE Board Indication
#if	(defined(CFG_FireBLE))
		#define	FireBLE_platform
#if	(defined(CFG_SWD))		
		#define	FB_SWD									1
#else
		#define	FB_SWD									0
#endif
#if	(defined(CFG_JOYSTICKS))
		#define	FB_JOYSTICKS						1
#else
		#define	FB_JOYSTICKS						0
#endif
#endif

在FireBLE/BLE/src/app/app_env.h中加入头文件

#if		(FB_JOYSTICKS)
  #include "joysticks.h"
#endif

LED适配

LED适配非常简单,只需更改LED引脚定义如下(FireBLE/BLE/src/qnevb/led.h):

  #if !defined(QN_9021_MINIDK)
    #if !defined(FireBLE_platform)
      #define LED1_PIN    (GPIO_P05)
      #define LED2_PIN    (GPIO_P04)
      #define LED3_PIN    (GPIO_P03)
      #define LED4_PIN    (GPIO_P02)
      #define LED5_PIN    (GPIO_P01)
    #else
      #define	LED1_PIN	 (GPIO_P27)
      #if !(FB_SWD)
        #define LED2_PIN   (GPIO_P06)
        #define LED3_PIN   (GPIO_P07)
      #else
        #define LED2_PIN    (GPIO_P02)  // no pin in QN9021
        #define LED3_PIN    (GPIO_P02)  // no pin in QN9021
      #endif
     #define LED4_PIN    (GPIO_P02)  // no pin in QN9021
     #define LED5_PIN    (GPIO_P02)  // no pin in QN9021
    #endif
  #else
    #define LED1_PIN    (GPIO_P03)
    #define LED2_PIN    (GPIO_P13)
    #define LED3_PIN    (GPIO_P02)  // no pin in QN9021
    #define LED4_PIN    (GPIO_P02)  // no pin in QN9021
    #define LED5_PIN    (GPIO_P02)  // no pin in QN9021
  #endif

关于宏定义FB_SWD说明:默认关闭SWD仿真,需要使用SWD仿真,需要在usr_config.h中打开CFG_SWD的宏定义

///defined it when used SWD Debug,and LED2/LED3 will do not work.
#define  CFG_SWD

此时GPIO的定义也要根据实际的应用场合去修改,由于SystemIOCfg属于usr用户组,是用户自定义文件,请用户根据自己需求定义,示例如下

static void SystemIOCfg(void)
{
    // pin mux
    syscon_SetPMCR0(QN_SYSCON, P00_UART0_TXD_PIN_CTRL
                             | P01_GPIO_1_PIN_CTRL
                             | P02_GPIO_2_PIN_CTRL
                             | P03_GPIO_3_PIN_CTRL
                             | P04_GPIO_4_PIN_CTRL
                             | P05_GPIO_5_PIN_CTRL
#if	!(FB_SWD)
			     | P06_GPIO_6_PIN_CTRL
			     | P07_GPIO_7_PIN_CTRL
#else
                             | P06_SW_DAT_PIN_CTRL
                             | P07_SW_CLK_PIN_CTRL
#endif

Joysticks适配

Joysticks适配相比刚才要复杂许多。首先修改Button引脚定义(FireBLE/BLE/src/qnevb/button.h):

  #if !defined(QN_9021_MINIDK)
    #if !defined(FireBLE_platform)
      #define BUTTON1_PIN    (GPIO_P14)
      #define BUTTON2_PIN    (GPIO_P15)
    #else
      #define	BUTTON1_PIN	 (GPIO_P12)
      #define	BUTTON2_PIN	 (GPIO_P02)  // no pin in QN9021
    #endif
  #else
    #define BUTTON1_PIN    (GPIO_P12)
    #define BUTTON2_PIN    (GPIO_P10)
  #endif

如果是基于proxr例程修改此程序,程序此时已经可以编译成功,并且能够正常执行,无论Joysticks往哪一个方向按下,都开启和关闭BLE广播,并且LED1在广播开启时会不停闪烁。但是由于是Joysticks,我们可不能满足于此,接下来还需要加入Joysticks方向的判断。Joysticks通过adc检测GPIO_P30的电平变化来确定Joysticks的方向(有兴趣的可以去看原理图),我们可以加入一个宏定义,在不需要使用Joysticks的情况下,可以不做这方面的识别。

在FireBLE/BLE/prj_xxx/src/usr_config.h中加入

  ///define it when used Joysticks
  #define CFG_JOYSTICKS

当打开此项宏定义时表示需要使用到Joysticks。

和LED一样,由于使用到ADC作为反向识别,所以需要修改引脚配置,示例,在FireBLE/BLE/prj_xxx/src/system.c中加入GPIO的引脚配置,配置GPIO_P30位ADC的输入通道。

  #if	!(FB_JOYSTICKS)
        | P30_GPIO_24_PIN_CTRL
  #else
        | P30_AIN0_PIN_CTRL
  #endif

添加qnevb文件夹中的joysticks.c文件,该文件实现了对Joysticks的方向识别。在task_app.c和task_app.h中加入如下信息。 app_task.c

#if (FB_JOYSTICKS)
		{APP_KEY_PROCESS_TIMER,						(ke_msg_func_t) app_key_process_timer_handler},
		{APP_KEY_SCAN_TIMER,						(ke_msg_func_t) app_key_scan_timer_handler},
#endif

app_task.h

#if	(FB_JOYSTICKS)
		APP_KEY_PROCESS_TIMER,
		APP_KEY_SCAN_TIMER,
#endif

先编译一遍,看看会有些什么错误出现。

FireBLE joysticks error1.png

没有包含adc驱动,实际上还有一个analog.c的文件也是要包含的(在工程目录的上一级目录的src中的driver文件夹中)。

再次运行,无报错了,貌似是可以用了,但是,实际是不行的,还需如下几步:

  • 五向按键用到了ADC采样,所以还需要注册按键中ADC采样完成中断事件。
#if    (FB_JOYSTICKS)
  if(KE_EVENT_OK != ke_evt_callback_set(EVENT_ADC_KEY_SAMPLE_CMP_ID,
                                            app_event_adc_key_sample_cmp_handler))
  {
       ASSERT_ERR(0);
  }
#endif

  • 在按键事件处理函数app_event_button1_press_handler中添加一个定时器事件跳转到Ioysticks识别处理函数,并且屏蔽掉之前的按键处理函数。
  void app_event_button1_press_handler(void) 
  {
  #if ((QN_DEEP_SLEEP_EN) && (!QN_32K_RCO))
    if (sleep_env.deep_sleep)
    {
        sleep_env.deep_sleep = false;
        // start 32k xtal wakeup timer
        wakeup_32k_xtal_start_timer();
    }
  #endif
    // delay 20ms to debounce
  #if (FB_JOYSTICKS)
    ke_timer_set(APP_KEY_SCAN_TIMER,TASK_APP,2);
  #else
    ke_timer_set(APP_SYS_BUTTON_1_TIMER, TASK_APP, 2);
  #endif
    ke_evt_clear(1UL << EVENT_BUTTON1_PRESS_ID);
  }

  • 按键唤醒后需要初始化一些参数,在usr_button1_cb函数中加入如下代码:
  void usr_button1_cb(void)
  {
    // If BLE is in the sleep mode, wakeup it.
    if(ble_ext_wakeup_allow())
    {
  #if ((QN_DEEP_SLEEP_EN) && (!QN_32K_RCO))
        if (sleep_env.deep_sleep)
        {
            wakeup_32k_xtal_switch_clk();
        }
  #endif
        sw_wakeup_ble_hw();
  // #if (QN_DEEP_SLEEP_EN)
  //         // prevent deep sleep
  //         if(sleep_get_pm() == PM_DEEP_SLEEP)
  //         {
  //             sleep_set_pm(PM_SLEEP);
  //         }
  // #endif
      }
  #if (FB_JOYSTICKS)
      usr_button_env.button_st = button_press;
  #endif
    // key debounce:
    // We can set a soft timer to debounce.
    // After wakeup BLE, the timer is not calibrated immediately and it is not precise.
    // So We set a event, in the event handle, set the soft timer.
    ke_evt_set(1UL << EVENT_BUTTON1_PRESS_ID);
  }

这样,Joysticks的驱动就算是移植完成了,可以在usr_key_process_timer_handler中实现按键触发后需要执行的操作。

OLED适配

FireBLE可以使用官网上配套的OLED,OLED有两种通信方式,IIC协议和SPI协议,可通过更改少量OLED背部的器件位置来更改其通信方式。其中开发板背面OLED接口的右边有日期字样的,如图,既可以使用IIC版的OLED,也可以使用SPI版的OLED;如果没有日期字样,则表示版本较老,仅支持IIC点屏。

  • 首先将oled.c oled.h以及oledfont.h拷贝到BLE/src/qnevb目录下。
  • 在usr_config.h中添加开启OLED的宏定义,可以选择屏幕采用IIC协议通信或者是SPI协议通信,由于IIC协议是软件模拟的通信协议,刷屏速度略慢于SPI协议。
///choose SPI OLED or IIC OLED  (CFG_IIC_OLED , CFG_SPI_OLED)
#define	CFG_IIC_OLED

  • 在app_config.h中添加OLED使用的宏定义。
/// FireBLE Board Indication
#if	(defined(CFG_FireBLE))
		#define	FireBLE_platform
#if	(defined(CFG_SWD))		
		#define	FB_SWD									1
#else
		#define	FB_SWD									0
#endif
#if	(defined(CFG_JOYSTICKS))
		#define	FB_JOYSTICKS						1
#else
		#define	FB_JOYSTICKS						0
#endif
#if	(defined(CFG_IIC_OLED) || defined(CFG_SPI_OLED))
		#define	FB_OLED									1
 
#if	defined(CFG_IIC_OLED)
		#define	FB_IIC_OLED							1
#else
		#define	FB_IIC_OLED							0
#endif
 
#if defined(CFG_SPI_OLED)
		#define	FB_SPI_OLED							1
#else
		#define	FB_SPI_OLED							0
#endif
 
#else
		#define	FB_OLED									0
#endif
#endif

  • 在app_env.h中加入头文件
#if	(FB_OLED)
#include	"oled.h"
#endif

  • 在app_task.c中添加定时器事件任务
#if	(FB_OLED)
		{APP_OLED_DISPLAY_TIMER,						(ke_msg_func_t) app_oled_display_timer_handler},
		{APP_OLED_STATE_DISPlAY_TIMER,			(ke_msg_func_t) app_oled_state_display_timer_handler},
		{APP_OLED_CLEAR_KEY_DISPLAY_TIMER,	(ke_msg_func_t) app_oled_clear_key_display_timer_handler},
#endif

  • 在app_task.h中声明事件
#if		(FB_OLED)
		APP_OLED_DISPLAY_TIMER,
		APP_OLED_STATE_DISPlAY_TIMER,
		APP_OLED_CLEAR_KEY_DISPLAY_TIMER,
#endif

  • 在usr_design.h中声明如下函数
#if FB_OLED
extern int app_oled_clear_key_display_timer_handler(ke_msg_id_t const msgid, void const *param,
                               ke_task_id_t const dest_id, ke_task_id_t const src_id);
extern int app_oled_state_display_timer_handler(ke_msg_id_t const msgid,
                               void *param,
                               ke_task_id_t const dest_id,
                               ke_task_id_t const src_id);
#endif

  • 在usr_design.c中实现该函数
#if FB_OLED
 
/**
 ****************************************************************************************
 * @brief Handles oled display status.
 *
 * @param[in] msgid     APP_OLED_STATE_DISPlAY_TIMER
 * @param[in] param     Null
 * @param[in] dest_id   TASK_APP
 * @param[in] src_id    TASK_NONE
 *
 * @return If the message was consumed or not.
 ****************************************************************************************
 */
int app_oled_state_display_timer_handler(ke_msg_id_t const msgid,
                               void *param,
                               ke_task_id_t const dest_id,
                               ke_task_id_t const src_id)
{
    // Stop proxr alert
    switch(ke_state_get(TASK_APP))
		{
			case	APP_ADV	:
			{
				OLED_ShowString(0,2, (uint8_t *)"                ");	
				OLED_ShowString(0,2,(uint8_t *)"  Advertising   ");
			}break;
			case	APP_IDLE	:
			{
					if(app_proxr_env->enabled)
					{
						OLED_ShowString(0,2, (uint8_t *)"                ");	
						OLED_ShowString(0,2,(uint8_t *)"   Connected    ");
					}
					else
					{
						OLED_ShowString(0,2, (uint8_t *)"                ");	
						OLED_ShowString(0,2,(uint8_t *)"   unConected   ");
					}						
			}break;
			case	APP_INIT	:
			{
					OLED_ShowString(0,2, (uint8_t *)"                 ");	
					OLED_ShowString(0,2,(uint8_t *)"    Init!        ");													
			}break;
			default	:
			{
					OLED_ShowString(0,2, (uint8_t *)"                 ");	
					OLED_ShowString(0,2,(uint8_t *)"    Init!        ");													
			}break;				
		}
    return (KE_MSG_CONSUMED);
}
 
int app_oled_clear_key_display_timer_handler(ke_msg_id_t const msgid, void const *param,
                               ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
		OLED_ShowString(0,4,"                ");		
		return(KE_MSG_CONSUMED);
}
#endif

  • 在system.c中初始化OLED
#if	(FB_OLED)
		OLED_Init();			//Init OLED
 
		OLED_Clear();			//Clear OLED,Black will be covered with the entire screen
 
		OLED_ShowString(0,0,"  Firefly Team  ");
		OLED_ShowString(0,2,"    Wait ...    ");