1 Star 0 Fork 3

eming / at_client_with_rtos

forked from yshark / at_client_with_rtos 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

at_client_with_rtos

介绍

将RT-Thread 的AT组件移植到FreeRTOS中,若移植到其他操作系统也可以参照此项目

软件架构

  1. freertos(提供信号量、互斥量、创建线程)
  2. at client(RT-Thread 的AT Client实现)
  3. easylogger(为AT Client提供日志支持)
  4. bsp_uart_fifo.c bsp_uart_fifo.h(安富莱电子的串口驱动,带有环形缓冲区)

安装教程

  1. 复制at_client文件夹、easylogger文件夹、bsp_uart_fifo.c bsp_uart_fifo.h文件(这两个串口驱动文件可以不复制,由用户实现串口驱动,建议串口驱动最好带上环形缓冲区)
  2. 先初始化串口驱动,再初始化easylogger,最后初始化at client,详细过程见bsp_init()函数。
  3. 以下是为了适配freertos对原始文件所作出的修改

at.h

#define 	RT_EOK   			0
#define 	RT_ERROR   			1
#define 	RT_ETIMEOUT   		2
#define 	RT_EFULL   			3
#define 	RT_EEMPTY   		4
#define 	RT_ENOMEM   		5
#define 	RT_ENOSYS   		6
#define 	RT_EBUSY   			7
#define 	RT_EIO   			8
#define 	RT_EINTR   			9
#define 	RT_EINVAL   		10
#define 	RT_TRUE   			1
#define 	RT_FALSE   			0
#define 	RT_NAME_MAX			16
#define 	RT_IPC_FLAG_PRIO    0x01
#define 	RT_IPC_FLAG_FIFO    0x00

#define RT_THREAD_PRIORITY_MAX						configMAX_PRIORITIES
#define RT_ASSERT(expression)						do{if(!(expression))printf("expression:(%s) == 0\r\nfile:%s line:%d\r\n",#expression,__FILE__,__LINE__);}while(0)
#define rt_memcpy 									memcpy
#define rt_memset									memset
#define rt_strstr									strstr
#define rt_memcmp									memcmp
#define rt_snprintf									snprintf
#define rt_strlen									strlen
#define rt_strcmp									strcmp
#define	rt_strncmp									strncmp
#define RT_WAITING_FOREVER							portMAX_DELAY
#define rt_weak 									__attribute__((weak))
#define rt_sem_take(Sem,timeout)					xSemaphoreTake(Sem,timeout)
#define rt_sem_release(Sem)							do{if(xPortIsInsideInterrupt()){BaseType_t pxHigherPriorityTaskWoken;xSemaphoreGiveFromISR(Sem,&pxHigherPriorityTaskWoken);} else xSemaphoreGive(Sem);}while(0)
#define rt_mutex_release(mutex)						xSemaphoreGive(mutex)
#define rt_mutex_take(mutex,timeout)				xSemaphoreTake(mutex,timeout)
#define rt_tick_from_millisecond 					pdMS_TO_TICKS
#define rt_tick_get 								xTaskGetTickCount

/* 根据实际需要选择是否修改以下宏定义 */
#define rt_calloc					calloc
#define rt_free						free
#define rt_realloc					realloc
#define rt_kprintf					printf
#define rt_device_t 				COM_PORT_E
	

typedef	size_t 						rt_size_t ;
typedef size_t 						rt_size_t ;
typedef int32_t 					rt_int32_t ;
typedef uint32_t 					rt_uint32_t ;
typedef TaskHandle_t 				rt_thread_t ;
typedef SemaphoreHandle_t 			rt_sem_t ;
typedef SemaphoreHandle_t 			rt_mutex_t ;
typedef size_t						rt_tick_t;
typedef size_t						rt_off_t;
typedef int 						rt_bool_t;
typedef uint8_t 					rt_uint8_t;
typedef long 						rt_base_t;
typedef rt_base_t 					rt_err_t;

struct at_client
{
    rt_device_t device;
	/* 自己添加的 */
	char *client_name;

    at_status_t status;
    char end_sign;

    /* the current received one line data buffer */
    char *recv_line_buf;
    /* The length of the currently received one line data */
    rt_size_t recv_line_len;
    /* The maximum supported receive data length */
    rt_size_t recv_bufsz;
    rt_sem_t rx_notice;
    rt_mutex_t lock;

    at_response_t resp;
    rt_sem_t resp_notice;
    at_resp_status_t resp_status;

    struct at_urc_table *urc_table;
    rt_size_t urc_table_size;

    rt_thread_t parser;	//线程控制块
};

at_utils.c

/*************************************************移植到freertos需要做的修改*******************************************************/




/**
 * 为了兼容 rt_device_read()函数自己定义的
 *
 * @param dev	设备句柄
 * @param pos	读取的偏移量
 * @param buffer	用于保存读取数据的数据缓冲区
 * @param size	缓冲区的大小
 *
 * @return 成功返回实际读取的大小,如果是字符设备,返回大小以字节为单位,如果是块设备,返回的大小以块为单位;失败则返回0
 */
rt_weak rt_size_t rt_device_read	( rt_device_t 	dev,
									  rt_off_t 	pos,
									  void * 	buffer,
									  rt_size_t 	size 
									)	
{
	uint32_t rx_num = 0;
	
	for(uint32_t i = 0; i < size; i++){
		if(comGetChar(dev - 1,buffer))
			rx_num++;
		else
			goto exit;
	}
	
	exit:
	return rx_num;
}									  

/**
 * 为了兼容 rt_device_write()函数自己定义的
 *
 * @param dev	设备句柄
 * @param pos	写入的偏移量
 * @param buffer	要写入设备的数据缓冲区
 * @param size	写入数据的大小
 *
 * @return 成功返回实际写入数据的大小,如果是字符设备,返回大小以字节为单位;如果是块设备,返回的大小以块为单位;失败则返回0。
 */
rt_size_t rt_device_write	(rt_device_t 	dev,
							 rt_off_t 	pos,
							 const void * 	buffer,
							 rt_size_t 	size 
							)	
{
	comSendBuf(dev - 1,(uint8_t*)buffer,size);
	return size;
}

/*创建信号量 */
rt_sem_t rt_sem_create	(const char * 	name,
						 rt_uint32_t 	value,
						 rt_uint8_t 	flag 
						 )	
{
	return xSemaphoreCreateBinary();
}

/*创建互斥量 */
rt_mutex_t rt_mutex_create	(const char * 	name,
							 rt_uint8_t 	flag 
							)	
{
	return xSemaphoreCreateMutex();
}

/* 删除互斥量 */
rt_err_t rt_mutex_delete	(rt_mutex_t 	mutex)	
{
	vSemaphoreDelete(mutex);
	return RT_EOK;
}
/* 删除信号量 */
rt_err_t rt_sem_delete	(	rt_sem_t 	sem	)	
{
	vSemaphoreDelete(sem);
	return RT_EOK;
}

根据实际需要修改 rt_device_read() rt_device_write()函数

at_client.c

/**
 * AT client initialize.
 * 为了兼容安富莱的串口驱动,新增一个参数  _ucPort
 * @param dev_name AT client device name
 * @param recv_bufsz the maximum number of receive buffer length
 * @param _ucPort 为at客户端指定一个串口设备
 * @return 0 : initialize success
 *        -1 : initialize failed
 *        -5 : no memory
 */
int at_client_init(const char *dev_name,  rt_size_t recv_bufsz ,COM_PORT_E _ucPort)
{
    int idx = 0;
    int result = RT_EOK;
    rt_err_t open_result = RT_EOK;
    at_client_t client = RT_NULL;

    RT_ASSERT(dev_name);
    RT_ASSERT(recv_bufsz > 0);

    if (at_client_get(dev_name) != RT_NULL)
    {
        return result;
    }

    for (idx = 0; idx < AT_CLIENT_NUM_MAX && at_client_table[idx].device; idx++);

    if (idx >= AT_CLIENT_NUM_MAX)
    {
        LOG_E("AT client initialize failed! Check the maximum number(%d) of AT client.", AT_CLIENT_NUM_MAX);
        result = -RT_EFULL;
        goto __exit;
    }

    client = &at_client_table[idx];
    client->recv_bufsz = recv_bufsz;

    result = at_client_para_init(client);
    if (result != RT_EOK)
    {
        goto __exit;
    }

    /* find and open command device */
	/* 为了兼容安富莱驱动 */
	client->client_name = (char*)dev_name;
	client->device = _ucPort + 1; /* 加一是因为at_client_init会通过dev来遍历at_client_table,遍历语句for (idx = 0; idx < AT_CLIENT_NUM_MAX && at_client_table[idx].device; idx++); */
//    client->device = rt_device_find(dev_name);
//    if (client->device)
//    {

//        /* using DMA mode first */
//        open_result = rt_device_open(client->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);
//        /* using interrupt mode when DMA mode not supported */
//        if (open_result == -RT_EIO)
//        {
//            open_result = rt_device_open(client->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
//        }
//        RT_ASSERT(open_result == RT_EOK);

//        rt_device_set_rx_indicate(client->device, at_client_rx_ind);
//    }
//    else
//    {
//        LOG_E("AT client initialize failed! Not find the device(%s).", dev_name);
//        result = -RT_ERROR;
//        goto __exit;
//    }

__exit:
    if (result == RT_EOK)
    {
        client->status = AT_STATUS_INITIALIZED;

//        rt_thread_startup(client->parser);

        LOG_I("AT client(V%s) on device %s initialize success.", AT_SW_VERSION, dev_name);
    }
    else
    {
        LOG_E("AT client(V%s) on device %s initialize failed(%d).", AT_SW_VERSION, dev_name, result);
    }

    return result;
}

根据实际需要修改at_client_init()函数,让at client和串口设备关联在一起,这里通过client->device = _ucPort + 1;语句,使client和安富莱串口驱动关联在一起。

串口驱动接收到字符需要通知线程来处理字符,at client组件已经为我们准备好了串口回调函数,我们只需要在串口接收中断或者DMA中断中调用即可。回调函数如下所示:

/* 由静态函数改为全局函数 */
rt_err_t at_client_rx_ind(rt_device_t dev, rt_size_t size)
{
    int idx = 0;

    for (idx = 0; idx < AT_CLIENT_NUM_MAX; idx++)
    {
        if (at_client_table[idx].device == dev && size > 0)
        {
            rt_sem_release(at_client_table[idx].rx_notice);
        }
    }

    return RT_EOK;
}

调用示例如下:

/* AIR780E_ReciveNew是我的串口接收中断回调函数 */
void AIR780E_ReciveNew(uint8_t _byte)
{
	at_client_rx_ind(COM1 + 1,1); /* 加一是因为at_client_init会通过dev来遍历at_client_table,遍历语句for (idx = 0; idx < AT_CLIENT_NUM_MAX && at_client_table[idx].device; idx++); */
}

这里需要注意:由于需要在中断中使用freertos的API,所以中断的优先级需要小于等于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY(数值越小,优先级越高。即中断优先级的数值需要大于等于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY)

如果需要移植到其他操作系统,需要特别注意你使用的操作系统成功释放信号量是返回1还是返回0。(RT-Thread成功释放返回0,FreeRTOS成功释放返回1)

举例:

rt_size_t at_client_obj_recv(at_client_t client, char *buf, rt_size_t size, rt_int32_t timeout)
{
    rt_size_t len = 0;

    RT_ASSERT(buf);

    if (client == RT_NULL)
    {
        LOG_E("input AT Client object is NULL, please create or get AT Client object!");
        return 0;
    }

    while (1)
    {
        rt_size_t read_len;

//        rt_sem_control(client->rx_notice, RT_IPC_CMD_RESET, RT_NULL);

        read_len = rt_device_read(client->device, 0, buf + len, size);
        if(read_len > 0)
        {
            len += read_len;
            size -= read_len;
            if(size == 0)
                break;

            continue;
        }

//        if(rt_sem_take(client->rx_notice, rt_tick_from_millisecond(timeout)) != RT_EOK) //RT-Thread   #define RT_EOK 0
		if(rt_sem_take(client->rx_notice, rt_tick_from_millisecond(timeout)) != pdTRUE)   //FreeRTOS    #define pdTRUE 1
            break;
    }

#ifdef AT_PRINT_RAW_CMD
    at_print_raw_cmd("urc_recv", buf, len);
#endif

    return len;
}

事实上,为了移植到freertos上所作出的修改不仅仅只是上面的那些修改,作出修改的地方一般都以注释的方式保留了原始代码。可以通过是否有注释来判断是否修改了源码。

使用说明

example:

main.c文件中创建了以下任务:

static void LED_Task (void* parameter) 
{ 
	if(at_client_wait_connect(10000)){
		printf("连接失败\r\n");
	}else{
		printf("AT连接成功\r\n");
	}
      while (1)        
      { 
			bsp_LedToggle(1);
          vTaskDelay(1000);   /* 延时 500 个 tick */ 
	}
 }

at_client_wait_connect()函数会在10秒内不断发送AT\r\n,直到接收字符串OK\r\n at 连接成功

请参考以下链接

RT-Thread AT 组件

RT-Thread API参考手册

转载说明

转载文章请保留以下链接:

资源下载链接1: https://gitee.com/sharkisyou/at_client_with_rtos

文章转载链接: https://blog.csdn.net/qq_41430785/article/details/133416610?spm=1001.2014.3001.5501

MIT License Copyright (c) 2023 木易 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

将RT-Thread 的AT组件移植到FreeRTOS中,若移植到其他操作系统也可以参照此项目 展开 收起
C
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
C
1
https://gitee.com/eming/at_client_with_rtos.git
git@gitee.com:eming/at_client_with_rtos.git
eming
at_client_with_rtos
at_client_with_rtos
master

搜索帮助