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. I2C相关类型
位于 components\driver\include\driver_iic.h
。
3.1 iic_channel_t
enum iic_channel_t
{
IIC_CHANNEL_0,
IIC_CHANNEL_1,
IIC_CHANNEL_MAX,
};
3.2 enum_iic_int_indx_t
typedef enum
{
INT_TRANS_DONE = 0x00000001,
INT_ARB_FAIL = 0x00000002,
INT_NO_ACK = 0x00000004,
INT_MS_DATA_REQ = 0x00000008,
INT_SLV_DATA_REQ = 0x00000010,
INT_RX_FFF = 0x00000020,
INT_RX_FFNE = 0x00000040,
INT_MS_TX_FFNF = 0x00000080,
INT_SW_RST = 0x00000100,
INT_ADD_TYPE_SEL = 0x00000200,
INT_SLV_TX_FFNF = 0x00000400,
}enum_iic_int_indx_t;
04. I2C相关API
4.1 iic_init
/*********************************************************************
* @fn iic_init
*
* @brief Initialize iic instance.
*
* @param channel - IIC_CHANNEL_0 or IIC_CHANNEL_1.
* speed - SCL speed when working as master, N * 1000
* slave_addr - local address when working as slave
*
* @return None.
*/
void iic_init(enum iic_channel_t channel, uint16_t speed, uint16_t slave_addr);
功能:
初始化 IIC 模块,默认配置为为 7 位地址模式
参数:
channel 初始化对象,可选 IIC_CHANNEL_0、IIC_CHANNEL_1
speed 配置总线时钟速率为 speed*1000
slave_addr 当本机工作在从机模式时的从机地址
返回值:
None
4.2 iic_write_byte
/*********************************************************************
* @fn iic_write_byte
*
* @brief write one byte to slave.
*
* @param channel - IIC_CHANNEL_0 or IIC_CHANNEL_1.
* slave_addr - slave address
* reg_addr - which register to be writen
* data - data to be writen
*
* @return None.
*/
uint8_t iic_write_byte(enum iic_channel_t channel, uint8_t slave_addr, uint8_t reg_addr, uint8_t data);
功能:
将一个字节数据发送给从机的特定地址
参数:
channel - 操作对象
slave_addr - 从机地址
reg_addr - 操作的从机寄存器地址
data - 待写入的数据
返回值:
None
4.3 iic_write_bytes
/*********************************************************************
* @fn iic_write_bytes
*
* @brief write multi-bytes to slave.
*
* @param channel - IIC_CHANNEL_0 or IIC_CHANNEL_1.
* slave_addr - slave address
* reg_addr - which register to be writen
* buffer - pointer to data buffer
* length - how many bytes to be written
*
* @return None.
*/
uint8_t iic_write_bytes(enum iic_channel_t channel, uint8_t slave_addr, uint8_t reg_addr, uint8_t *buffer, uint16_t length);
功能:
将多个字节数据发送给从机的特定地址
参数:
channel - 操作对象
slave_addr - 从机地址
reg_addr - 操作的从机寄存器起始地址
buffer - 待写入的数据
length - 待写入的数据长度
返回值:
true:写入成功;false:写入失败
4.4 iic_read_byte
/*********************************************************************
* @fn iic_read_byte
*
* @brief read one byte frome slave.
*
* @param channel - IIC_CHANNEL_0 or IIC_CHANNEL_1.
* slave_addr - slave address
* reg_addr - which register to be written
* buffer - store data to buffer
*
* @return None.
*/
uint8_t iic_read_byte(enum iic_channel_t channel, uint8_t slave_addr, uint8_t reg_addr, uint8_t *buffer);
功能:
从从机的特定地址读取一个字节数据
参数:
channel - 操作对象
slave_addr - 从机地址
reg_addr - 操作的从机寄存器地址
buffer - 读取数据的保存地址
返回值:
true:读取成功;false:读取失败
4.5 iic_read_bytes
/*********************************************************************
* @fn iic_read_bytes
*
* @brief read multi-bytes frome slave.
*
* @param channel - IIC_CHANNEL_0 or IIC_CHANNEL_1.
* slave_addr - slave address
* reg_addr - which register to be written
* buffer - buffer pointer to be written
* length - how many bytes to be read
*
* @return None.
*/
uint8_t iic_read_bytes(enum iic_channel_t channel, uint8_t slave_addr, uint8_t reg_addr, uint8_t *buffer, uint16_t length);
功能:
从从机的特定地址读取多个字节数据
参数:
channel - 操作对象
slave_addr - 从机地址
reg_addr - 操作的从机寄存器起始地址
buffer - 读取数据的保存地址
length - 待读取的数据长度
返回值:
true:读取成功;false:读取失败
05. 软件I2C程序示例
myi2c.h
void och1973_i2c_init(void);
void och_i2c_read(uint8_t reg, uint8_t length, uint8_t *i2cdata);
void och_i2c_write(uint8_t reg, uint8_t length, uint8_t *i2cdata);
void och_delay(uint8_t delayms);
void I2C_Configuration(void);
myi2c.c
#define SUCCESS 1 // 响应正常
#define ERROR 0 // 响应错误
#define ACK_OK 1 // 响应正常
#define ACK_ERR 0 // 响应错误
#define IIC_DEVICE_RADDR (0x0C << 1) + 1 // IIC 初始化地址
#define IIC_DEVICE_WADDR 0x0C << 1
// IIC 通信延时,User_IIC_Delay()函数使用,
// 数值越小,则通信速度越快
#define IIC_DelayVaule 3
#define User_IIC_SCL(a) (a ? gpio_porta_write(gpio_porta_read() | (1 << 6)) : gpio_porta_write(gpio_porta_read() & ~(1 << 6)))
#define User_IIC_SDA(a) (a ? gpio_porta_write(gpio_porta_read() | (1 << 7)) : gpio_porta_write(gpio_porta_read() & ~(1 << 7)))
#define User_IIC_READ_SDA() (gpio_porta_read() & (1 << 7) ? 1 : 0)
#define User_IIC_SET_SDA_INPUT() (gpio_porta_set_dir(gpio_porta_get_dir() | (1 << 7)))
#define User_IIC_SET_SDA_OUPUT() (gpio_porta_set_dir(gpio_porta_get_dir() & ~(1 << 7)))
/*******************************************************************************
* Function Name : I2C_Configuration
* Description : EEPROM 管脚配置
* Input : None
* Output : None
* Return : None
* Attention : None
*******************************************************************************/
void I2C_Configuration(void)
{
system_set_port_mux(GPIO_PORT_A, GPIO_BIT_6, PORTA6_FUNC_A6);
system_set_port_mux(GPIO_PORT_A, GPIO_BIT_7, PORTA7_FUNC_A7);
gpio_set_dir(GPIO_PORT_A, GPIO_BIT_6, GPIO_DIR_OUT);
gpio_set_dir(GPIO_PORT_A, GPIO_BIT_7, GPIO_DIR_OUT);
gpio_set_pin_value(GPIO_PORT_A, GPIO_BIT_6, 1);
gpio_set_pin_value(GPIO_PORT_A, GPIO_BIT_7, 1);
}
/*****************************************************************************************
* 函 数 名: User_IIC_Delay
* 入口参数: a - 延时时间
* 返 回 值: 无
* 函数功能: 简单延时函数
* 说 明: 为了移植的简便性且对延时精度要求不高,所以不需要使用定时器做延时
******************************************************************************************/
void User_IIC_Delay(uint32_t a)
{
volatile uint16_t i;
while (a--)
{
for (i = 0; i < 2; i++)
;
}
}
/*****************************************************************************************
* 函 数 名: User_IIC_Start * 入口参数: 无
* 返 回 值: 无
* 函数功能: IIC 起始信号
* 说 明: 在 SCL 处于高电平期间,SDA 由高到低跳变为起始信号
******************************************************************************************/
void User_IIC_Start(void)
{
User_IIC_SDA(1);
User_IIC_SCL(1);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SDA(0);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(0);
User_IIC_Delay(IIC_DelayVaule);
}
/*****************************************************************************************
* 函 数 名: User_IIC_Stop
* 入口参数: 无
* 返 回 值: 无
* 函数功能: IIC 停止信号
* 说 明: 在 SCL 处于高电平期间,SDA 由低到高跳变为起始信号
******************************************************************************************/
void User_IIC_Stop(void)
{
User_IIC_SCL(0);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SDA(0);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(1);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SDA(1);
User_IIC_Delay(IIC_DelayVaule);
}
/*****************************************************************************************
* 函 数 名: User_IIC_ACK
* 入口参数: 无
* 返 回 值: 无
* 函数功能: IIC 应答信号
* 说 明: 在 SCL 为高电平期间,SDA 引脚输出为低电平,产生应答信号
******************************************************************************************/
void User_IIC_ACK(void)
{
User_IIC_SCL(0);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SDA(0);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(1);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(0); // SCL 输出低时,SDA 应立即拉高,释放总线
User_IIC_SDA(1);
User_IIC_Delay(IIC_DelayVaule);
}
/*****************************************************************************************
* 函 数 名: User_IIC_NoACK
* 入口参数: 无
* 返 回 值: 无
* 函数功能: IIC 非应答信号
* 说 明: 在 SCL 为高电平期间,若 SDA 引脚为高电平,产生非应答信号
******************************************************************************************/
void User_IIC_NoACK(void)
{
User_IIC_SCL(0);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SDA(1);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(1);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(0);
User_IIC_Delay(IIC_DelayVaule);
}
/*****************************************************************************************
* 函 数 名: User_IIC_WaitACK
* 入口参数: 无
* 返 回 值: 无
* 函数功能: 等待接收设备发出应答信号
* 说 明: 在 SCL 为高电平期间,若检测到 SDA 引脚为低电平,则接收设备响应正常
******************************************************************************************/
uint8_t User_IIC_WaitACK(void)
{
uint8_t ret;
User_IIC_SDA(1);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(1);
User_IIC_SET_SDA_INPUT(); // 设置 SDA 为输入模式
User_IIC_Delay(IIC_DelayVaule);
if (User_IIC_READ_SDA() != 0) // 判断设备是否有做出响应
{
User_IIC_SCL(0);
User_IIC_Delay(IIC_DelayVaule);
ret = ACK_ERR; // 无应答
}
else
{
User_IIC_SCL(0);
User_IIC_Delay(IIC_DelayVaule);
ret = ACK_OK; // 应答正常
}
User_IIC_SET_SDA_OUPUT(); // 设置 SDA 为输出模式
return ret;
}
/*****************************************************************************************
* 函 数 名: User_IIC_WriteByte
* 入口参数: IIC_Data - 要写入的 8 位数据
* 返 回 值: ACK_OK - 设备响应正常
* ACK_ERR - 设备响应错误
* 函数功能: 写一字节数据
* 说 明:高位在前
******************************************************************************************/
uint8_t User_IIC_WriteByte(uint8_t IIC_Data)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
User_IIC_SDA(IIC_Data & 0x80);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(1);
User_IIC_Delay(IIC_DelayVaule);
User_IIC_SCL(0);
if (i == 7)
{
User_IIC_SDA(1);
}
IIC_Data <<= 1;
}
return User_IIC_WaitACK(); // 等待设备响应
}
/*****************************************************************************************
* 函 数 名: User_IIC_ReadByte
* 入口参数: ACK_Mode - 响应模式,输入 1 则发出应答信号,输入 0 发出非应答信号
* 返 回 值: ACK_OK - 设备响应正常
* ACK_ERR - 设备响应错误
* 函数功能:读一字节数据
* 说 明:1.高位在前
* 2.应在主机接收最后一字节数据时发送非应答信号
******************************************************************************************/
uint8_t User_IIC_ReadByte(uint8_t ACK_Mode)
{
uint8_t IIC_Data = 0;
uint8_t i = 0;
User_IIC_SET_SDA_INPUT();
for (i = 0; i < 8; i++)
{
IIC_Data <<= 1;
User_IIC_SCL(1);
User_IIC_Delay(IIC_DelayVaule);
IIC_Data |= (User_IIC_READ_SDA() & 0x01);
User_IIC_SCL(0);
User_IIC_Delay(IIC_DelayVaule);
}
User_IIC_SET_SDA_OUPUT();
if (ACK_Mode == 1) // 应答信号
User_IIC_ACK();
else
User_IIC_NoACK(); // 非应答信号
return IIC_Data;
}
/*****************************************************************************************
* 函 数 名: IIC_WriteHandle
* 入口参数: 寄存器地址
* 返 回 值: 操作结果
*
* 函数功能:写设备地址和寄存器地址
******************************************************************************************/
uint8_t IIC_WriteHandle(uint8_t addr)
{
uint8_t status; // 状态标志位
// User_IIC_Start(); // 启动 IIC 通信
if (User_IIC_WriteByte(IIC_DEVICE_WADDR) == ACK_OK) // 写数据指令
{
if (User_IIC_WriteByte(addr) == ACK_OK) // 写入 8 位地址
status = SUCCESS; // 操作成功
}
else
{
status = ERROR;
}
return status;
}
/*****************************************************************************************
* 函 数 名: IIC_WriteData
* 入口参数: 寄存器地址 参数
* 返 回 值: 操作结果
*
* 函数功能:向寄存器地址写入参数
******************************************************************************************/
uint8_t IIC_WriteData(uint8_t addr, uint8_t value)
{
uint8_t status;
User_IIC_Start(); // 启动 IIC 通讯
if (IIC_WriteHandle(addr) == SUCCESS) // 写入要操作的寄存器
{
if (User_IIC_WriteByte(value) != ACK_OK) // 写数据
{
status = ERROR;
}
}
User_IIC_Stop(); // 停止通讯
status = SUCCESS; // 写入成功
return status;
}
/*****************************************************************************************
* 函 数 名: IIC_WriteReg
* 入口参数: 寄存器地址 参数
* 返 回 值: 操作结果
*
* 函数功能:向寄存器地址写入多个参数
******************************************************************************************/
uint8_t IIC_WriteReg(uint8_t addr, uint8_t cnt, uint8_t *value)
{
uint8_t status;
uint8_t i;
User_IIC_Start();
if (IIC_WriteHandle(addr) == SUCCESS) // 写入要操作的寄存器
{
for (i = 0; i < cnt; i++) // 计数
{
User_IIC_WriteByte(value[i]); // 写入数据
}
User_IIC_Stop(); // 停止 IIC 通信
status = SUCCESS; // 写入成功
}
else
{
User_IIC_Stop(); // 停止 IIC 通信
status = ERROR; // 写入失败
}
return status;
}
/*****************************************************************************************
* 函 数 名: IIC_ReadReg
* 入口参数: 寄存器地址 夺取个数 缓存
* 返 回 值: 操作结果
*
* 函数功能:从寄存器地址读取多个参数
******************************************************************************************/
uint8_t IIC_ReadReg(uint8_t addr, uint8_t cnt, uint8_t *value)
{
uint8_t status;
uint8_t i;
status = ERROR;
User_IIC_Start(); // 启动 IIC 通信
if (IIC_WriteHandle(addr) == SUCCESS) // 写入要操作的寄存器
{
User_IIC_Start(); // 重新启动 IIC 通讯
if (User_IIC_WriteByte(IIC_DEVICE_RADDR) == ACK_OK) // 发送读命令
{
for (i = 0; i < cnt; i++) // 计数
{
if (i == (cnt - 1))
{
value[i] = User_IIC_ReadByte(0); // 读到最后一个数据时发送 非应答信号
}
else
{
value[i] = User_IIC_ReadByte(1); // 发送应答信号
}
}
User_IIC_Stop(); // 停止 IIC 通信
status = SUCCESS;
}
}
User_IIC_Stop(); // 停止 IIC 通信
return (status);
}
void och_i2c_read(uint8_t reg, uint8_t length, uint8_t *i2cdata)
{
IIC_ReadReg(reg, length, i2cdata);
}
void och_i2c_write(uint8_t reg, uint8_t length, uint8_t *i2cdata)
{
IIC_WriteReg(reg, length, i2cdata);
}
void och_delay(uint8_t delayms)
{
co_delay_100us(delayms * 10);
}
demo.c
void och1973_demo(void)
{
uint8_t data[2] = {0};
uint8_t data_array[6]; // create an array to store data output from sensors
int16_t result[3];
char str[16];
I2C_Configuration();
OCH1973_CheckWIA(data);
co_printf("ID: 0x%2X 0x%2X\r\n", data[0], data[1]);
value = 0x0c;
och_i2c_write(OCH1973_REG_CNTL2, 1, &value);
co_delay_100us(1);
// 默认0x03
value = 0;
och_i2c_read(OCH1973_REG_CNTL2, 1, &value);
co_printf("OCH1973_REG_CNTL2: 0x%02x\r\n", value);
// 0: Magnetic output mode 1: Angle output mode
// OCH1973_OP_CONTINUOUS_MODE_6_500Hz OCH1973_Rising OCH1973_Falling
OCH1973_Init(OCH1973_VALUE_CONTINUOUS_MODE_1_5Hz, OCH1973_LOW_NOISE_MODE, OCH1973_HIGH_RANGE_MODE, OCH1973_Falling, OCH1973_XYplane, OCH_DISABLE);
// continuous measurement mode,measurement frequency is 100Hz,low noise mode,high range mode,OD-INT Interrupt edge Falling,XYplane,Enable Angle
while (1)
{
//===========Polling mode==================
if (OCH1973_CheckDRDY() == 1) // data is ready
{
OCH1973_GetXYZ_HLdata(data_array); // read datas
// OCH1973_Get_ANGLE(angle_array);
OCH1973_MergeHighLowData(data_array, result, 6);
och_i2c_read(OCH1973_REG_CNTL2, 1, &value);
co_printf("--->value: %d x: %d y: %d z: %d \r\n", value, result[2], result[1], result[0]);
}
co_delay_100us(10000);
}
}
程序运行结果
******Connected******
******Start Auto Burn******
****************************************************
******CRC Success******
******Burn Success******
?ID: 0x48 0xC0
OCH1973_REG_CNTL2: 0x0c