00. 目录
文章目录
01. FR801xH概述
FR801xH 系列芯片是面向SOC(片上系统),易于快速开发的低功耗蓝牙芯片。基于 Freqchip 的蓝牙智能固件和协议栈的支持,完全兼容蓝牙 V5.3(LE 模式)协议。同时用户可以基于芯片内置的 ARM CorteM3 嵌入式 32 位高性能单片机开发各种应用程序。
蓝牙智能固件包括 L2CAP 服务层协议、安全管理器 (SM)、属性协议(ATT)、通用属性配置文件 (GATT)和通用访问配置文件(GAP)。此外,还 支持应用程序配置文件,例如接近度、健康温度计、 心率、血压、血糖、人机界面设备(HID)和 SDK (包括驱动程序、OS-API 等)。SDK 还集成了用于网络应用程序的 SIG Mesh 协议。
采用 Freqchip 的创新技术,将 PMU(锂电池充电 器+LDO)、带 XIP 模式的 QSPI FLASH ROM、 I2C、UART、GPIO、ADC、PWM 集成在一块芯片中,为客户提供:
- 竞争力的功耗
- 稳定的蓝牙连接
- 极低的 BOM 成本
02. FR801xH功能框图
03. GPIO相关类型
位于 components\driver\include\driver_gpio.h。
3.1 system_port_t
enum system_port_t
{
GPIO_PORT_A,
GPIO_PORT_B,
GPIO_PORT_C,
GPIO_PORT_D,
};
3.2 system_port_bit_t
enum system_port_bit_t
{
GPIO_BIT_0,
GPIO_BIT_1,
GPIO_BIT_2,
GPIO_BIT_3,
GPIO_BIT_4,
GPIO_BIT_5,
GPIO_BIT_6,
GPIO_BIT_7,
};
3.3 DIR
#define GPIO_DIR_IN 1
#define GPIO_DIR_OUT 0
04. GPIO相关函数
4.1 gpio_portX_write
/*********************************************************************
* @fn gpio_porta_write
*
* @brief set gpio PORTA output value.
*
* @param value - output value.
*
* @return None.
*/
__INLINE void gpio_porta_write(uint8_t value)
{
REG_PL_WR(GPIO_PORTA_DATA, value);
}
/*********************************************************************
* @fn gpio_portb_write
*
* @brief set gpio PORTB output value.
*
* @param value - output value.
*
* @return None.
*/
__INLINE void gpio_portb_write(uint8_t value)
{
REG_PL_WR(GPIO_PORTB_DATA, value);
}
/*********************************************************************
* @fn gpio_portc_write
*
* @brief set gpio PORTC output value.
*
* @param value - output value.
*
* @return None.
*/
__INLINE void gpio_portc_write(uint8_t value)
{
REG_PL_WR(GPIO_PORTC_DATA, value);
}
/*********************************************************************
* @fn gpio_portd_write
*
* @brief set gpio PORTD output value.
*
* @param value - output value.
*
* @return None.
*/
__INLINE void gpio_portd_write(uint8_t value)
{
REG_PL_WR(GPIO_PORTD_DATA, value);
}
void gpio_portX_write(uint8_t value)
功能:
设置一组 IO 由大数字控制时的输出值,x 为 a、b、c、d
参数:
value IO 的输出值
返回值:
无
4.2 gpio_portX_read
/*********************************************************************
* @fn gpio_porta_read
*
* @brief get current value of gpio PORTA.
*
* @param None.
*
* @return current value of gpio PORTA.
*/
__INLINE uint8_t gpio_porta_read(void)
{
return REG_PL_RD(GPIO_PORTA_DATA);
}
/*********************************************************************
* @fn gpio_portb_read
*
* @brief get current value of gpio PORTB.
*
* @param None.
*
* @return current value of gpio PORTB.
*/
__INLINE uint8_t gpio_portb_read(void)
{
return REG_PL_RD(GPIO_PORTB_DATA);
}
/*********************************************************************
* @fn gpio_portc_read
*
* @brief get current value of gpio PORTC.
*
* @param None.
*
* @return current value of gpio PORTC.
*/__INLINE uint8_t gpio_portc_read(void)
{
return REG_PL_RD(GPIO_PORTC_DATA);
}
/*********************************************************************
* @fn gpio_portd_read
*
* @brief get current value of gpio PORTD.
*
* @param None.
*
* @return current value of gpio PORTD.
*/
__INLINE uint8_t gpio_portd_read(void)
{
return REG_PL_RD(GPIO_PORTD_DATA);
}
uint8_t gpio_portX_read(void)
功能:
获取一组 IO 由大数字控制时的当前值,x 为 a、b、c、d
参数:
无
返回值:
IO 的当前值
4.3 gpio_portX_set_dir
/*********************************************************************
* @fn gpio_porta_set_dir
*
* @brief set gpio works in output or input mode.
*
* @param dir - the in-out direction of gpio, each bit represent one channel,
* 0: output, 1:input.
*
* @return None.
*/
__INLINE void gpio_porta_set_dir(uint8_t dir)
{
REG_PL_WR(GPIO_PORTA_DIR, dir);
}
/*********************************************************************
* @fn gpio_portb_set_dir
*
* @brief set gpio works in output or input mode.
*
* @param dir - the in-out direction of gpio, each bit represent one channel,
* 0: output, 1:input.
*
* @return None.
*/
__INLINE void gpio_portb_set_dir(uint8_t dir)
{
REG_PL_WR(GPIO_PORTB_DIR, dir);
}
/*********************************************************************
* @fn gpio_portc_set_dir
*
* @brief set gpio works in output or input mode.
*
* @param dir - the in-out direction of gpio, each bit represent one channel,
* 0: output, 1:input.
*
* @return None.
*/
__INLINE void gpio_portc_set_dir(uint8_t dir)
{
REG_PL_WR(GPIO_PORTC_DIR, dir);
}
/*********************************************************************
* @fn gpio_portd_set_dir
*
* @brief set gpio works in output or input mode.
*
* @param dir - the in-out direction of gpio, each bit represent one channel,
* 0: output, 1:input.
*
* @return None.
*/
__INLINE void gpio_portd_set_dir(uint8_t dir)
{
REG_PL_WR(GPIO_PORTD_DIR, dir);
}
void gpio_portX_set_dir(uint8_t dir)
功能:
设置一组 IO 由大数字控制时的输入输出,x 为 a、b、c、d
参数:
dir 输入输出,每一位对应一个 IO,0:输出;1:输入
返回值:
无
4.4 gpio_portX_get_dir
/*********************************************************************
* @fn gpio_porta_set_dir
*
* @brief get current gpio PORTA in-out setting.
*
* @param None.
*
* @return current setting.
*/
__INLINE uint8_t gpio_porta_get_dir(void)
{
return REG_PL_RD(GPIO_PORTA_DIR);
}
/*********************************************************************
* @fn gpio_portb_set_dir
*
* @brief get current gpio PORTB in-out setting.
*
* @param None.
*
* @return current setting.
*/
__INLINE uint8_t gpio_portb_get_dir(void)
{
return REG_PL_RD(GPIO_PORTB_DIR);
}
/*********************************************************************
* @fn gpio_portc_set_dir
*
* @brief get current gpio PORTC in-out setting.
*
* @param None.
*
* @return current setting.
*/
__INLINE uint8_t gpio_portc_get_dir(void)
{
return REG_PL_RD(GPIO_PORTC_DIR);
}
/*********************************************************************
* @fn gpio_portd_set_dir
*
* @brief get current gpio PORTD in-out setting.
*
* @param None.
*
* @return current setting.
*/
__INLINE uint8_t gpio_portd_get_dir(void)
{
return REG_PL_RD(GPIO_PORTD_DIR);
}
uint8_t gpio_portX_get_dir(void)
功能:
获取一组 IO 由大数字控制时的输入输出设置,x 为 a、b、c、d
参数:
无
返回值:
当前的输入输出配置
4.5 gpio_set_dir
/*********************************************************************
* @fn gpio_set_dir
*
* @brief set specific gpio channel working mode: output or input.
*
* @param port - which port this channel belongs to. @ref system_port_t
* bit - channel number. @ref system_port_bit_t
* dir - in-out selection, should be GPIO_DIR_IN or GPIO_DIR_OUT.
*
* @return None.
*/
void gpio_set_dir(enum system_port_t port, enum system_port_bit_t bit, uint8_t dir);
功能:
设置 IO 由大数字控制时的输入输出,一次设置一个 IO
参数:
port IO 所属的端口组
bit IO 的 channel 编号
dir 输入或者输出
返回值:
无
示例
gpio_set_dir(GPIO_PORT_A, GPIO_BIT_0, GPIO_DIR_OUT);
05. 低功耗模式GPIO接口
5.1 pmu_set_gpio_value
/*********************************************************************
* @fn pmu_set_gpio_value
*
* @brief set value of IOs which are controlled by PMU.
* example usage:
* pmu_set_gpio_value(GPIO_PORT_A, (1<<GPIO_BIT0)|((1<<GPIO_BIT1), 1)
*
* @param port - which group the io belongs to, @ref system_port_t
* bits - the numbers of io
* value - 1: set the IO to high, 0: set the IO to low.
*
* @return None.
*/
void pmu_set_gpio_value(enum system_port_t port, uint8_t bits, uint8_t value);
功能:
当某个 pin 脚被配置为 pmu gpio 控制,并且是输出模式时,设置该 pin 脚的值
参数:
port 选择 pin 脚对应的 port 口,一共有 4 个 port 口,PA,PB,PC,PD。参见 enum system_port_t 定义。
bits 选择 pin 脚对应的 pin 号码,bit7~bit0 分别代表每个 port 口的 pin7~pin0。每个 bit 位表示该 pin 被选中。
Value 设置 pin 脚输出值。只能填以下二值:1,该 pin 输出为高。0,该 pin 输出为低。
返回值:
无
示例
void pmu_gpio_test(void)
{
pmu_set_port_mux(GPIO_PORT_A,GPIO_BIT_0,PMU_PORT_MUX_GPIO);
pmu_set_port_mux(GPIO_PORT_A,GPIO_BIT_1,PMU_PORT_MUX_GPIO);
pmu_set_pin_to_PMU(GPIO_PORT_A,BIT(0)|BIT(1) );
pmu_set_pin_dir(GPIO_PORT_A,BIT(0)|BIT(1), GPIO_DIR_OUT);
pmu_set_pin_pull(GPIO_PORT_A, BIT(0)|BIT(1), true);
pmu_set_gpio_value(GPIO_PORT_A, BIT(0)|BIT(1), 1);
co_delay_100us(10);
pmu_set_gpio_value(GPIO_PORT_A, BIT(0)|BIT(1), 0);
}
5.2 pmu_get_gpio_value
/*********************************************************************
* @fn pmu_get_gpio_value
*
* @brief get value of IO which are controlled by PMU and in GPIO mode.
* example usage:
* pmu_get_gpio_value(GPIO_PORT_A, GPIO_BIT_0)
*
* @param port - which group the io belongs to, @ref system_port_t
* bit - the number of io
*
* @return 1: the IO is high, 0: the IO is low..
*/
uint8_t pmu_get_gpio_value(enum system_port_t port, uint8_t bit);
功能:
当某个 pin 脚被配置为 pmu gpio 控制,并且是输入模式时,获取该 pin 脚的值。
参数:
port 选择 pin 脚对应的 port 口,一共有 4 个 port 口,PA,PB,PC,PD。参见 enum system_port_t 定义。
bit 选择 pin 脚对应的 pin 号码,参见 enum system_port_bit_t 定义
返回值:
IO口的值
示例
void pmu_gpio_test(void)
{
pmu_set_port_mux(GPIO_PORT_A,GPIO_BIT_2,PMU_PORT_MUX_GPIO);
pmu_set_port_mux(GPIO_PORT_A,GPIO_BIT_3,PMU_PORT_MUX_GPIO);
pmu_set_pin_to_PMU(GPIO_PORT_A,BIT(2)|BIT(3) );
pmu_set_pin_dir(GPIO_PORT_A,BIT(2)|BIT(3), false);
co_printf("PA2:%d,PA3:%d\r\n",pmu_get_gpio_value(GPIO_PORT_A,GPIO_BIT_2)
,pmu_get_gpio_value(GPIO_PORT_A,GPIO_BIT_3) );
}
06. 按键控制LED闪烁模式(非阻塞)
6.1 定时器实现非阻塞式程序
•程序功能:按键控制LED,使其切换不同的点亮模式
•程序要求:按键灵敏,每次按键按下都能准确切换模式
模块要高度封装,主程序调用要简洁
在任何时候模块代码都不能阻塞主程序
6.2 阻塞和非阻塞
•阻塞:执行某段程序时,CPU因为需要等待延时或者等待某个信号而被迫处于暂停状态一段时间,程序执行时间较长或者时间不定
•非阻塞:执行某段程序时,CPU不会等待,程序很快执行结束
6.3 定时器扫描按键-单按键
•定时中断,每隔20ms读取一次本次引脚值和上次引脚值
•判断,如果本次是1,上次是0,则表示按键按下且当前处于刚松手的状态
•置键码标志位,向主程序报告此事件
6.4 定时器扫描按键-多按键
•先写一个获取键码值的子函数(非阻塞式)
•定时中断,每隔20ms读取一次本次键码值和上次键码值
•判断,如果本次是0,上次非0,则表示按键按下且当前处于刚松手的状态
•置键码标志位,向主程序报告此事件
6.5 定时器实现LED闪烁
•定时中断,每隔1ms计次变量自增
•计次变量计到周期值时,归零
•判断,如果计次变量小于一个比较值,开灯,否则,关灯
07. 程序示例
led.h
#ifndef __LED_H__
#define __LED_H__
// PA1
void led_init(void);
void led_set_mode(uint8_t mode);
void led_tick(void);
#endif
led.c
#include <stdio.h>
#include "os_timer.h"
#include "co_printf.h"
#include "driver_gpio.h"
static uint8_t led_mode = 0;
static uint16_t led_count = 0;
// PA1
void led_init(void)
{
system_set_port_mux(GPIO_PORT_A, GPIO_BIT_1, PORTA1_FUNC_A1);
gpio_set_dir(GPIO_PORT_A, GPIO_BIT_1, GPIO_DIR_OUT);
gpio_set_pin_value(GPIO_PORT_A, GPIO_BIT_1, 0);
}
void led_set_mode(uint8_t mode)
{
if (led_mode != mode)
{
led_mode = mode;
led_count = 0;
}
}
static void led_on(void)
{
gpio_set_pin_value(GPIO_PORT_A, GPIO_BIT_1, 1);
}
static void led_off(void)
{
gpio_set_pin_value(GPIO_PORT_A, GPIO_BIT_1, 0);
}
void led_tick(void)
{
if (0 == led_mode)
{
// 熄灭
led_off();
}
else if (1 == led_mode)
{
// 常亮
led_on();
}
else if (2 == led_mode)
{
led_count++;
led_count %= 100;
// 慢闪
if (led_count < 50)
{
led_on();
}
else
{
led_off();
}
}
else if (3 == led_mode)
{
led_count++;
led_count %= 10;
// 快闪
if (led_count < 5)
{
led_on();
}
else
{
led_off();
}
}
else if (4 == led_mode)
{
led_count++;
led_count %= 100;
// 点闪
if (led_count < 10)
{
led_on();
}
else
{
led_off();
}
}
}
key.h
#ifndef __MY_KEY_H__
#define __MY_KEY_H__
#include "sys_utils.h"
// PC5
void key_init(void);
uint8_t key_get_status(void);
void key_tick(void);
void key_demo(void);
#endif
key.c
#include "key.h"
#include <stdio.h>
#include "os_timer.h"
#include "co_printf.h"
#include "driver_gpio.h"
#include "led.h"
// 按键检测定时器
os_timer_t key_timer;
// PC5
uint8_t key_status;
void key_init(void)
{
system_set_port_mux(GPIO_PORT_C, GPIO_BIT_5, PORTC5_FUNC_C5);
gpio_set_dir(GPIO_PORT_C, GPIO_BIT_5, GPIO_DIR_IN);
}
// 读取之后清零
uint8_t key_get_status(void)
{
uint8_t tmp;
if (key_status)
{
tmp = key_status;
key_status = 0;
return tmp;
}
return 0;
}
uint8_t key_get_pressed(void)
{
// 按键按下
if (gpio_get_pin_value(GPIO_PORT_C, GPIO_BIT_5) == 0)
{
return 1;
}
// 按键没有按下
return 0;
}
void key_tick(void)
{
static uint8_t count = 0;
static uint8_t prev_state = 0, curr_state = 0;
count++;
// 20ms检测一次
if (count >= 2)
{
count = 0;
prev_state = curr_state;
curr_state = key_get_pressed();
// co_printf("prev_state: %d curr_state: %d\r\n", prev_state, curr_state);
if (0 != prev_state && 0 == curr_state)
{
key_status = prev_state;
}
}
}
//--------------------------------------------------------------------------
// 定时器
void key_timer_func(void *parg)
{
uint8_t key_status = 0;
static uint8_t led_mode = 0;
// co_printf("timer timeout.....\r\n");
key_tick();
led_tick();
key_status = key_get_status();
if (1 == key_status)
{
led_mode++;
led_mode %= 5;
co_printf("led_mode: %d\r\n", led_mode);
led_set_mode(led_mode);
}
}
void key_demo(void)
{
uint8_t key_status = 0;
uint8_t led_mode = 0;
co_printf("key demo\r\n");
// 创建定时器1
os_timer_init(&key_timer, key_timer_func, NULL);
// 10ms定时器
os_timer_start(&key_timer, 10, 1);
key_init();
led_init();
}
程序运行结果
led_mode: 1
led_mode: 2
led_mode: 3
led_mode: 4
led_mode: 0
led_mode: 1
led_mode: 2
led_mode: 3
led_mode: 4
led_mode: 0
led_mode: 1
led_mode: 2
led_mode: 3
led_mode: 4