1. API Overview
1.1. Introduction
bl_mcu_sdk code hierarchy is divided into the following main layers.
The application layer: codes written by users.
The component layer: some opensource components,the interface calls the HAL layer, while the wireless layer is used for wireless functions.
- HAL layer and wireless layer for adaptation to different MCUs, where the HAL layer is divided into two layers
Device driver management: provides a standard set of interfaces, which are implemented by the peripheral driver adaptation layer
Peripheral driver adaptation layer: implements the standard interface of the device driver management and extends its own unique interface
Standard driver layer based on register packaging
Hardware layer, also known as the register layer
1.2. Device driver management layer
The realization of the device driver management layer adopts the object-oriented idea. First of all, we regard the peripheral as a device or a file, adhering to the concept of everything is a file, and files have standard calling interfaces: open
, close
, ctrl
, write
, read
, callback
. Different file types are different (such as serial device, ADC device, SPI device), and the way of opening is also different (such as polling, interrupt, DMA), from this, we can construct a base class (parent class) of an object.
Base class
struct device
{
char name[NAME_MAX]; /*name of device */
dlist_t list; /*list node of device */
enum device_status_type status; /*status of device */
enum device_class_type type; /*type of device */
uint16_t oflag; /*oflag of device */
int (*open)(struct device *dev, uint16_t oflag);
int (*close)(struct device *dev);
int (*control)(struct device *dev, int cmd, void *args);
int (*write)(struct device *dev, uint32_t pos, const void *buffer, uint32_t size);
int (*read)(struct device *dev, uint32_t pos, void *buffer, uint32_t size);
void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event);
void *handle;
};
Base class member: name
Name the device and use device_find
to find the device.
Base class member: type
type
records the category of the current device, and the type
that can be selected are as follows.
enum device_class_type
{
DEVICE_CLASS_NONE = 0,
DEVICE_CLASS_GPIO,
DEVICE_CLASS_UART,
DEVICE_CLASS_SPI,
DEVICE_CLASS_I2C,
DEVICE_CLASS_ADC,
DEVICE_CLASS_DMA,
DEVICE_CLASS_TIMER,
DEVICE_CLASS_PWM,
DEVICE_CLASS_SDIO,
DEVICE_CLASS_USB,
DEVICE_CLASS_I2S,
DEVICE_CLASS_CAMERA,
DEVICE_CLASS_SEC_HASH,
} ;
Base class member: status
status
is used to record the current status of the device and provides 4 statuses.
enum device_status_type
{
DEVICE_UNREGISTER = 0,
DEVICE_REGISTERED,
DEVICE_OPENED,
DEVICE_CLOSED
} ;
Base class member: oflag
oflag
records the flag information filled in during registration and the oflag
information filled in when using device_open
.
Base class members: list
The addition and deletion of equipment is stored in a doubly linked list, which saves memory.
Base class members: standard function pointers
Provides a standard function interface for different peripherals. When the peripheral implements this type of interface and assigns it to the member, the function of rewriting can be achieved.
1.3. Device driver management layer standard interface
1.3.1. device_register
device_register
is used to register the device and register the device information in the linked list.
int device_register(struct device *dev, const char *name);
dev: device handle.
name: the name of the device.
return: return error code, 0 means registration is successful, others mean errors.
1.3.2. device_unregister
device_unregister
is used to delete the device and delete the device information from the linked list.
int device_unregister(const char *name);
dev: device handle
name: the name of the device to be deleted
return: error code, 0 means delete, others mean error
1.3.3. device_find
device_find
is used to find the device from the linked list according to name
, and return the first address of the device handle.
struct device *device_find(const char *name);
dev: device handle
name: the name of the device to be searched
return: error code,! 0 means the device handle was found, NULL means the device was not found.
1.3.4. device_open
device_open
is used to open the device, and oflag
represents the opening method. Currently, there are 6 opening methods available. The bottom layer calls the open
member in the dev
handle.
int device_open(struct device *dev, uint16_t oflag);
dev: device handle
oflag: open method
return: error code, 0 means opening is successful, others mean errors
oflag
can write the following parameters:
#define DEVICE_OFLAG_STREAM_TX 0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX 0x002 /* The device is turned on in polling receiving mode */
#define DEVICE_OFLAG_INT_TX 0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX 0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX 0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX 0x020 /* The device is turned on in DMA receiving mode */
1.3.5. device_close
device_close
is used to close the device. The bottom layer calls the close
member in the dev
handle.
int device_close(struct device *dev);
-dev: device handle -return: error code, 0 means closing is successful, others mean error
1.3.6. device_control
device_control
is used to control the device and modify parameters according to commands. The bottom layer calls the control
member in the dev
handle.
int device_control(struct device *dev, int cmd, void *args);
dev: device handle
cmd: device control command
args: control parameters
return: Different control commands return different meanings.
cmd
provides the following standard commands. In addition, different peripherals also have their own commands
#define DEVICE_CTRL_SET_INT 0x01 /* set interrupt */
#define DEVICE_CTRL_CLR_INT 0x02 /* clear interrupt */
#define DEVICE_CTRL_GET_INT 0x03 /* get interrupt status*/
#define DEVICE_CTRL_RESUME 0x04 /* resume device */
#define DEVICE_CTRL_SUSPEND 0x05 /* suspend device */
#define DEVICE_CTRL_CONFIG 0x06 /* config device */
#define DEVICE_CTRL_GET_CONFIG 0x07 /* get device configuration */
#define DEVICE_CTRL_ATTACH_TX_DMA 0x08
#define DEVICE_CTRL_ATTACH_RX_DMA 0x09
#define DEVICE_CTRL_TX_DMA_SUSPEND 0x0a
#define DEVICE_CTRL_RX_DMA_SUSPEND 0x0b
#define DEVICE_CTRL_TX_DMA_RESUME 0x0c
#define DEVICE_CTRL_RX_DMA_RESUME 0x0d
#define DEVICE_CTRL_RESVD1 0x0E
#define DEVICE_CTRL_RESVD2 0x0F
1.3.7. device_write
device_write
is used to send data, and the sending mode can be polling, interrupt, dma. The bottom layer calls the write
member in the dev
handle.
int device_write(struct device *dev, uint32_t pos, const void *buffer, uint32_t size);
dev: device handle
pos: different devices have different meanings for pos
buffer: the buffer to be written
size: the length to be written
return: error code, 0 means writing is successful, others mean errors
1.3.8. device_read
device_read
is used to receive data, and the receiving mode can be polling, interrupt, dma. The bottom layer calls the read
member in the dev
handle.
int device_read(struct device *dev, uint32_t pos, void *buffer, uint32_t size);
dev: device handle
pos: different devices have different meanings for pos
buffer: the buffer to be read
size: the length to be read
return: error code, 0 means successful reading, others mean errors
1.3.9. device_set_callback
device_set_callback
is used to register interrupt callback function. The bottom layer calls the callback
member in the dev
handle.
int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
dev: device handle
callback: the interrupt callback function to be registered
dev: device handle
args: Different peripherals have different meanings
size: transmission length
event: interrupt event type
1.4. Peripheral driver adaptation layer
Subclass inherits from parent class
The first member of different peripherals is struct device
, which is equivalent to the inheritance of the parent class, so that the subclass can be used to access the parent class member. When the subclass is used to modify the members of the parent class, it has its own functions. The realization principle is that the first address of different structures is the address of the first member in the structure.
typedef struct xxx_device
{
struct device parent;
} xxx_device_t;
Rewrite standard interface
Each peripheral has a xxx_register
function, which is used to rewrite the standard interface.
dev->open = xxx_open;
dev->close = xxx_close;
dev->control = xxx_control;
dev->write = xxx_write;
dev->read = xxx_read;