目录
1:service框架简介
在嵌入式中,我们获取一个资源、设置一个变量、更新数据的话最直接的方式就是直接调用一个函数。这样的写法也可以满足基本的功能,前提是这个系统是只要一个任务,只有一个while(1)如图1.1。
图1.1
当有两个或者两个以上的任务的时候,同时调用一个函数(设置一个状态值、操作同一个资源、操作指针等行为)的流程如图1.2。
图1.2
当两个任务同时操作一个资源的时候,就会有重入的问题,会导致想不到的异常问题(数据不对、指针异常等)。
为了解决这个重入的问题,我们就引入了这个服务框架,就是新增一个任务做中专站,统一处理资源。
1.1:service框架简流程
图1.1.1
多个任务同时调用一个函数,通过服务框架发给另一个任务统一处理,这样就有效的防止代码的重入问题,解决不可发生的异常情况。
2:service框架代码
2.1:代码大概框架思路
(1):独立的一个任务:service_task;
(2):消息的ID、数据、数据长度、消息服务的名称;
(3):消息发送的互斥锁;
(4):消息发送的函数(依赖rtos)xMessageBufferSendFromISR、xMessageBufferSend;
(5):一个消息链表,保存每个业务的服务消息;
2.2:代码
2.2.1:代码思路基本流程
2.2.2:创建服务任务
void service_task_init(void)
{
// create service task
xTaskCreate(service_task,
SERVICE_TASK_NAME,
SERVICE_TASK_STACK_SIZE,
NULL,
SERVICE_TASK_PRIORITY,
&g_service_task);
if (g_service_task == NULL) {
log_e("service_task_init error\n");
} else {
log_i("service task init ok\n");
}
}
2.2.3:初始化
1:用到的结构体
#define SERVICE_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define META_LIST_FOR_EACH_ENTRY(item, list, type, member) \
for (item = META_LIST_ENTRY((list)->pst_next, type, member); \
&(item)->member != (list); \
item = META_LIST_ENTRY((item)->member.pst_next, type, member))
typedef struct {
uint16_t service_count; /** service count */
meta_list service_list;
} service_manager_t;
typedef struct {
uint16_t msg_id; /** message id */
uint16_t msg_len; /** message length */
}service_msg_t;
typedef struct {
uint16_t version; /** Service version*/
uint8_t is_local; /** Service is local or not */
char name[SERVICE_NAME_MAX_LEN]; /** Service name */
uint32_t (*init)(void); /** Service initialization */
uint32_t (*read)(uint8_t* data, uint32_t len); /** Service read */
uint32_t (*write)(uint8_t* data, uint32_t len); /** Service write */
void* (*ioctl)(uint32_t cmd, void* data, uint32_t len); /** Service ioctl */
uint32_t (*deinit)(void); /** Service deinitialization */
uint32_t (*handler)(uint32_t event_id, uint8_t* data, uint32_t len); /** Service event handler */
} service_t;
2:创建互斥量、消息
// create service message buffer
g_service_msgbuf_handle = xMessageBufferCreate(SERVICE_MSG_BUF_SIZE);
if (g_service_msgbuf_handle == NULL) {
log_e("service_msg_buf error");
return;
}
// create message mutex
g_service_msg_mutex = xSemaphoreCreateMutex();
if (g_service_msg_mutex == NULL) {
log_e("service_msg_mutex error");
return;
}
memset(g_msg_recv_buf, 0, sizeof(g_msg_recv_buf));
3:消息链表初始化
uint32_t services_list_init(void)
{
memset(&g_service_manager, 0, sizeof(service_manager_t));
/**
* The Service Management Link table is initialized
*/
meta_list_init(&g_service_manager.service_list);
g_service_manager.service_count = 0;
return 0;
}
4:各个业务服务注册
static void service_add(service_t *service)
{
if (service == NULL) {
log_e("service is NULL");
return;
}
service_node_t *pst_service_list = NULL;
pst_service_list = (service_node_t *)SERVICE_MALLOC(sizeof(service_node_t));
if (pst_service_list == NULL) {
log_e("service_node_t malloc failed");
return;
}
pst_service_list->service = service;
meta_list_tail_insert(&g_service_manager.service_list, &pst_service_list->list);
}
uint32_t service_register(service_t *service)
{
if (service == NULL) {
log_e("service is NULL");
return SERVICE_RET_PARAM_ERR;
}
if (service->init == NULL) {
log_e("service init is NULL");
return SERVICE_RET_PARAM_ERR;
}
service->init();
service_add(service);
return SERVICE_RET_OK;
}
/**
* Service Link table initialization
*/
for (int i = 0; i < SERVICE_ARRAY_SIZE(g_local_service_tab); i++) {
// register to service list
uint32_t ret = service_register((service_t *)&g_local_service_tab[i]);
if (ret != SERVICE_RET_OK) {
log_e("service_register error(%d)", ret);
}
log_i("register service %s", g_local_service_tab[i].name);
}
5:消息发送函数
uint32_t service_msg_send(uint32_t msg_id, void *msg, uint16_t len)
{
if (len > SERVICE_RECV_BUF_SIZE) {
return SERVICE_RET_PARAM_ERR;
}
if((msg == NULL && len != 0) ||(msg != NULL && len == 0)) {
return SERVICE_RET_PARAM_ERR;
}
uint8_t *buf = (uint8_t *)SERVICE_MALLOC(len + sizeof(service_msg_t));
if (buf == NULL) {
log_e("service_msg_send malloc error");
return SERVICE_RET_MALLOC_ERR;
}
/**
* Service Message Structure
*/
service_msg_t *msg_send = (service_msg_t *)buf;
msg_send->msg_id = msg_id;
msg_send->msg_len = len;
uint16_t pos = sizeof(service_msg_t);
memcpy(&buf[pos], msg, len);
const TickType_t x100ms = pdMS_TO_TICKS(100);
// send message
if (__get_IPSR()) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// in interrupt
// send message directly
size_t length = xMessageBufferSendFromISR(g_service_msgbuf_handle, (void *)buf, len + sizeof(service_msg_t), &xHigherPriorityTaskWoken);
if (length == 0) {
log_e("service_msg_send error");
SERVICE_FREE(buf);
return SERVICE_RET_SEND_ERR;
}
} else {
// in task
if (msg_id != SERVICE_MSG_WEAR_TIMER_SWITCH) {
log_i("service_msg_send in task");
}
// mutex lock
service_msg_mutex_lock();
// send message
size_t length = xMessageBufferSend(g_service_msgbuf_handle, (void *)buf, len + sizeof(service_msg_t), x100ms);
// mutex unlock
service_msg_mutex_unlock();
if (length == 0) {
log_e("service_msg_send error");
SERVICE_FREE(buf);
return SERVICE_RET_SEND_ERR;
}
// wait for send complete
vTaskDelay(1);
}
// free buffer
SERVICE_FREE(buf);
return SERVICE_RET_OK;
}
6:消息接收解析
uint32_t services_manager_process_handler(uint32_t msg_id, void *msg_body, uint32_t msg_body_len)
{
service_node_t *pst_service_list = NULL;
META_LIST_FOR_EACH_ENTRY(pst_service_list, &g_service_manager.service_list, service_node_t, list) {
if (pst_service_list->service->handler != NULL) {
//TODO:
pst_service_list->service->handler(msg_id, msg_body, msg_body_len);
}
}
return SERVICE_RET_OK;
}
while (1) {
size_t length = xMessageBufferReceive(g_service_msgbuf_handle, (void *)&g_msg_recv_buf, sizeof(g_msg_recv_buf), portMAX_DELAY);
if (length > 0) {
// service message
service_msg_t *msg = (service_msg_t *)g_msg_recv_buf;
// service id
uint16_t msg_id = msg->msg_id;
uint16_t msg_len = msg->msg_len;
uint16_t pos = sizeof(service_msg_t);
uint8_t *msg_buf = (uint8_t *)&g_msg_recv_buf[pos];
// service handle
services_manager_process_handler((uint32_t)msg_id, msg_buf, (uint32_t)msg_len);
}
}
在TODO:的地方应该还要添加判断当前发送的服务消息是否跟当前的handler函数是一致的,否则就会每个服务都会收到这个发送的ID。
这样做的好处还有一个就是 SERVICE_MESSAGE_ID都是可以独立,也可以重复。
3:demo
uint8_t worship_angle = 0;
static void worship_set_angle(uint8_t angle)
{
worship_angle = angle;
}
void worship_serv_set_angle(uint8_t angle)
{
service_msg_send(SERVICE_MSG_WORSHIP_XXX, (void *)(&angle), sizeof(uint8_t));
}
void worship_serv_set_angle(uint8_t angle)
{
service_node_t *worship_service = get_service_by_name(WORSHIP_SERVICE_NAME);
service_ioctl(worship_service, WORSHIP_IOCTL_XXX, (void *)(&angle), sizeof(uint8_t));
}
bool worship_serv_get_angle(void)
{
service_node_t *worship_service = get_service_by_name(WORSHIP_SERVICE_NAME);
return (bool)(*(uint8_t *)service_ioctl(worship_service, WORSHIP_IOCTL_GET_ANGLE, NULL, 0));
}
uint32_t worship_service_init(void)
{
return 0;
}
uint32_t worship_service_deinit(void)
{
return 0;
}
uint32_t worship_service_read(uint8_t *data, uint32_t len)
{
return 0;
}
uint32_t worship_service_write(uint8_t *data, uint32_t len)
{
return 0;
}
void* worship_service_ioctl(uint32_t cmd, void *data, uint32_t len)
{
switch (cmd) {
case WORSHIP_IOCTL_GET_ANGLE:
return &worship_angle;
break;
case MUSIC_IOCTL_SET_LYRIC_STATE:{
worship_set_angle((uint8_t)(*(uint8_t *)data));
}
default:
break;
}
return NULL;
}
uint32_t worship_service_handler(uint32_t event, uint8_t *data, uint32_t len)
{
switch (event) {
case SERVICE_MSG_WORSHIP_XXX: {
worship_set_angle((uint8_t)(*(uint8_t *)data));
}
break;
default:
break;
}
return 0;
}
3.1:数据的读写
我们需要传递一个变量、结构体数据等,可以参考:worship_serv_set_angle。有两种方式可自行选择,其中的差异自行体会。
获取数据可以参考:worship_serv_get_angle
3.2:函数的注册
我们需要注册6个函数:init、read、write、ioctl、deinit、handler;
typedef enum {
WORSHIP_SERVICE_ID = 0,
SERVICE_ID_MAX
} SERVICE_ID_TYPE_E;
#define WORSHIP_SERVICE_NAME "worship"
const service_t g_local_service_tab[SERVICE_ID_MAX] = {
[WORSHIP_SERVICE_ID] = {
.version = 0,
.is_local = SERVICE_TYPE_INSIDE,
.name = WORSHIP_SERVICE_NAME,
.init = worship_service_init,
.read = worship_service_read,
.write = worship_service_write,
.ioctl = worship_service_ioctl,
.deinit = worship_service_deinit,
.handler = worship_service_handler,
},
}
这样就会在我们上面“各个业务服务注册”那里进行注册;
4:结语
上诉是一个最简单的嵌入式的服务框架了,基本能满足需求,如有其他的建议或者需求可以沟通一起学习。
其中这个框架其实还可以加个一个发送自定义延迟发送的功能,这个功能还没有时间进行编写,但是感觉这个延迟功能还是不错;
如果想要对这个框架进行升华的话可以参考安卓的H andler + MessageQueue + Looper,这样的。之前我导师写过,但是没有全部学过来。后续可以研究一下