增加BSP文件
This commit is contained in:
parent
c9dc76f048
commit
8cc4b48b5e
|
@ -1,5 +1,10 @@
|
||||||
# 驱动列表
|
# 目录说明
|
||||||
|
|
||||||
|
手册:驱动文件对应的芯片手册
|
||||||
|
|
||||||
|
bsp:部分驱动中用到的底层实现
|
||||||
|
|
||||||
|
# 驱动列表
|
||||||
|
|
||||||
| 驱动 | 分类 | 驱动方式 | 功能 |
|
| 驱动 | 分类 | 驱动方式 | 功能 |
|
||||||
| ------------ | -------- | -------- | ------------------- |
|
| ------------ | -------- | -------- | ------------------- |
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include "gpios.h"
|
||||||
|
#include "gpio.h"
|
||||||
|
/**
|
||||||
|
* @brief 设置GPIO引脚为高电平
|
||||||
|
* @param {gpio_t} gpio - GPIO对象
|
||||||
|
* @note: 用于设置指定GPIO引脚为高电平。
|
||||||
|
*/
|
||||||
|
static void _set(gpio_t gpio)
|
||||||
|
{
|
||||||
|
GPIO_SET(gpio.port, gpio.pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置GPIO引脚为低电平
|
||||||
|
* @param {gpio_t} gpio - GPIO对象
|
||||||
|
* @note: 用于设置指定GPIO引脚为低电平。
|
||||||
|
*/
|
||||||
|
static void _reset(gpio_t gpio)
|
||||||
|
{
|
||||||
|
GPIO_RESET(gpio.port, gpio.pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 切换GPIO引脚状态
|
||||||
|
* @param {gpio_t} gpio - GPIO对象
|
||||||
|
* @note: 用于切换指定GPIO引脚的状态,即高电平变为低电平,低电平变为高电平。
|
||||||
|
*/
|
||||||
|
static void _toggle(gpio_t gpio)
|
||||||
|
{
|
||||||
|
GPIO_TOGGLE(gpio.port, gpio.pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取GPIO引脚状态
|
||||||
|
* @param {gpio_t} gpio - GPIO对象
|
||||||
|
* @return {*} - GPIO引脚当前状态,即0表示低电平,1表示高电平
|
||||||
|
* @note: 用于读取指定GPIO引脚的状态,即返回0或1。
|
||||||
|
*/
|
||||||
|
static uint8_t _read(gpio_t gpio)
|
||||||
|
{
|
||||||
|
return (uint8_t)GPIO_READ(gpio.port, gpio.pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建GPIO对象
|
||||||
|
* @param {GPIO_TypeDef} *port - GPIO寄存器指针
|
||||||
|
* @param {uint16_t} pin - 引脚号
|
||||||
|
* @return {gpio_t *} - 创建的GPIO对象指针
|
||||||
|
* @note: 用于创建一个GPIO对象,用于操作特定端口和引脚的GPIO功能。
|
||||||
|
*/
|
||||||
|
gpio_t *gpio_create(GPIO_TypeDef *port, uint16_t pin)
|
||||||
|
{
|
||||||
|
gpio_t *gpio = (gpio_t *)osel_mem_alloc(sizeof(gpio_t));
|
||||||
|
DBG_ASSERT(gpio != NULL __DBG_LINE);
|
||||||
|
gpio->port = port;
|
||||||
|
gpio->pin = pin;
|
||||||
|
gpio->set = _set;
|
||||||
|
gpio->reset = _reset;
|
||||||
|
gpio->toggle = _toggle;
|
||||||
|
gpio->read = _read;
|
||||||
|
return gpio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 释放GPIO对象
|
||||||
|
* @param {gpio_t} *gpio - GPIO对象指针
|
||||||
|
* @return {*}
|
||||||
|
* @note: 用于释放一个GPIO对象,释放后不能再使用该对象。
|
||||||
|
*/
|
||||||
|
void gpio_free(gpio_t *gpio)
|
||||||
|
{
|
||||||
|
if (gpio != NULL)
|
||||||
|
{
|
||||||
|
osel_mem_free(gpio);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* @file gpios.h
|
||||||
|
* @brief Header file for GPIO configuration and control.
|
||||||
|
*
|
||||||
|
* This file contains the declarations and definitions for GPIO configuration and control functions.
|
||||||
|
*
|
||||||
|
* @author xxx
|
||||||
|
* @date 2023-12-27 14:44:03
|
||||||
|
* @version 1.0
|
||||||
|
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GPIOS_H__
|
||||||
|
#define __GPIOS_H__
|
||||||
|
#include "lib.h"
|
||||||
|
#include "main.h"
|
||||||
|
/**
|
||||||
|
* @brief Set the GPIO pin to high.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
*/
|
||||||
|
#define GPIO_SET(port, pin) (LL_GPIO_SetOutputPin(port, pin))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the GPIO pin to low.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
*/
|
||||||
|
#define GPIO_RESET(port, pin) (LL_GPIO_ResetOutputPin(port, pin))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Toggle the state of the GPIO pin.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
*/
|
||||||
|
#define GPIO_TOGGLE(port, pin) (LL_GPIO_TogglePin(port, pin))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read the state of the GPIO pin.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
* @return The state of the GPIO pin (1 if high, 0 if low).
|
||||||
|
*/
|
||||||
|
#define GPIO_READ(port, pin) (LL_GPIO_IsInputPinSet(port, pin))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the GPIO pin as input.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
*/
|
||||||
|
#define GPIO_SET_INPUT(port, pin) (LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_INPUT))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the GPIO pin as output.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
*/
|
||||||
|
#define GPIO_SET_OUTPUT(port, pin) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_OUTPUT); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the GPIO pin as alternate function.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
*/
|
||||||
|
#define GPIO_SET_ALTERNATE(port, pin) (LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_ALTERNATE))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the GPIO pin as analog.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
*/
|
||||||
|
#define GPIO_SET_ANALOG(port, pin) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_ANALOG); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure representing a GPIO pin.
|
||||||
|
*/
|
||||||
|
typedef struct GPIO
|
||||||
|
{
|
||||||
|
GPIO_TypeDef *port; ///< The GPIO port.
|
||||||
|
uint16_t pin; ///< The GPIO pin.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the GPIO pin to high.
|
||||||
|
*
|
||||||
|
* @param gpio The GPIO pin.
|
||||||
|
*/
|
||||||
|
void (*set)(struct GPIO gpio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the GPIO pin to low.
|
||||||
|
*
|
||||||
|
* @param gpio The GPIO pin.
|
||||||
|
*/
|
||||||
|
void (*reset)(struct GPIO gpio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Toggle the state of the GPIO pin.
|
||||||
|
*
|
||||||
|
* @param gpio The GPIO pin.
|
||||||
|
*/
|
||||||
|
void (*toggle)(struct GPIO gpio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read the state of the GPIO pin.
|
||||||
|
*
|
||||||
|
* @param gpio The GPIO pin.
|
||||||
|
* @return The state of the GPIO pin (1 if high, 0 if low).
|
||||||
|
*/
|
||||||
|
uint8_t (*read)(struct GPIO gpio);
|
||||||
|
} gpio_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a GPIO pin.
|
||||||
|
*
|
||||||
|
* @param port The GPIO port.
|
||||||
|
* @param pin The GPIO pin.
|
||||||
|
* @return The created GPIO pin.
|
||||||
|
*/
|
||||||
|
extern gpio_t *gpio_create(GPIO_TypeDef *port, uint16_t pin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free the memory allocated for a GPIO pin.
|
||||||
|
*
|
||||||
|
* @param gpio The GPIO pin to free.
|
||||||
|
*/
|
||||||
|
extern void gpio_free(gpio_t *gpio);
|
||||||
|
|
||||||
|
#endif ///< __GPIOS_H__
|
|
@ -0,0 +1,669 @@
|
||||||
|
#include "i2cs.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
static inline void delay(i2c_t *handle); // 延时函数
|
||||||
|
static inline void _ack(i2c_t *handle); // 应答
|
||||||
|
static inline void _nack(i2c_t *handle); // 非应答
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 启动I2C总线
|
||||||
|
* @param {i2c_t} *handle - I2C总线句柄
|
||||||
|
* @note: 用于启动I2C总线的操作。在发送或接收数据之前,需要先启动总线。
|
||||||
|
*/
|
||||||
|
static void _start(i2c_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
// 获取gpios指针
|
||||||
|
i2c_gpio_group_t *gpios = &handle->gpios;
|
||||||
|
// 获取scl指针
|
||||||
|
gpio_t *scl = gpios->scl;
|
||||||
|
// 获取sda指针
|
||||||
|
gpio_t *sda = gpios->sda;
|
||||||
|
// 设置sda
|
||||||
|
gpios->sda->set(*sda);
|
||||||
|
// 设置scl
|
||||||
|
gpios->scl->set(*scl);
|
||||||
|
// 延时
|
||||||
|
delay(handle);
|
||||||
|
// 重置sda
|
||||||
|
gpios->sda->reset(*sda);
|
||||||
|
// 延时
|
||||||
|
delay(handle);
|
||||||
|
// 重置scl
|
||||||
|
gpios->scl->reset(*scl);
|
||||||
|
// 延时
|
||||||
|
delay(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 停止I2C总线
|
||||||
|
* @param {i2c_t} *handle - I2C总线句柄
|
||||||
|
* @note: 用于停止I2C总线的操作。在发送或接收数据之后,需要先停止总线。
|
||||||
|
*/
|
||||||
|
static void _stop(i2c_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
i2c_gpio_group_t *gpios = &handle->gpios;
|
||||||
|
gpio_t *scl = gpios->scl;
|
||||||
|
gpio_t *sda = gpios->sda;
|
||||||
|
gpios->scl->reset(*scl);
|
||||||
|
gpios->sda->reset(*sda);
|
||||||
|
delay(handle);
|
||||||
|
gpios->scl->set(*scl);
|
||||||
|
gpios->sda->set(*sda);
|
||||||
|
delay(handle);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief 等待应答信号
|
||||||
|
* @param {i2c_t} *handle - I2C总线句柄
|
||||||
|
* @return {BOOL} - 等待成功返回TRUE,否则返回FALSE
|
||||||
|
* @note: 用于等待I2C总线上发送的应答信号。
|
||||||
|
*/
|
||||||
|
static BOOL _wait_ack(i2c_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
uint8_t count = 0;
|
||||||
|
i2c_gpio_group_t *gpios = &handle->gpios;
|
||||||
|
gpio_t *scl = gpios->scl;
|
||||||
|
gpio_t *sda = gpios->sda;
|
||||||
|
gpios->sda->set(*sda);
|
||||||
|
gpios->scl->set(*scl);
|
||||||
|
delay(handle);
|
||||||
|
while (gpios->sda->read(*sda))
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if (count > 250)
|
||||||
|
{
|
||||||
|
_stop(handle);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gpios->scl->reset(*scl);
|
||||||
|
delay(handle);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取一个字节
|
||||||
|
* @param {i2c_t} *handle - I2C总线句柄
|
||||||
|
* @param {BOOL} ack - 应答信号标志
|
||||||
|
* @return {uint8_t} - 读取到的字节
|
||||||
|
* @note: 用于从I2C总线上读取一个字节。在读取一个字节后,需要发送应答信号。
|
||||||
|
*/
|
||||||
|
static uint8_t _read_byte(i2c_t *handle, BOOL ack)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
uint8_t i = 0, receive = 0;
|
||||||
|
i2c_gpio_group_t *gpios = &handle->gpios;
|
||||||
|
gpio_t *scl = gpios->scl;
|
||||||
|
gpio_t *sda = gpios->sda;
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
gpios->sda->set(*sda);
|
||||||
|
gpios->scl->set(*scl);
|
||||||
|
receive <<= 1;
|
||||||
|
delay(handle);
|
||||||
|
|
||||||
|
if (gpios->sda->read(*sda))
|
||||||
|
receive++;
|
||||||
|
|
||||||
|
gpios->scl->reset(*scl);
|
||||||
|
delay(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TRUE == ack)
|
||||||
|
{
|
||||||
|
_ack(handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_nack(handle);
|
||||||
|
}
|
||||||
|
return receive;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief 发送一个字节的数据到I2C总线上
|
||||||
|
* @param {i2c_t} *handle I2C总线的句柄
|
||||||
|
* @param {uint8_t} data 要发送的字节数据
|
||||||
|
* @return {*} 无
|
||||||
|
* @note: 该函数用于在I2C总线上发送一个字节的数据。它首先定义了一个循环,用于遍历要发送的字节中的每一位。在循环中,首先检查当前位是否为1,如果是,则设置SDA为1,否则设置SDA为0。然后设置SCL为1,延时1ms。接着将数据右移一位,然后设置SCL为0,延时1ms。当位遍历完后,最后设置SDA为1,以确保正确的结束传输。
|
||||||
|
*/
|
||||||
|
static void _write_byte(i2c_t *handle, uint8_t data)
|
||||||
|
{
|
||||||
|
// 定义变量i
|
||||||
|
uint8_t i = 0;
|
||||||
|
// 断言参数handle不为空
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
// 定义变量gpios
|
||||||
|
i2c_gpio_group_t *gpios = &handle->gpios;
|
||||||
|
// 定义变量scl
|
||||||
|
gpio_t *scl = gpios->scl;
|
||||||
|
// 定义变量sda
|
||||||
|
gpio_t *sda = gpios->sda;
|
||||||
|
// 遍历每一位
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
// 如果data的最低位为1
|
||||||
|
if (data & 0x80)
|
||||||
|
{
|
||||||
|
// 设置sda的状态为1
|
||||||
|
gpios->sda->set(*sda);
|
||||||
|
}
|
||||||
|
// 否则,设置sda的状态为0
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 设置sda的状态为0
|
||||||
|
gpios->sda->reset(*sda);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置scl的状态为1
|
||||||
|
gpios->scl->set(*scl);
|
||||||
|
// 延时1ms
|
||||||
|
delay(handle);
|
||||||
|
// 将data右移1位
|
||||||
|
data <<= 1;
|
||||||
|
// 设置scl的状态为0
|
||||||
|
gpios->scl->reset(*scl);
|
||||||
|
// 延时1ms
|
||||||
|
delay(handle);
|
||||||
|
|
||||||
|
// 如果i等于7
|
||||||
|
if (i == 7)
|
||||||
|
{
|
||||||
|
// 设置sda的状态为1
|
||||||
|
gpios->sda->set(*sda);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 发送一个字节的数据到I2C总线上
|
||||||
|
* @param {i2c_t} *handle I2C总线的句柄
|
||||||
|
* @param {uint16_t} data 要发送的字节数据
|
||||||
|
* @return {*} 无
|
||||||
|
* @note: 该函数用于在I2C总线上发送一个字节的数据。它首先定义了一个循环,用于遍历要发送的字节中的每一位。在循环中,首先检查当前位是否为1,如果是,则设置SDA为1,否则设置SDA为0。然后设置SCL为1,延时1ms。接着将数据右移一位,然后设置SCL为0,延时1ms。当位遍历完后,最后设置SDA为1,以确保正确的结束传输。
|
||||||
|
*/
|
||||||
|
static void _write_word(i2c_t *handle, uint16_t data)
|
||||||
|
{
|
||||||
|
// 循环写入2个字节
|
||||||
|
for (uint8_t i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
// 将data的第i个字节写入i2c接口
|
||||||
|
_write_byte(handle, (uint8_t)(data >> (8 * i)));
|
||||||
|
// 等待ACK
|
||||||
|
_wait_ack(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建一个I2C总线设备
|
||||||
|
* @param {i2c_gpio_group_t} gpios I2C总线的GPIO配置
|
||||||
|
* @param {uint16_t} delay_ticks I2C总线的延时参数
|
||||||
|
* @return {i2c_t *} 创建的I2C总线设备句柄
|
||||||
|
* @note: 该函数用于创建一个I2C总线设备。它首先创建一个i2c_t结构体,并将gpios和delay_ticks的内存地址复制到handle结构体中。然后,它为handle结构体定义了start、stop、wait_ack、read_byte、write_byte和write_word函数。最后,它返回handle结构体。
|
||||||
|
*/
|
||||||
|
i2c_t *i2c_create(i2c_gpio_group_t gpios, uint16_t delay_ticks)
|
||||||
|
{
|
||||||
|
// 创建一个i2c_t结构体
|
||||||
|
i2c_t *handle = (i2c_t *)osel_mem_alloc(sizeof(i2c_t));
|
||||||
|
// 将gpios的内存地址复制到handle结构体中
|
||||||
|
osel_memcpy((uint8_t *)&handle->gpios, (uint8_t *)&gpios, sizeof(i2c_gpio_group_t));
|
||||||
|
// 将delay_ticks的内存地址复制到handle结构体中
|
||||||
|
handle->delay_ticks = delay_ticks;
|
||||||
|
|
||||||
|
// 创建一个start函数
|
||||||
|
handle->interface.start = _start;
|
||||||
|
// 创建一个stop函数
|
||||||
|
handle->interface.stop = _stop;
|
||||||
|
// 创建一个wait_ack函数
|
||||||
|
handle->interface.wait_ack = _wait_ack;
|
||||||
|
// 创建一个read_byte函数
|
||||||
|
handle->interface.read_byte = _read_byte;
|
||||||
|
// 创建一个write_byte函数
|
||||||
|
handle->interface.write_byte = _write_byte;
|
||||||
|
// 创建一个write_word函数
|
||||||
|
handle->interface.write_word = _write_word;
|
||||||
|
|
||||||
|
handle->dead_count = 0;
|
||||||
|
// 返回handle结构体
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置I2C器件地址
|
||||||
|
* @param {i2c_t} *handle
|
||||||
|
* @param {uint8_t} w_address 写地址
|
||||||
|
* @param {uint8_t} r_address 读地址
|
||||||
|
* @return {*}
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
void i2c_dma_set_address(i2c_t *handle, uint8_t w_address, uint8_t r_address)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->w_address = w_address;
|
||||||
|
handle->r_address = r_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 释放I2C总线设备
|
||||||
|
* @param {i2c_t} *handle 需要释放的I2C总线设备句柄
|
||||||
|
* @return {*} 无
|
||||||
|
* @note: 该函数用于释放一个I2C总线设备。它首先检查handle是否为空,如果不为空,则释放所有的GPIO。最后,释放handle的内存。
|
||||||
|
*/
|
||||||
|
void i2c_free(i2c_t *handle)
|
||||||
|
{
|
||||||
|
// 如果handle不为空,则释放所有的gpio
|
||||||
|
if (NULL != handle)
|
||||||
|
{
|
||||||
|
gpio_free(handle->gpios.scl);
|
||||||
|
gpio_free(handle->gpios.sda);
|
||||||
|
osel_mem_free(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2C DMA TX回调函数
|
||||||
|
* @param {i2c_t} handle
|
||||||
|
* @return {*}
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
void i2c_dma_callback(i2c_t *handle)
|
||||||
|
{
|
||||||
|
// 检查输入参数是否为空
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
// 清除传输完成标志位
|
||||||
|
if (handle->dma_tx_cb != NULL)
|
||||||
|
{
|
||||||
|
handle->dma_tx_cb(handle);
|
||||||
|
}
|
||||||
|
if (handle->dma_rx_cb != NULL)
|
||||||
|
{
|
||||||
|
handle->dma_rx_cb(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(STM32L4xx_LL_I2C_H)
|
||||||
|
static void i2c_reset(i2c_t *handle);
|
||||||
|
static BOOL _read_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size);
|
||||||
|
static BOOL _write_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size);
|
||||||
|
/**
|
||||||
|
* @brief 创建一个I2C总线设备 DMA
|
||||||
|
* @param {I2C_TypeDef} *i2c
|
||||||
|
* @param {DMA_TypeDef} *dma
|
||||||
|
* @param {uint16_t} rxsize
|
||||||
|
* @param {uint32_t} dma_rx_channel
|
||||||
|
* @param {uint16_t} txsize
|
||||||
|
* @param {uint32_t} dma_tx_channel
|
||||||
|
* @return {*}
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
i2c_t *i2c_create_dma(I2C_TypeDef *i2c, DMA_TypeDef *dma, uint16_t rxsize, uint32_t dma_rx_channel,
|
||||||
|
i2cs_dma_callback *dma_rx_cb, uint16_t txsize, uint32_t dma_tx_channel, i2cs_dma_callback *dma_tx_cb)
|
||||||
|
{
|
||||||
|
i2c_t *handle = (i2c_t *)osel_mem_alloc(sizeof(i2c_t));
|
||||||
|
handle->i2c = i2c;
|
||||||
|
handle->dma = dma;
|
||||||
|
handle->dma_rx_channel = dma_rx_channel;
|
||||||
|
handle->dma_tx_channel = dma_tx_channel;
|
||||||
|
handle->rxbuf = (uint8_t *)osel_mem_alloc(rxsize);
|
||||||
|
handle->txbuf = (uint8_t *)osel_mem_alloc(txsize);
|
||||||
|
handle->rxsize = rxsize;
|
||||||
|
handle->txsize = txsize;
|
||||||
|
handle->tx_dma_ok = TRUE;
|
||||||
|
handle->interface.write_mem_dma = _write_mem_dma;
|
||||||
|
handle->interface.read_mem_dma = _read_mem_dma;
|
||||||
|
if (dma_rx_cb != NULL)
|
||||||
|
{
|
||||||
|
handle->dma_rx_cb = dma_rx_cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dma_tx_cb != NULL)
|
||||||
|
{
|
||||||
|
handle->dma_tx_cb = dma_tx_cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_DMA_DisableChannel(dma, dma_tx_channel);
|
||||||
|
LL_DMA_DisableChannel(dma, dma_rx_channel);
|
||||||
|
|
||||||
|
// TX
|
||||||
|
uint8_t *pTransmitBuffer = handle->txbuf;
|
||||||
|
LL_DMA_ConfigTransfer(dma, dma_tx_channel, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE);
|
||||||
|
LL_DMA_ConfigAddresses(dma, dma_tx_channel, (uint32_t)pTransmitBuffer, (uint32_t)LL_I2C_DMA_GetRegAddr(i2c, LL_I2C_DMA_REG_DATA_TRANSMIT), LL_DMA_GetDataTransferDirection(dma, dma_tx_channel));
|
||||||
|
LL_DMA_SetPeriphRequest(dma, dma_tx_channel, LL_DMA_REQUEST_3);
|
||||||
|
|
||||||
|
// RX
|
||||||
|
uint8_t *pReceiveBuffer = handle->rxbuf;
|
||||||
|
LL_DMA_ConfigTransfer(dma, dma_rx_channel, LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE);
|
||||||
|
LL_DMA_ConfigAddresses(dma, dma_rx_channel, (uint32_t)LL_I2C_DMA_GetRegAddr(i2c, LL_I2C_DMA_REG_DATA_RECEIVE), (uint32_t)pReceiveBuffer, LL_DMA_GetDataTransferDirection(dma, dma_rx_channel));
|
||||||
|
LL_DMA_SetPeriphRequest(dma, dma_rx_channel, LL_DMA_REQUEST_3);
|
||||||
|
|
||||||
|
LL_DMA_EnableIT_TC(dma, dma_tx_channel);
|
||||||
|
LL_DMA_EnableIT_TE(dma, dma_tx_channel);
|
||||||
|
LL_DMA_EnableIT_TC(dma, dma_rx_channel);
|
||||||
|
LL_DMA_EnableIT_TE(dma, dma_rx_channel);
|
||||||
|
|
||||||
|
LL_I2C_EnableDMAReq_TX(i2c);
|
||||||
|
LL_I2C_EnableDMAReq_RX(i2c);
|
||||||
|
LL_I2C_Enable(i2c);
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 非阻塞模式下使用DMA从特定内存地址读取数据
|
||||||
|
* @param {i2c_t} *handle 指向一个i2c_t结构体,其中包含指定I2C的配置信息
|
||||||
|
* @param {uint16_t} dev_address 目标设备地址:在数据手册中,设备7位地址值需要向左移动以调用接口
|
||||||
|
* @param {uint16_t} mem_address 内部内存地址
|
||||||
|
* @param {uint16_t} mem_addsize 内部内存地址大小
|
||||||
|
* @param {uint8_t} *data 数据缓冲区的指针
|
||||||
|
* @param {uint16_t} size 要发送的数据量
|
||||||
|
* @return {*}
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
static BOOL _read_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(data != NULL __DBG_LINE);
|
||||||
|
if (LL_I2C_IsActiveFlag_BUSY(handle->i2c) == 1)
|
||||||
|
{
|
||||||
|
i2c_reset(handle); // xsh:重置I2C,修复一段时间后无法读写的问题
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
uint16_t count = 2000;
|
||||||
|
handle->txsize = 0;
|
||||||
|
handle->tx_dma_ok = FALSE;
|
||||||
|
handle->rx_dma_ok = FALSE;
|
||||||
|
|
||||||
|
for (uint8_t i = mem_addsize; i > 0; i--)
|
||||||
|
{
|
||||||
|
handle->txbuf[handle->txsize++] = (uint8_t)(mem_address >> (8 * (i - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, handle->txsize);
|
||||||
|
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
|
||||||
|
|
||||||
|
LL_I2C_HandleTransfer(handle->i2c, handle->w_address, LL_I2C_ADDRSLAVE_7BIT, handle->txsize, LL_I2C_MODE_SOFTEND, LL_I2C_GENERATE_START_WRITE);
|
||||||
|
count = 2000;
|
||||||
|
while (!handle->tx_dma_ok)
|
||||||
|
{
|
||||||
|
if (count-- == 0)
|
||||||
|
{
|
||||||
|
handle->tx_dma_ok = TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 2000;
|
||||||
|
while (LL_I2C_IsActiveFlag_TC(handle->i2c) != 1)
|
||||||
|
{
|
||||||
|
if (count-- == 0)
|
||||||
|
{
|
||||||
|
handle->tx_dma_ok = TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle->tx_dma_ok = FALSE;
|
||||||
|
handle->rx_dma_ok = FALSE;
|
||||||
|
handle->rxsize = size;
|
||||||
|
osel_memset(handle->rxbuf, 0, handle->rxsize);
|
||||||
|
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
|
||||||
|
LL_DMA_SetDataLength(handle->dma, handle->dma_rx_channel, handle->rxsize);
|
||||||
|
LL_DMA_EnableChannel(handle->dma, handle->dma_rx_channel);
|
||||||
|
LL_I2C_HandleTransfer(handle->i2c, handle->r_address, LL_I2C_ADDRSLAVE_7BIT, handle->rxsize, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_RESTART_7BIT_READ);
|
||||||
|
|
||||||
|
count = 2000;
|
||||||
|
while (!LL_I2C_IsActiveFlag_STOP(handle->i2c))
|
||||||
|
{
|
||||||
|
if (count-- == 0)
|
||||||
|
{
|
||||||
|
handle->tx_dma_ok = TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_DMA_DisableChannel(handle->dma, handle->dma_rx_channel);
|
||||||
|
LL_I2C_ClearFlag_STOP(handle->i2c);
|
||||||
|
osel_memcpy(data, handle->rxbuf, handle->rxsize);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 非阻塞模式下使用DMA将数据写入特定内存地址
|
||||||
|
* @param {i2c_t} *handle 指向一个i2c_t结构体,其中包含指定I2C的配置信息
|
||||||
|
* @param {uint16_t} dev_address 目标设备地址:在数据手册中,设备7位地址值需要向左移动以调用接口
|
||||||
|
* @param {uint16_t} mem_address 内部内存地址
|
||||||
|
* @param {uint16_t} mem_addsize 内部内存地址大小
|
||||||
|
* @param {uint8_t} *data 数据缓冲区的指针
|
||||||
|
* @param {uint16_t} size 要发送的数据量
|
||||||
|
* @return {*}
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
static BOOL _write_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(data != NULL __DBG_LINE);
|
||||||
|
uint16_t count = 2000;
|
||||||
|
if (LL_I2C_IsActiveFlag_BUSY(handle->i2c) == 1)
|
||||||
|
{
|
||||||
|
i2c_reset(handle); // xsh:重置I2C,修复一段时间后无法读写的问题
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle->txsize = 0;
|
||||||
|
handle->tx_dma_ok = FALSE;
|
||||||
|
for (uint8_t i = mem_addsize; i > 0; i--)
|
||||||
|
{
|
||||||
|
handle->txbuf[handle->txsize++] = (uint8_t)(mem_address >> (8 * (i - 1)));
|
||||||
|
}
|
||||||
|
osel_memcpy(&handle->txbuf[handle->txsize], data, size);
|
||||||
|
handle->txsize += size;
|
||||||
|
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, handle->txsize);
|
||||||
|
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
|
||||||
|
|
||||||
|
LL_I2C_HandleTransfer(handle->i2c, handle->w_address, LL_I2C_ADDRSLAVE_7BIT, handle->txsize, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_WRITE);
|
||||||
|
|
||||||
|
count = 2000;
|
||||||
|
while (!handle->tx_dma_ok)
|
||||||
|
{
|
||||||
|
if (count-- == 0)
|
||||||
|
{
|
||||||
|
handle->tx_dma_ok = TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count = 2000;
|
||||||
|
while (!LL_I2C_IsActiveFlag_STOP(handle->i2c))
|
||||||
|
{
|
||||||
|
if (count-- == 0)
|
||||||
|
{
|
||||||
|
handle->tx_dma_ok = TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
|
||||||
|
LL_I2C_ClearFlag_STOP(handle->i2c);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2C重置
|
||||||
|
* @param {I2C_TypeDef} *I2Cx
|
||||||
|
* @return {*}
|
||||||
|
* @note 解决I2C总线死锁问题
|
||||||
|
*/
|
||||||
|
static void i2c_reset(i2c_t *handle)
|
||||||
|
{
|
||||||
|
LL_I2C_Disable(handle->i2c);
|
||||||
|
LL_I2C_Enable(handle->i2c);
|
||||||
|
handle->dead_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2C 中断回调函数
|
||||||
|
* @param {i2c_t} *handle
|
||||||
|
* @return {*}
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
void i2c_ev_callback(i2c_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
if (LL_I2C_IsActiveFlag_ADDR(handle->i2c))
|
||||||
|
{
|
||||||
|
/* Verify the Address Match with the OWN Slave address */
|
||||||
|
if (LL_I2C_GetAddressMatchCode(handle->i2c) == handle->w_address || LL_I2C_GetAddressMatchCode(handle->i2c) == handle->r_address)
|
||||||
|
{
|
||||||
|
/* Verify the transfer direction, a write direction, Slave enters receiver mode */
|
||||||
|
if (LL_I2C_GetTransferDirection(handle->i2c) == LL_I2C_DIRECTION_WRITE)
|
||||||
|
{
|
||||||
|
/* Clear ADDR flag value in ISR register */
|
||||||
|
LL_I2C_ClearFlag_ADDR(handle->i2c);
|
||||||
|
|
||||||
|
/* Enable Receive Interrupt */
|
||||||
|
LL_I2C_EnableIT_RX(handle->i2c);
|
||||||
|
}
|
||||||
|
else if (LL_I2C_GetTransferDirection(handle->i2c) == LL_I2C_DIRECTION_READ)
|
||||||
|
{
|
||||||
|
/* Clear ADDR flag value in ISR register */
|
||||||
|
LL_I2C_ClearFlag_ADDR(handle->i2c);
|
||||||
|
|
||||||
|
/* Enable Transmit Interrupt */
|
||||||
|
LL_I2C_EnableIT_TX(handle->i2c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Clear ADDR flag value in ISR register */
|
||||||
|
LL_I2C_ClearFlag_ADDR(handle->i2c);
|
||||||
|
|
||||||
|
/* Call Error function */
|
||||||
|
DBG_ASSERT(FALSE __DBG_LINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Check NACK flag value in ISR register */
|
||||||
|
else if (LL_I2C_IsActiveFlag_NACK(handle->i2c))
|
||||||
|
{
|
||||||
|
/* End of Transfer */
|
||||||
|
LL_I2C_ClearFlag_NACK(handle->i2c);
|
||||||
|
}
|
||||||
|
/* Check RXNE flag value in ISR register */
|
||||||
|
else if (LL_I2C_IsActiveFlag_RXNE(handle->i2c))
|
||||||
|
{
|
||||||
|
/* Call function Slave Reception Callback */
|
||||||
|
}
|
||||||
|
/* Check TXIS flag value in ISR register */
|
||||||
|
else if (LL_I2C_IsActiveFlag_TXIS(handle->i2c))
|
||||||
|
{
|
||||||
|
/* Call function Slave Ready to Transmit Callback */
|
||||||
|
}
|
||||||
|
/* Check STOP flag value in ISR register */
|
||||||
|
else if (LL_I2C_IsActiveFlag_STOP(handle->i2c))
|
||||||
|
{
|
||||||
|
/* End of Transfer */
|
||||||
|
LL_I2C_ClearFlag_STOP(handle->i2c);
|
||||||
|
|
||||||
|
/* Check TXE flag value in ISR register */
|
||||||
|
if (!LL_I2C_IsActiveFlag_TXE(handle->i2c))
|
||||||
|
{
|
||||||
|
/* Flush TX buffer */
|
||||||
|
LL_I2C_ClearFlag_TXE(handle->i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call function Slave Complete Callback */
|
||||||
|
}
|
||||||
|
/* Check TXE flag value in ISR register */
|
||||||
|
else if (!LL_I2C_IsActiveFlag_TXE(handle->i2c))
|
||||||
|
{
|
||||||
|
/* Do nothing */
|
||||||
|
/* This Flag will be set by hardware when the TXDR register is empty */
|
||||||
|
/* If needed, use LL_I2C_ClearFlag_TXE() interface to flush the TXDR register */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Call Error function */
|
||||||
|
DBG_ASSERT(FALSE __DBG_LINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*下面是内部实现方法*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 此方法是一个简单的延迟函数,它使用循环来执行指定数量的NOP(无操作)指令。这个延迟函数的目的是在程序的执行中引入延迟,这在需要精确定时的某些应用中很有用。延迟时间由输入参数“count”的值决定,该参数指定要执行的NOP指令的数量。延迟时间通常以微秒或毫秒为单位测量,具体取决于使用延迟的环境。
|
||||||
|
* @param {uint16_t} count NOP指令的数量
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void delay(i2c_t *handle)
|
||||||
|
{
|
||||||
|
// 断言参数handle不为空
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
// 定义循环计数变量count
|
||||||
|
uint16_t count = 0;
|
||||||
|
// 设置循环计数变量count的值为handle->delay_ticks
|
||||||
|
count = handle->delay_ticks;
|
||||||
|
// 循环计数变量count的值,直到count的值为0
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
// 每次循环调用__NOP()函数
|
||||||
|
__NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 发送ACK信号
|
||||||
|
* @param {i2c_t} *handle I2C总线的句柄
|
||||||
|
* @return {*} 无
|
||||||
|
* @note: 该函数用于在I2C总线上发送ACK信号。它首先断言handle不为空,然后获取gpios和scl、sda的指针。接着重置sda,延时1ms,设置scl为1,延时1ms,重置scl,确保正确的结束传输。
|
||||||
|
*/
|
||||||
|
static inline void _ack(i2c_t *handle)
|
||||||
|
{
|
||||||
|
// 断言handle不为空
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
// 获取gpios指针
|
||||||
|
i2c_gpio_group_t *gpios = &handle->gpios;
|
||||||
|
// 获取scl指针
|
||||||
|
gpio_t *scl = gpios->scl;
|
||||||
|
// 获取sda指针
|
||||||
|
gpio_t *sda = gpios->sda;
|
||||||
|
|
||||||
|
// 重置sda
|
||||||
|
gpios->sda->reset(*sda);
|
||||||
|
// 延时
|
||||||
|
delay(handle);
|
||||||
|
// 设置scl
|
||||||
|
gpios->scl->set(*scl);
|
||||||
|
// 延时
|
||||||
|
delay(handle);
|
||||||
|
// 重置scl
|
||||||
|
gpios->scl->reset(*scl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 发送NACK信号
|
||||||
|
* @param {i2c_t} *handle I2C总线的句柄
|
||||||
|
* @return {*} 无
|
||||||
|
* @note: 该函数用于在I2C总线上发送NACK信号。它首先断言handle不为空,然后获取gpios和scl、sda的指针。接着设置sda为1,延时1ms,设置scl为1,延时1ms,重置scl,确保正确的结束传输。
|
||||||
|
*/
|
||||||
|
static inline void _nack(i2c_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
// 获取gpios指针
|
||||||
|
i2c_gpio_group_t *gpios = &handle->gpios;
|
||||||
|
// 获取scl指针
|
||||||
|
gpio_t *scl = gpios->scl;
|
||||||
|
// 获取sda指针
|
||||||
|
gpio_t *sda = gpios->sda;
|
||||||
|
|
||||||
|
// 设置sda引脚
|
||||||
|
gpios->sda->set(*sda);
|
||||||
|
// 等待延时
|
||||||
|
delay(handle);
|
||||||
|
// 设置scl引脚
|
||||||
|
gpios->scl->set(*scl);
|
||||||
|
// 等待延时
|
||||||
|
delay(handle);
|
||||||
|
// 重置scl引脚
|
||||||
|
gpios->scl->reset(*scl);
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
/**
|
||||||
|
* @file i2cs.h
|
||||||
|
* @brief Header file for I2C Slave module.
|
||||||
|
*
|
||||||
|
* This file contains the declarations and definitions for the I2C Slave module.
|
||||||
|
* It provides functions to initialize and configure the I2C peripheral as a slave,
|
||||||
|
* as well as functions to send and receive data over the I2C bus.
|
||||||
|
*
|
||||||
|
* @author xxx
|
||||||
|
* @date 2023-12-27 14:44:03
|
||||||
|
* @version 1.0
|
||||||
|
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __I2CS_H__
|
||||||
|
#define __I2CS_H__
|
||||||
|
#include "lib.h"
|
||||||
|
#include "gpios.h"
|
||||||
|
/**
|
||||||
|
* @file i2cs.h
|
||||||
|
* @brief Header file containing the definition of the I2C slave (I2CS) structure and related functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct I2CS i2c_t;
|
||||||
|
typedef void i2cs_dma_callback(i2c_t *handle);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
void (*start)(i2c_t *handle); ///< Function pointer to start the I2C communication.
|
||||||
|
void (*stop)(i2c_t *handle); ///< Function pointer to stop the I2C communication.
|
||||||
|
BOOL(*wait_ack)
|
||||||
|
(i2c_t *handle); ///< Function pointer to wait for the acknowledgment from the I2C bus.
|
||||||
|
|
||||||
|
void (*write_byte)(i2c_t *handle, uint8_t data); ///< Function pointer to write a byte of data to the I2C bus.
|
||||||
|
uint8_t (*read_byte)(i2c_t *handle, BOOL ack); ///< Function pointer to read a byte of data from the I2C bus.
|
||||||
|
|
||||||
|
void (*write_word)(i2c_t *handle, uint16_t data); ///< Function pointer to write two bytes of data to the I2C bus.
|
||||||
|
|
||||||
|
BOOL(*write_mem_dma)
|
||||||
|
(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size); ///< Function pointer to write multiple bytes of data to a memory address using DMA.
|
||||||
|
BOOL(*read_mem_dma)
|
||||||
|
(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size); ///< Function pointer to read multiple bytes of data from a memory address using DMA.
|
||||||
|
} i2c_interface_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
struct GPIO *scl; ///< Pointer to the GPIO pin used for the I2C clock (SCL).
|
||||||
|
struct GPIO *sda; ///< Pointer to the GPIO pin used for the I2C data (SDA).
|
||||||
|
} i2c_gpio_group_t;
|
||||||
|
|
||||||
|
struct I2CS
|
||||||
|
{
|
||||||
|
///< Analog part definition
|
||||||
|
i2c_gpio_group_t gpios; ///< Structure containing the GPIO pins used for the I2C communication.
|
||||||
|
uint16_t delay_ticks; ///< Number of NOP instructions to delay the I2C communication.
|
||||||
|
|
||||||
|
///< Hardware part definition
|
||||||
|
I2C_TypeDef *i2c; ///< Pointer to the I2C peripheral.
|
||||||
|
DMA_TypeDef *dma; ///< Pointer to the DMA peripheral.
|
||||||
|
uint32_t dma_rx_channel; ///< DMA channel used for receiving data.
|
||||||
|
uint32_t dma_tx_channel; ///< DMA channel used for transmitting data.
|
||||||
|
uint8_t *rxbuf; ///< Pointer to the receive buffer.
|
||||||
|
uint16_t rxsize; ///< Size of the receive buffer.
|
||||||
|
uint8_t *txbuf; ///< Pointer to the transmit buffer.
|
||||||
|
uint16_t txsize; ///< Size of the transmit buffer.
|
||||||
|
uint8_t w_address; ///< 7-bit write address.
|
||||||
|
uint8_t r_address; ///< 7-bit read address.
|
||||||
|
__IO BOOL rx_dma_ok; ///< Flag indicating the completion of receive DMA.
|
||||||
|
__IO BOOL tx_dma_ok; ///< Flag indicating the completion of transmit DMA.
|
||||||
|
|
||||||
|
i2cs_dma_callback *dma_rx_cb; ///< Callback function called when receive DMA is completed.
|
||||||
|
i2cs_dma_callback *dma_tx_cb; ///< Callback function called when transmit DMA is completed.
|
||||||
|
|
||||||
|
i2c_interface_t interface; ///< Structure containing the function pointers for the I2C interface.
|
||||||
|
uint16_t dead_count; ///< Counter for the number of deadlocks.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates an I2C slave instance with GPIO pins for clock and data.
|
||||||
|
* @param gpios The GPIO pins used for the I2C communication.
|
||||||
|
* @param delay_ticks The number of NOP instructions to delay the I2C communication.
|
||||||
|
* @return A pointer to the created I2C slave instance.
|
||||||
|
*/
|
||||||
|
extern i2c_t *i2c_create(i2c_gpio_group_t gpios, uint16_t delay_ticks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates an I2C slave instance with DMA support.
|
||||||
|
* @param i2c Pointer to the I2C peripheral.
|
||||||
|
* @param dma Pointer to the DMA peripheral.
|
||||||
|
* @param rxsize Size of the receive buffer.
|
||||||
|
* @param dma_rx_channel DMA channel used for receiving data.
|
||||||
|
* @param dma_rx_cb Callback function called when receive DMA is completed.
|
||||||
|
* @param txsize Size of the transmit buffer.
|
||||||
|
* @param dma_tx_channel DMA channel used for transmitting data.
|
||||||
|
* @param dma_tx_cb Callback function called when transmit DMA is completed.
|
||||||
|
* @return A pointer to the created I2C slave instance.
|
||||||
|
*/
|
||||||
|
extern i2c_t *i2c_create_dma(I2C_TypeDef *i2c, DMA_TypeDef *dma, uint16_t rxsize, uint32_t dma_rx_channel,
|
||||||
|
i2cs_dma_callback *dma_rx_cb, uint16_t txsize, uint32_t dma_tx_channel, i2cs_dma_callback *dma_tx_cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the write and read addresses for the I2C slave instance with DMA support.
|
||||||
|
* @param handle Pointer to the I2C slave instance.
|
||||||
|
* @param w_address 7-bit write address.
|
||||||
|
* @param r_address 7-bit read address.
|
||||||
|
*/
|
||||||
|
extern void i2c_dma_set_address(i2c_t *handle, uint8_t w_address, uint8_t r_address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees the resources used by the I2C slave instance.
|
||||||
|
* @param handle Pointer to the I2C slave instance.
|
||||||
|
*/
|
||||||
|
extern void i2c_free(i2c_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function called when an I2C event occurs.
|
||||||
|
* @param handle Pointer to the I2C slave instance.
|
||||||
|
*/
|
||||||
|
extern void i2c_ev_callback(i2c_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function called when an I2C DMA event occurs.
|
||||||
|
* @param handle Pointer to the I2C slave instance.
|
||||||
|
*/
|
||||||
|
extern void i2c_dma_callback(i2c_t *handle);
|
||||||
|
|
||||||
|
#endif ///< __I2CS_H__
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,811 @@
|
||||||
|
#include "spis.h"
|
||||||
|
#include "delay.h"
|
||||||
|
|
||||||
|
#define SPI_TIMEOUT 2000
|
||||||
|
|
||||||
|
#define CMD_RDSR 0x05 /*!< Read Status Register instruction */
|
||||||
|
#define CMD_WRSR 0x01 /*!< Write Status Register instruction */
|
||||||
|
#define CMD_WREN 0x06 /*!< Write enable instruction */
|
||||||
|
#define CMD_WRDI 0x04 /*!< Write disable instruction */
|
||||||
|
#define CMD_READ 0x03 /*!< Read from Memory instruction */
|
||||||
|
#define CMD_WRITE 0x02 /*!< Write to Memory instruction */
|
||||||
|
#define DUMMY_BYTE 0xA5 ///< 虚拟字节
|
||||||
|
|
||||||
|
static inline void spi_delay(spi_t *handle); // 延时函数
|
||||||
|
|
||||||
|
static inline void spi_rdy_high(spi_t *handle); // RDY高电平
|
||||||
|
static inline void spi_rdy_low(spi_t *handle); // RDY低电平
|
||||||
|
static inline void spi_cs_high(spi_t *handle); // CS高电平
|
||||||
|
static inline void spi_cs_low(spi_t *handle); // CS低电平
|
||||||
|
static inline void spi_mosi_high(spi_t *handle); // MOSI高电平
|
||||||
|
static inline void spi_mosi_low(spi_t *handle); // MOSI低电平
|
||||||
|
static inline void spi_sck_high(spi_t *handle); // SCK高电平
|
||||||
|
static inline void spi_sck_low(spi_t *handle); // SCK低电平
|
||||||
|
static inline uint8_t spi_miso_read(spi_t *handle); // 读取MISO电平
|
||||||
|
|
||||||
|
static uint8_t spi_read_write_byte(spi_t *handle, uint8_t tx_data); // 读写一个字节
|
||||||
|
static void spi_reset(spi_t *handle); // 复位
|
||||||
|
static BOOL spi_write(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); // 写数据
|
||||||
|
static BOOL spi_read(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); // 读数据
|
||||||
|
static void spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data); // 写寄存器
|
||||||
|
static uint8_t spi_read_reg(spi_t *handle, uint8_t reg); // 读寄存器
|
||||||
|
|
||||||
|
static void _hardware_enable(spi_t *handle, SPI_TypeDef *spi); // 硬件SPI
|
||||||
|
static void _dma_enable(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb,
|
||||||
|
uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb); // DMA SPI
|
||||||
|
static void _spi_dma_callback(spi_t *handle); // DMA发送完成回调
|
||||||
|
static BOOL _spi_dma_send(spi_t *handle, uint8_t *data, uint16_t length); // DMA发送数据
|
||||||
|
static uint8_t _read_drdy(spi_t *handle); // 读取DRDY电平
|
||||||
|
static uint8_t _write_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); // 写多个寄存器
|
||||||
|
static uint8_t _read_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); // 读多个寄存器
|
||||||
|
static uint8_t _spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data); // 写单个寄存器
|
||||||
|
static uint8_t _spi_read_reg(spi_t *handle, uint8_t reg); // 读单个寄存器
|
||||||
|
static uint8_t _spi_write_cmd(spi_t *handle, uint8_t cmd); // 写命令
|
||||||
|
static uint8_t _spi_write_data(spi_t *handle, uint8_t *data, uint16_t len); // 写数据
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建一个SPI总线设备
|
||||||
|
* @param {spi_type_e} spi_type SPI总线的类型
|
||||||
|
* @param {spi_gpio_group_t} gpios SPI总线的GPIO配置
|
||||||
|
* @param {uint16_t} delay_ticks SPI总线的延时参数
|
||||||
|
* @return {*} 创建的SPI总线设备句柄
|
||||||
|
* @note: 该函数用于创建一个SPI总线设备。它首先断言spi_type在有效的范围内,然后创建一个spi_t结构体,并将gpios和delay_ticks的内存地址复制到handle结构体中。接着,根据spi_type的值,设置不同的接口函数。最后,返回handle结构体。
|
||||||
|
*/
|
||||||
|
spi_t *spi_create(spi_type_e spi_type, spi_gpio_group_t gpios, uint16_t delay_ticks)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(spi_type < SPI_TYPE_MAX __DBG_LINE);
|
||||||
|
spi_t *handle = (spi_t *)osel_mem_alloc(sizeof(spi_t));
|
||||||
|
osel_memcpy((uint8_t *)&handle->gpios, (uint8_t *)&gpios, sizeof(spi_gpio_group_t));
|
||||||
|
handle->delay_ticks = delay_ticks;
|
||||||
|
handle->spi_type = spi_type;
|
||||||
|
handle->simualte_gpio = TRUE;
|
||||||
|
handle->interface.hardware_enable = _hardware_enable;
|
||||||
|
handle->interface.dma_enable = _dma_enable;
|
||||||
|
handle->interface.spi_dma_send = _spi_dma_send;
|
||||||
|
handle->interface.spi_dma_callback = _spi_dma_callback;
|
||||||
|
if (spi_type == SPI_TYPE_NORMAL)
|
||||||
|
{
|
||||||
|
handle->interface.u.normal.write_reg = _spi_write_reg;
|
||||||
|
handle->interface.u.normal.read_reg = _spi_read_reg;
|
||||||
|
handle->interface.u.normal.write_regs = _write_regs;
|
||||||
|
handle->interface.u.normal.read_regs = _read_regs;
|
||||||
|
handle->interface.u.normal.read_drdy = _read_drdy;
|
||||||
|
|
||||||
|
handle->interface.u.normal.spi_send = spi_read_write_byte;
|
||||||
|
handle->interface.u.normal.spi_reset = spi_reset;
|
||||||
|
handle->interface.u.normal.spi_read = spi_read;
|
||||||
|
handle->interface.u.normal.spi_write = spi_write;
|
||||||
|
handle->interface.u.normal.spi_write_reg = spi_write_reg;
|
||||||
|
handle->interface.u.normal.spi_read_reg = spi_read_reg;
|
||||||
|
|
||||||
|
handle->cfg.cmd_rdsr = CMD_RDSR;
|
||||||
|
handle->cfg.cmd_wrsr = CMD_WRSR;
|
||||||
|
handle->cfg.cmd_wren = CMD_WREN;
|
||||||
|
handle->cfg.cmd_wrdi = CMD_WRDI;
|
||||||
|
handle->cfg.cmd_read = CMD_READ;
|
||||||
|
handle->cfg.cmd_write = CMD_WRITE;
|
||||||
|
handle->cfg.dummy_byte = DUMMY_BYTE;
|
||||||
|
handle->cfg.continuous_write = FALSE;
|
||||||
|
}
|
||||||
|
else if (spi_type == SPI_TYPE_LCD)
|
||||||
|
{
|
||||||
|
handle->interface.u.lcd.write_cmd = _spi_write_cmd;
|
||||||
|
handle->interface.u.lcd.write_data = _spi_write_data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_ASSERT(FALSE __DBG_LINE);
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_free(spi_t *handle)
|
||||||
|
{
|
||||||
|
if (handle != NULL)
|
||||||
|
{
|
||||||
|
gpio_free(handle->gpios.cs);
|
||||||
|
gpio_free(handle->gpios.rdy);
|
||||||
|
gpio_free(handle->gpios.rst);
|
||||||
|
gpio_free(handle->gpios.mosi);
|
||||||
|
gpio_free(handle->gpios.miso);
|
||||||
|
gpio_free(handle->gpios.sck);
|
||||||
|
|
||||||
|
osel_mem_free(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 硬件SPI模式
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @param {SPI_TypeDef} *spi SPI总线的寄存器结构体指针
|
||||||
|
* @return {*} 无
|
||||||
|
* @note: 该函数用于设置SPI总线的硬件模式。它首先断言handle不为空,然后断言spix不为空。接着,将handle的simulate_gpio设置为FALSE,并将spix的地址赋值给handle->spix。最后,调用SPI_ENABLE函数启用SPI总线。
|
||||||
|
*/
|
||||||
|
static void _hardware_enable(spi_t *handle, SPI_TypeDef *spi)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(spi != NULL __DBG_LINE);
|
||||||
|
handle->simualte_gpio = FALSE;
|
||||||
|
handle->spi = spi;
|
||||||
|
SPI_ENABLE(spi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _dma_enable(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb,
|
||||||
|
uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(dma != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(handle->spi != NULL __DBG_LINE);
|
||||||
|
handle->dma = dma;
|
||||||
|
handle->dma_rx_channel = dma_rx_channel;
|
||||||
|
handle->dma_tx_channel = dma_tx_channel;
|
||||||
|
handle->rx_dma_ok = TRUE;
|
||||||
|
handle->tx_dma_ok = TRUE;
|
||||||
|
if (dma_rx_cb != NULL)
|
||||||
|
{
|
||||||
|
handle->dma_rx_cb = dma_rx_cb;
|
||||||
|
}
|
||||||
|
if (dma_tx_cb != NULL)
|
||||||
|
{
|
||||||
|
handle->dma_tx_cb = dma_tx_cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_DMA_SetPeriphAddress(dma, dma_tx_channel, LL_SPI_DMA_GetRegAddr(handle->spi));
|
||||||
|
LL_DMA_ClearFlag_GI1(dma);
|
||||||
|
LL_DMA_ClearFlag_TC1(dma);
|
||||||
|
LL_DMA_EnableIT_TC(dma, dma_tx_channel);
|
||||||
|
LL_SPI_EnableDMAReq_TX(handle->spi);
|
||||||
|
LL_SPI_Enable(handle->spi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _spi_dma_callback(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
if (handle->dma_tx_cb != NULL)
|
||||||
|
{
|
||||||
|
handle->dma_tx_cb(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle->dma_rx_cb != NULL)
|
||||||
|
{
|
||||||
|
handle->dma_rx_cb(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL _spi_dma_send(spi_t *handle, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
handle->tx_dma_ok = FALSE;
|
||||||
|
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
|
||||||
|
|
||||||
|
LL_DMA_SetMemoryAddress(handle->dma, handle->dma_tx_channel, (uint32_t)data);
|
||||||
|
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, length);
|
||||||
|
|
||||||
|
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
|
||||||
|
|
||||||
|
uint16_t i = 0;
|
||||||
|
while (handle->tx_dma_ok == FALSE)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
if (i > 50000)
|
||||||
|
{
|
||||||
|
__NOP();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle->tx_dma_ok == TRUE ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取数据准备好信号
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @return {uint8_t} 数据准备好信号的值
|
||||||
|
* @note: 该函数用于读取SPI总线的数据准备好信号。它首先断言handle不为空,然后返回handle的gpios.rdy的读取结果。
|
||||||
|
*/
|
||||||
|
static uint8_t _read_drdy(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
return handle->gpios.rdy->read(*handle->gpios.rdy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 写入单个寄存器
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @param {uint8_t} reg 寄存器号
|
||||||
|
* @param {uint8_t} data 寄存器值
|
||||||
|
* @return {*} 操作结果
|
||||||
|
* @note: 该函数用于写入SPI总线的单个寄存器。它首先断言handle不为空,然后断言data不为空。接着,将SPI总线的CS引脚设置为低电平,发送寄存器号和寄存器值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||||||
|
*/
|
||||||
|
static uint8_t _write_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(data != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(len != 0 __DBG_LINE);
|
||||||
|
spi_cs_low(handle);
|
||||||
|
status = spi_read_write_byte(handle, reg); // 发送寄存器号
|
||||||
|
while (len--)
|
||||||
|
{
|
||||||
|
spi_read_write_byte(handle, *data); // 写入寄存器的值
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
spi_cs_high(handle);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取多个寄存器
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @param {uint8_t} reg 寄存器号
|
||||||
|
* @param {uint8_t} *data 寄存器值的指针
|
||||||
|
* @param {uint8_t} len 寄存器值的个数
|
||||||
|
* @return {uint8_t} 操作结果
|
||||||
|
* @note: 该函数用于读取SPI总线的多个寄存器。它首先断言handle不为空,然后断言data不为空,接着断言len大于0。接着,将SPI总线的CS引脚设置为低电平,发送寄存器号,然后循环读取多个寄存器的值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||||||
|
*/
|
||||||
|
static uint8_t _read_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(data != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(len != 0 __DBG_LINE);
|
||||||
|
|
||||||
|
spi_cs_low(handle);
|
||||||
|
status = spi_read_write_byte(handle, reg);
|
||||||
|
for (uint8_t i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
data[i] = spi_read_write_byte(handle, 0xff); // 读出数据
|
||||||
|
}
|
||||||
|
spi_cs_high(handle);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 写入单个寄存器
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @param {uint8_t} reg 寄存器号
|
||||||
|
* @param {uint8_t} data 寄存器值
|
||||||
|
* @return {*} 操作结果
|
||||||
|
* @note: 该函数用于写入SPI总线的单个寄存器。它首先断言handle不为空,然后将SPI总线的CS引脚设置为低电平,发送寄存器号和寄存器值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||||||
|
*/
|
||||||
|
static uint8_t _spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
|
||||||
|
spi_cs_low(handle);
|
||||||
|
status = spi_read_write_byte(handle, reg); // 发送寄存器号
|
||||||
|
spi_read_write_byte(handle, data); // 写入寄存器的值
|
||||||
|
|
||||||
|
spi_cs_high(handle);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取单个寄存器
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @param {uint8_t} reg 寄存器号
|
||||||
|
* @return {*} 寄存器值
|
||||||
|
* @note: 该函数用于读取SPI总线的单个寄存器。它首先断言handle不为空,然后将SPI总线的CS引脚设置为低电平,发送寄存器号,接着读取寄存器的值,最后将SPI总线的CS引脚设置为高电平,返回寄存器值。
|
||||||
|
*/
|
||||||
|
static uint8_t _spi_read_reg(spi_t *handle, uint8_t reg)
|
||||||
|
{
|
||||||
|
uint8_t reg_val = 0;
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
|
||||||
|
spi_cs_low(handle);
|
||||||
|
spi_read_write_byte(handle, reg); // 发送寄存器号
|
||||||
|
reg_val = spi_read_write_byte(handle, 0);
|
||||||
|
spi_cs_high(handle);
|
||||||
|
return reg_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 写入命令
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @param {uint8_t} cmd 命令
|
||||||
|
* @return {*} 操作结果
|
||||||
|
* @note: 该函数用于写入SPI总线的命令。它首先断言handle不为空,然后将SPI总线的RDY引脚设置为低电平,发送命令,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||||||
|
*/
|
||||||
|
static uint8_t _spi_write_cmd(spi_t *handle, uint8_t cmd)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
spi_rdy_low(handle);
|
||||||
|
spi_cs_low(handle);
|
||||||
|
status = spi_read_write_byte(handle, cmd); // 发送命令
|
||||||
|
spi_cs_high(handle);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 写入数据
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @param {uint8_t} *data 要写入的数据的指针
|
||||||
|
* @param {uint16_t} len 要写入的数据的长度
|
||||||
|
* @return {*} 操作结果
|
||||||
|
* @note: 该函数用于写入SPI总线的数据。它首先断言handle不为空,然后将SPI总线的RDY引脚设置为高电平,发送数据,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||||||
|
*/
|
||||||
|
static uint8_t _spi_write_data(spi_t *handle, uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
spi_rdy_high(handle);
|
||||||
|
spi_cs_low(handle);
|
||||||
|
for (uint16_t i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
status = spi_read_write_byte(handle, *data);
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
spi_cs_high(handle);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPI延时函数
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @return {*} 无
|
||||||
|
* @note: 该函数用于SPI总线的延时。它首先断言handle不为空,然后根据handle的delay_ticks的值,循环执行NOP指令。
|
||||||
|
*/
|
||||||
|
static inline void spi_delay(spi_t *handle)
|
||||||
|
{
|
||||||
|
uint16_t count = 0;
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
count = handle->delay_ticks;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
__NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL spi_wait_flag(spi_t *handle, uint32_t flag, uint32_t timeout)
|
||||||
|
{
|
||||||
|
uint32_t i = 0;
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
if (flag == LL_SPI_SR_TXE)
|
||||||
|
{
|
||||||
|
while (LL_SPI_IsActiveFlag_TXE(handle->spi) == 0)
|
||||||
|
{
|
||||||
|
if (i++ > timeout)
|
||||||
|
{
|
||||||
|
return FALSE; // 超时
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
__NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (flag == LL_SPI_SR_RXNE)
|
||||||
|
{
|
||||||
|
while (LL_SPI_IsActiveFlag_RXNE(handle->spi) == 0)
|
||||||
|
{
|
||||||
|
if (i++ > timeout)
|
||||||
|
{
|
||||||
|
return FALSE; // 超时
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
__NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (flag == LL_SPI_SR_BSY)
|
||||||
|
{
|
||||||
|
while (LL_SPI_IsActiveFlag_BSY(handle->spi) == 0)
|
||||||
|
{
|
||||||
|
if (i++ > timeout)
|
||||||
|
{
|
||||||
|
return FALSE; // 超时
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
__NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_ASSERT(FALSE __DBG_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE; // 成功
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读写一个字节
|
||||||
|
* @param {spi_t} *handle SPI总线设备句柄
|
||||||
|
* @param {uint8_t} tx_data 要写入的数据
|
||||||
|
* @return {*} 读取到的数据
|
||||||
|
* @note: 该函数用于SPI总线的读写一个字节。它首先断言handle不为空,然后根据handle的simulate_gpio的值,选择模拟SPI或硬件SPI。最后,返回读取到的数据。
|
||||||
|
*/
|
||||||
|
static uint8_t spi_read_write_byte(spi_t *handle, uint8_t tx_data)
|
||||||
|
{
|
||||||
|
uint8_t bit_ctr;
|
||||||
|
uint8_t rdata = 0xff;
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
if (handle->simualte_gpio == TRUE)
|
||||||
|
{
|
||||||
|
// 模拟SPI
|
||||||
|
for (bit_ctr = 0; bit_ctr < 8; bit_ctr++)
|
||||||
|
{
|
||||||
|
spi_sck_low(handle);
|
||||||
|
spi_delay(handle);
|
||||||
|
|
||||||
|
if (tx_data & 0x80)
|
||||||
|
spi_mosi_high(handle);
|
||||||
|
else
|
||||||
|
spi_mosi_low(handle);
|
||||||
|
|
||||||
|
spi_delay(handle);
|
||||||
|
spi_sck_high(handle);
|
||||||
|
spi_delay(handle);
|
||||||
|
tx_data = (tx_data << 1);
|
||||||
|
rdata = rdata << 1;
|
||||||
|
|
||||||
|
if (NULL != handle->gpios.miso->port)
|
||||||
|
{
|
||||||
|
if (spi_miso_read(handle) == 1)
|
||||||
|
rdata += 0x01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (rdata);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LL_SPI_TransmitData8(handle->spi, tx_data);
|
||||||
|
|
||||||
|
if (spi_wait_flag(handle, LL_SPI_SR_RXNE, SPI_TIMEOUT) == FALSE)
|
||||||
|
{
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
rdata = LL_SPI_ReceiveData8(handle->spi);
|
||||||
|
return rdata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _write_enable(spi_t *handle)
|
||||||
|
{
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs);
|
||||||
|
handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_wren);
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _write_disable(spi_t *handle)
|
||||||
|
{
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs);
|
||||||
|
handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_wrdi);
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t _read_status(spi_t *handle)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs);
|
||||||
|
handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_rdsr);
|
||||||
|
data = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte);
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _ready(spi_t *handle)
|
||||||
|
{
|
||||||
|
uint16_t count = 0;
|
||||||
|
while (_read_status(handle) & 0x01)
|
||||||
|
{
|
||||||
|
if (count++ > 20000)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
__NOP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL spi_write(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
BOOL ret = TRUE;
|
||||||
|
uint8_t cnt = 0; // 返回值检查
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(data != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(length > 0 __DBG_LINE);
|
||||||
|
DBG_ASSERT(handle->cfg.page_size > 0 __DBG_LINE);
|
||||||
|
uint32_t page_size = handle->cfg.page_size;
|
||||||
|
_write_enable(handle); // 写入使能命令
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信
|
||||||
|
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_write); // 发送写入命令
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle->cfg.address_bytes == 2)
|
||||||
|
{
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8); // 发送高位地址
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr); // 发送低位地址
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 16);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (length--)
|
||||||
|
{
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, *data); // 发送一个字节数据
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
data++;
|
||||||
|
if (handle->cfg.continuous_write == FALSE)
|
||||||
|
{
|
||||||
|
write_addr++;
|
||||||
|
if (((write_addr % page_size) == 0) && (length > 0))
|
||||||
|
{
|
||||||
|
// 一页写完
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平,完成SPI通信
|
||||||
|
_ready(handle);
|
||||||
|
|
||||||
|
_write_enable(handle); // 写入使能命令
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信
|
||||||
|
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_write); // 发送写入命令
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (handle->cfg.address_bytes == 2)
|
||||||
|
{
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8); // 发送高位地址
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr); // 发送低位地址
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 16);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr >> 8);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, write_addr);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平,完成SPI通信
|
||||||
|
|
||||||
|
_ready(handle);
|
||||||
|
_write_disable(handle);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_write_reg(spi_t *handle, uint8_t reg, uint8_t value)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
_write_enable(handle); // 写入使能命令
|
||||||
|
handle->interface.u.normal.write_reg(handle, reg, value);
|
||||||
|
_ready(handle);
|
||||||
|
_write_disable(handle); // 写入禁止命令
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t spi_read_reg(spi_t *handle, uint8_t reg)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs);
|
||||||
|
handle->interface.u.normal.spi_send(handle, reg);
|
||||||
|
data = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte);
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL spi_read(spi_t *handle, uint32_t read_addr, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
BOOL ret = TRUE;
|
||||||
|
uint8_t cnt = 0; // 返回值检查
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(data != NULL __DBG_LINE);
|
||||||
|
DBG_ASSERT(length > 0 __DBG_LINE);
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信
|
||||||
|
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, handle->cfg.cmd_read); // 发送读取命令
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (handle->cfg.address_bytes == 2)
|
||||||
|
{
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 8); // 发送高位地址
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, read_addr); // 发送低位地址
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 16);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, read_addr >> 8);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cnt = handle->interface.u.normal.spi_send(handle, read_addr);
|
||||||
|
if (cnt == 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint16_t i = 0; i < length; i++) // 循环读取数据
|
||||||
|
{
|
||||||
|
data[i] = handle->interface.u.normal.spi_send(handle, handle->cfg.dummy_byte); // 发送空字节,读取实际数据
|
||||||
|
}
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs); // 设置CS引脚为高电平,完成SPI通信
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_reset(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs);
|
||||||
|
delay_tick(10);
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs);
|
||||||
|
delay_tick(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为高
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void spi_rdy_high(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.rdy->set(*handle->gpios.rdy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为低
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void spi_rdy_low(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.rdy->reset(*handle->gpios.rdy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为高
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void spi_cs_high(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.cs->set(*handle->gpios.cs);
|
||||||
|
spi_delay(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为低
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void spi_cs_low(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.cs->reset(*handle->gpios.cs);
|
||||||
|
spi_delay(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void spi_mosi_high(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.mosi->set(*handle->gpios.mosi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于将SPI(串行外围接口)设备的输出(MOSI)引脚设置为低
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void spi_mosi_low(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.mosi->reset(*handle->gpios.mosi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void spi_sck_high(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.sck->set(*handle->gpios.sck);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为低
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline void spi_sck_low(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
handle->gpios.sck->reset(*handle->gpios.sck);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 用于读取SPI(串行外围接口)设备的输入(MISO)引脚的电平
|
||||||
|
* @param {spi_id_e} id
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static inline uint8_t spi_miso_read(spi_t *handle)
|
||||||
|
{
|
||||||
|
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||||||
|
return handle->gpios.miso->read(*handle->gpios.miso);
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/**
|
||||||
|
* @file spis.h
|
||||||
|
* @brief SPI驱动, 用于SPI设备的读写操作
|
||||||
|
*
|
||||||
|
* This file contains the SPI driver used for reading and writing operations on SPI devices.
|
||||||
|
*
|
||||||
|
* @date 2023-08-01
|
||||||
|
* @version 1.0
|
||||||
|
*
|
||||||
|
* @note This file is part of the STM32 controller-v2 project.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPIS_H__
|
||||||
|
#define __SPIS_H__
|
||||||
|
|
||||||
|
#include "lib.h"
|
||||||
|
#include "gpios.h"
|
||||||
|
|
||||||
|
#define SPI_ENABLE(SPIX) LL_SPI_Enable(SPIX)
|
||||||
|
|
||||||
|
typedef struct SPIS spi_t;
|
||||||
|
typedef void spis_dma_callback(spi_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPI type enumeration
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SPI_TYPE_NORMAL = 0, ///< SPI1:NORMAL
|
||||||
|
SPI_TYPE_LCD, ///< SPI2:LCD
|
||||||
|
SPI_TYPE_MAX,
|
||||||
|
} spi_type_e;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPI GPIO group structure
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gpio_t *mosi; ///< MOSI
|
||||||
|
gpio_t *miso; ///< MISO
|
||||||
|
gpio_t *sck; ///< SCK
|
||||||
|
gpio_t *cs; ///< CS
|
||||||
|
gpio_t *rst; ///< RST
|
||||||
|
gpio_t *rdy; ///< DRDY
|
||||||
|
} spi_gpio_group_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPI normal interface structure
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t (*write_reg)(spi_t *handle, uint8_t reg, uint8_t data); ///< Write a single register via SPI
|
||||||
|
uint8_t (*read_reg)(spi_t *handle, uint8_t reg); ///< Read the value of a single register via SPI
|
||||||
|
uint8_t (*read_drdy)(spi_t *handle); ///< Get the value of the SPI DRDY pin
|
||||||
|
uint8_t (*write_regs)(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); ///< Write multiple registers via SPI
|
||||||
|
uint8_t (*read_regs)(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); ///< Read multiple registers via SPI
|
||||||
|
uint8_t (*spi_send)(spi_t *handle, uint8_t data); ///< Send data via SPI
|
||||||
|
void (*spi_reset)(spi_t *handle); ///< Reset SPI
|
||||||
|
|
||||||
|
BOOL(*spi_write)
|
||||||
|
(spi_t *handle, uint32_t write_addr, uint8_t *data, uint16_t length); ///< Write data via SPI
|
||||||
|
|
||||||
|
BOOL(*spi_read)
|
||||||
|
(spi_t *handle, uint32_t read_addr, uint8_t *data, uint16_t length); ///< Read data via SPI
|
||||||
|
|
||||||
|
void (*spi_write_reg)(spi_t *handle, uint8_t reg, uint8_t data); ///< Write a single register via SPI
|
||||||
|
uint8_t (*spi_read_reg)(spi_t *handle, uint8_t reg); ///< Read a single register via SPI
|
||||||
|
} spi_normal_interface_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPI LCD interface structure
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t (*write_cmd)(spi_t *handle, uint8_t cmd); ///< Write a command via SPI
|
||||||
|
uint8_t (*write_data)(spi_t *handle, uint8_t *data, uint16_t len); ///< Write data via SPI
|
||||||
|
} spi_lcd_interface_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPI interface structure
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
spi_normal_interface_t normal;
|
||||||
|
spi_lcd_interface_t lcd;
|
||||||
|
} u;
|
||||||
|
void (*hardware_enable)(spi_t *handle, SPI_TypeDef *spi); ///< Enable hardware SPI
|
||||||
|
void (*dma_enable)(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb,
|
||||||
|
uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb); ///< Enable DMA SPI
|
||||||
|
void (*spi_dma_callback)(spi_t *spi); ///< DMA send completion callback
|
||||||
|
BOOL(*spi_dma_send)
|
||||||
|
(spi_t *handle, uint8_t *data, uint16_t length); ///< DMA send
|
||||||
|
} spi_interface_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPI structure
|
||||||
|
*/
|
||||||
|
typedef struct SPIS spi_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPI DMA callback function
|
||||||
|
*/
|
||||||
|
typedef void spis_dma_callback(spi_t *handle);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// CMD
|
||||||
|
uint8_t cmd_rdsr; ///< Read Status Register instruction
|
||||||
|
uint8_t cmd_wrsr; ///< Write Status Register instruction
|
||||||
|
uint8_t cmd_wren; ///< Write enable instruction
|
||||||
|
uint8_t cmd_wrdi; ///< Write disable instruction
|
||||||
|
uint8_t cmd_read; ///< Read from Memory instruction
|
||||||
|
uint8_t cmd_write; ///< Write to Memory instruction
|
||||||
|
|
||||||
|
uint8_t dummy_byte; ///< Dummy byte
|
||||||
|
|
||||||
|
uint8_t address_bytes;
|
||||||
|
uint32_t page_size;
|
||||||
|
uint32_t total_size;
|
||||||
|
uint8_t ticks; ///< Delay in NOP ticks
|
||||||
|
BOOL continuous_write; ///< Continuous write
|
||||||
|
} spi_normal_config_t;
|
||||||
|
|
||||||
|
struct SPIS
|
||||||
|
{
|
||||||
|
spi_type_e spi_type; ///< SPI type
|
||||||
|
uint16_t delay_ticks; ///< Delay in NOP ticks
|
||||||
|
spi_gpio_group_t gpios; ///< SPI GPIOs
|
||||||
|
spi_interface_t interface; ///< SPI interface
|
||||||
|
SPI_TypeDef *spi; ///< SPI peripheral
|
||||||
|
BOOL simualte_gpio; ///< Simulate GPIO
|
||||||
|
spi_normal_config_t cfg; ///< Normal SPI configuration
|
||||||
|
///< DMA
|
||||||
|
DMA_TypeDef *dma; ///< External setting
|
||||||
|
uint32_t dma_rx_channel; ///< External setting
|
||||||
|
uint32_t dma_tx_channel; ///< External setting
|
||||||
|
__IO BOOL rx_dma_ok;
|
||||||
|
__IO BOOL tx_dma_ok;
|
||||||
|
spis_dma_callback *dma_rx_cb; ///< DMA receive callback function
|
||||||
|
spis_dma_callback *dma_tx_cb; ///< DMA send callback function
|
||||||
|
|
||||||
|
void *params; ///< 扩展参数
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new SPI instance
|
||||||
|
*
|
||||||
|
* @param spi_type The type of SPI
|
||||||
|
* @param gpios The SPI GPIO group
|
||||||
|
* @param delay_ticks The delay in NOP ticks
|
||||||
|
* @return spi_t* The created SPI instance
|
||||||
|
*/
|
||||||
|
extern spi_t *spi_create(spi_type_e spi_type, spi_gpio_group_t gpios, uint16_t delay_ticks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free the SPI instance
|
||||||
|
*
|
||||||
|
* @param spi The SPI instance to free
|
||||||
|
*/
|
||||||
|
extern void spi_free(spi_t *spi);
|
||||||
|
|
||||||
|
#endif ///< __SPIS_H__
|
Loading…
Reference in New Issue