I2C 簡介
I2C 是雙線雙向的串行總線,它為裝置之間資料交換提供了一種簡單高效的方法。I2C 标準是一個具有沖突檢測機制和仲裁機制的真正意義上的多主機總線。它能防止兩個或者多個主機在同時請求控制總線時發生資料沖突。
I2C 總線控制器,能滿足 I2C 總線的各種規格并支援所有與 I2C 總線通信的傳輸模式。
I2C 總線使用連接配接裝置的"SCL"(串行時鐘總線)和"SDA"(串行資料總線)來傳送資訊。資料在主機與從機之間通過 SCL 時鐘線控制在 SDA 資料線上實作一個位元組一個位元組的同步傳輸, 每個位元組為 8位長度,一個 SCL 時鐘脈沖傳輸一個資料位,資料由最高位 MSB 開始傳輸,每個傳輸位元組後跟随一個應答位, 每個位在 SCL 為高時采樣; 是以, SDA 線隻有在 SCL 為低時才可以改變, 在 SCL為高時 SDA 保持穩定。 當 SCL 為高時, SDA 線上的跳變視為指令中斷(START 或 STOP), I2C 邏輯能自主地處理位元組的傳輸。它能保持跟蹤串行傳送,而且還有一個狀态寄存器(I2C_SR)能反映I2C 總線控制器和 I2C 總線的狀态。
I2C 主要特性
I2C 控制器支援以下特性:
⚫ 支援主機發送/接收,從機發送/接收四種工作模式
⚫ 支援标準(100Kbps)/快速(400Kbps)/高速(1Mbps)三種工作速率
⚫ 支援 7 位尋址功能
⚫ 支援噪聲過濾功能
⚫ 支援廣播位址
⚫ 支援中斷狀态查詢功能
I2C 協定描述
通常标準 I2C 傳輸協定包含四個部分:
- 起始信号或重複起始信号
- 從機位址傳輸和 R/W 位傳輸
- 資料傳輸
- 停止信号
/********************************************************************************
* @file bsp_i2c.c
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-04-11
* @brief NULL
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include <stdlib.h>
#include "RTE_Components.h"
#include CMSIS_device_header
#include "cx32l003_hal.h"
#include "bsp_gpio.h"
#include "bsp_i2c.h"
/* Private Includes ----------------------------------------------------------*/
#include "business_gpio.h"
#include "business_function.h"
/* Private Variables ---------------------------------------------------------*/
#if BS_I2C0_EN
// 定義IIC0資訊句柄
static I2C_HandleTypeDef i2c0_handle_t = {
.Instance = I2C,
.Init.master = I2C_MASTER_MODE_ENABLE, // 主機模式使能
.Init.slave = I2C_SLAVE_MODE_DISABLE, // 從機模式禁止
.Mode = HAL_I2C_MODE_MASTER, // 主機模式
.Init.broadack = I2C_BROAD_ACK_DISABLE, // 廣播位址應答禁止
.Init.speedclock = BS_I2C0_SPEED_RATE, // I2C傳輸速率
.State = HAL_I2C_STATE_RESET
};
#endif
/**
* @brief [初始化] IIC初始化
* @note NULL
* @param i2c_bus: IIC組号
* @retval None
*/
void bsp_i2c_init(bsp_i2c_bus_t i2c_bus)
{
#if BS_I2C0_EN
if (i2c_bus == I2C_BUS0)
{
__HAL_RCC_I2C_CLK_ENABLE();
BS_I2C0_SDA_GPIO_CLK_ENABLE();
bsp_gpio_init_i2c(BS_I2C0_SCL_GPIO_PORT, BS_I2C0_SCL_PIN, GPIO_AF4_I2C_SCL);
bsp_gpio_init_i2c(BS_I2C0_SDA_GPIO_PORT, BS_I2C0_SDA_PIN, GPIO_AF4_I2C_SDA);
HAL_I2C_Init(&i2c0_handle_t);
}
#endif
}
/**
* @brief [反初始化] IIC關閉時鐘并複位引腳
* @note NULL
* @param i2c_bus: IIC組号
* @retval None
*/
void bsp_i2c_deinit(bsp_i2c_bus_t i2c_bus)
{
#if BS_I2C0_EN
if (i2c_bus == I2C_BUS0)
{
__HAL_RCC_I2C_CLK_DISABLE();
bsp_gpio_deinit(BS_I2C0_SCL_GPIO_PORT, BS_I2C0_SCL_PIN);
bsp_gpio_deinit(BS_I2C0_SDA_GPIO_PORT, BS_I2C0_SDA_PIN);
HAL_I2C_DeInit(&i2c0_handle_t);
}
#endif
}
/**
* @brief i2c讀取一個位元組
* @note NULL
* @param i2c_bus: IIC組号
* @param dev_addr: 器件位址
* @param reg_addr: 寄存器位址
* @param r_data: 欲發送的資料頭指針
* @retval 0--成功 1--失敗
*/
uint8_t bsp_i2c_read_byte(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t reg_addr, uint8_t *r_data)
{
if (i2c_bus == I2C_BUS0)
{
#if BS_I2C0_EN
return HAL_I2C_Master_Receive(&i2c0_handle_t, dev_addr, ®_addr, 1, r_data, 1);
#else
return HAL_ERROR;
#endif
}
return HAL_ERROR;
}
/**
* @brief i2c讀取多個位元組
* @note NULL
* @param i2c_bus: IIC組号
* @param dev_addr: 器件位址
* @param w_data: txbuff頭指針 資料格式:(reg_addr, w_data, w_data1, ...)
* @param w_size: txbuff長度
* @param r_data: rxbuff的頭指針
* @param r_size: rxbuff的大小
* @retval 0--成功 1--失敗
*/
uint8_t bsp_i2c_read_nbyte(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t *w_data, uint16_t w_size, uint8_t *r_data, uint16_t r_size)
{
if (i2c_bus == I2C_BUS0)
{
#if BS_I2C0_EN
return HAL_I2C_Master_Receive(&i2c0_handle_t, dev_addr, w_data, w_size, r_data, r_size);
#else
return HAL_ERROR;
#endif
}
return HAL_ERROR;
}
/**
* @brief i2c寫入一個位元組
* @note NULL
* @param i2c_bus: IIC組号
* @param dev_addr: 器件位址
* @param reg_addr: 寄存器位址
* @param w_data: 需要寫入的位元組值
* @retval 0--成功 1--失敗
*/
uint8_t bsp_i2c_write_byte(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t reg_addr, uint8_t w_data)
{
uint8_t ret = HAL_OK;
if (i2c_bus == I2C_BUS0)
{
#if BS_I2C0_EN
uint8_t data[2] = {reg_addr, w_data};
ret = HAL_I2C_Master_Transmit(&i2c0_handle_t, dev_addr, data, 2);
#else
ret = HAL_ERROR;
#endif
}
else
{
ret = HAL_ERROR;
}
return ret;
}
/**
* @brief i2c寫入多個位元組
* @note NULL
* @param i2c_bus: IIC組号
* @param dev_addr: 器件位址
* @param w_data: txbuff頭指針 資料格式:(reg_addr, w_data, w_data1, ...)
* @param w_size: txbuff長度
* @retval 0--成功 1--失敗
*/
uint8_t bsp_i2c_write_nbyte(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t *w_data, uint16_t w_size)
{
uint8_t ret = HAL_OK;
if (i2c_bus == I2C_BUS0)
{
#if BS_I2C0_EN
ret = HAL_I2C_Master_Transmit(&i2c0_handle_t, dev_addr, w_data, w_size);
#else
ret = HAL_ERROR;
#endif
}
else
{
ret = HAL_ERROR;
}
return ret;
}
// ---------------------散裝函數-----------------------------
/**
* @brief i2c寫入多個位元組且無停止信号(帶起始信号和器件位址)
* @note NULL
* @param i2c_bus: IIC組号
* @param dev_addr: 器件位址
* @param w_data: txbuff頭指針 資料格式:(reg_addr, w_data, w_data1, ...)
* @param w_size: txbuff長度
* @retval 0--成功 1--失敗
*/
uint8_t bsp_i2c_write_nbyte_nostop(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t *w_data, uint16_t w_size)
{
uint8_t ret = HAL_OK;
if (i2c_bus == I2C_BUS0)
{
#if BS_I2C0_EN
ret = HAL_I2C_Master_Transmit_NOStop(&i2c0_handle_t, dev_addr, w_data, w_size);
#else
ret = HAL_ERROR;
#endif
}
else
{
ret = HAL_ERROR;
}
return ret;
}
/**
* @brief i2c寫入多個位元組且無停止信号(不帶起始信号和器件位址)
* @note 必須配合[bsp_i2c_write_nbyte_nostop()]使用
* @param i2c_bus: IIC組号
* @param w_data: txbuff頭指針 資料格式:(w_data, w_data1, ...)
* @param w_size: txbuff長度
* @retval 0--成功 1--失敗
*/
uint8_t bsp_i2c_send_nbyte(bsp_i2c_bus_t i2c_bus, uint8_t *w_data, uint16_t w_size)
{
uint8_t ret = HAL_OK;
if (i2c_bus == I2C_BUS0)
{
#if BS_I2C0_EN
uint16_t i = 0;
uint32_t i2c_flag = 0XFF;
HAL_I2C_Wait_Flag(&i2c0_handle_t, &i2c_flag);
while (i < w_size)
{
i2c_flag = 0XFF;
HAL_I2C_Send_Byte(&i2c0_handle_t, w_data[i]);
HAL_I2C_Wait_Flag(&i2c0_handle_t, &i2c_flag);
if (i2c_flag != I2C_FLAG_MASTER_TX_DATA_ACK) // 0x00000028U
{
i2c0_handle_t.State = HAL_I2C_STATE_ERROR;
i2c0_handle_t.ErrorCode = i2c0_handle_t.PreviousState;
return HAL_ERROR;
}
i++;
}
#else
ret = HAL_ERROR;
#endif
}
else
{
ret = HAL_ERROR;
}
return ret;
}
/**
* @brief 發送一個停止信号
* @note NULL
* @param i2c_bus: IIC組号
* @param state: false--清除中斷标志位 true--不清除中斷标記位
* @retval 0--成功 1--失敗
*/
uint8_t bsp_i2c_stop(bsp_i2c_bus_t i2c_bus, bool state)
{
uint8_t ret = HAL_OK;
if (i2c_bus == I2C_BUS0)
{
#if BS_I2C0_EN
HAL_I2C_Stop_Config(&i2c0_handle_t, (FunctionalState)true);
#else
ret = HAL_ERROR;
#endif
}
else
{
ret = HAL_ERROR;
}
return ret;
}
/********************************************************************************
* @file bsp_i2c.h
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-04-11
* @brief NULL
********************************************************************************/
#ifndef __BSP_I2C_H
#define __BSP_I2C_H
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
/* Public enum ---------------------------------------------------------------*/
typedef enum
{
I2C_BUS0 = 0,
I2C_BUS1 = 1,
I2C_BUS2 = 2,
} bsp_i2c_bus_t;
/* Public Function Prototypes ------------------------------------------------*/
void bsp_i2c_init(bsp_i2c_bus_t i2c_bus);
void bsp_i2c_deinit(bsp_i2c_bus_t i2c_bus);
// 成品全套邏輯 啟動-應答-資料-停止
uint8_t bsp_i2c_read_byte(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t reg_addr, uint8_t *r_data);
uint8_t bsp_i2c_read_nbyte(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t *w_data, uint16_t w_size, uint8_t *r_data, uint16_t r_size);
uint8_t bsp_i2c_write_byte(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t reg_addr, uint8_t w_data);
uint8_t bsp_i2c_write_nbyte(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t *w_data, uint16_t w_size);
// 散裝函數
uint8_t bsp_i2c_write_nbyte_nostop(bsp_i2c_bus_t i2c_bus, uint8_t dev_addr, uint8_t *w_data, uint16_t w_size);
uint8_t bsp_i2c_send_nbyte(bsp_i2c_bus_t i2c_bus, uint8_t *w_data, uint16_t w_size);
uint8_t bsp_i2c_stop(bsp_i2c_bus_t i2c_bus, bool state);
#endif