建议使用以下浏览器,以获得最佳体验。 IE 9.0+以上版本 Chrome 31+ 谷歌浏览器 Firefox 30+ 火狐浏览器
请选择 进入手机版 | 继续访问电脑版
设置昵称

在此一键设置昵称,即可参与社区互动!

确定
我再想想
选择版块
直达楼层
标签
您还可以添加5个标签
  • 没有搜索到和“关键字”相关的标签
  • 云产品
  • 解决方案
  • 技术领域
  • 通用技术
  • 平台功能
取消

采纳成功

您已采纳当前回复为最佳回复

樊心昊

发帖: 172粉丝: 139

发消息 + 关注

发表于2020年05月11日 02:10:54 1279 5
直达本楼层的链接
楼主
显示全部楼层
[技术干货] 0x01 LiteOS_Lab仓库组件详解--AT(上)

摘要:本节以解析UART.c文件中的代码为主,为了后面讲解AT框架及Driver框架做铺垫。

关于串口的背景知识:https://bbs.huaweicloud.com/forum/thread-53612-1-1.html

由于该文件中设计到了ring_buffer也就是环形数据结构的操作,你可以先阅读该篇文章,理解环形数据结构:https://bbs.huaweicloud.com/forum/thread-55254-1-1.html

本节的代码摘自该文件:https://github.com/LiteOS/LiteOS_Lab/blob/iot_link/targets/STM32L431_BearPi/uart_at/uart_at.c

下图中红框的地方是本节讲解的重点:

image.png

auti_cb结构体


struct atio_cb
{
    unsigned short        w_next;    //the next position to be write
    osal_semp_t           rcvsync;   //if a frame has been written to the ring, then active it
    tag_ring_buffer_t     rcvring;

/*tag_ring_buffer_t结构体
typedef struct    
{    
unsigned char   *buf;      ///< which means the buffer    
int              buflen;   ///< which means the buffer limit    
int              datalen;  ///< which means how many data in the buffer    
int              dataoff;  ///< which means the valid data offset in the buffer    
}tag_ring_buffer_t;    

*/
    unsigned char         rcvbuf[CONFIG_UARTAT_RCVMAX];
    unsigned char         rcvringmem[CN_RCVMEM_LEN];
    //for the debug here
    unsigned int          rframeover; //how many times the frame has been over the max length
    unsigned int          rframedrop; //how many frame has been droped for memmory
    unsigned int          sndlen;     //how many bytes has been sent
    unsigned int          rcvlen;     //how many bytes has been received    用于在中断中存储接收到了多少字节的数据
    unsigned int          sndframe;   //how many frame has been sent
    unsigned int          rcvframe;   //how many frame has been received
    unsigned int          rcvringrst; //how many times the receive ring has been reset
};
static struct atio_cb   g_atio_cb;

初始化串口


/*******************************************************************************
function     :use this function to initialize the uart
parameters   :
instruction  :
*******************************************************************************/
bool_t uart_at_init(void *pri)
{
    //initialize the at controller
    (void) memset(&g_atio_cb,0,sizeof(g_atio_cb));    //为g_atio_cb结构体分配内存
    if(false == osal_semp_create(&g_atio_cb.rcvsync,CN_RCVMEM_LEN,0))    //创建一个信号量
    {
        printf("%s:semp create error\n\r",__FUNCTION__);            //如果创建失败直接退出该函数并打印报错信息
        goto EXIT_SEMP;
    }
    ring_buffer_init(&g_atio_cb.rcvring,g_atio_cb.rcvringmem,CN_RCVMEM_LEN,0,0);
    
/* ring_buffer_init(&g_atio_cb.rcvring,g_atio_cb.rcvringmem,CN_RCVMEM_LEN,0,0);函数的定义
int ring_buffer_init(tag_ring_buffer_t *ring,unsigned char *buf, int buflen,int offset,int datalen)    
{    
    int ret = -1;    
    if((NULL == ring))    
    {    
    return ret;    
    }    
    ring->buf     = buf;    //将rcvringmem的地址与tag_ring_buffer_t类型的rcvring结构体中的buf指针进行绑定
    ring->buflen  = buflen; //设置长度为用户定义的CN_RCVMEM_LEN   
    ring->datalen = datalen;//设置为0    
    ring->dataoff = offset; //设置为0  
    ret = 0;    
    return ret;    
}    

*/

/* 初始化串口硬件相关,波特率、数据位、停止位以及校验位 */
    uart_at.Instance = s_pUSART;
    uart_at.Init.BaudRate = CONFIG_UARTAT_BAUDRATE;
    uart_at.Init.WordLength = UART_WORDLENGTH_8B;
    uart_at.Init.StopBits = UART_STOPBITS_1;
    uart_at.Init.Parity = UART_PARITY_NONE;
    uart_at.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    uart_at.Init.Mode = UART_MODE_TX_RX;
    uart_at.Init.OverSampling = UART_OVERSAMPLING_16;
    if(HAL_UART_Init(&uart_at) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }
    __HAL_UART_CLEAR_FLAG(&uart_at,UART_FLAG_TC);    //清除标志位
    LOS_HwiCreate(s_uwIRQn, 3, 0, atio_irq, 0);  //通过LiteOS接管中断的方式创建中断,发生相应中断跳转到atio_irq函数处理
    __HAL_UART_ENABLE_IT(&uart_at, UART_IT_IDLE);   //开启空闲中断:可以用该中断判断是否收到一帧数据(注意这里是一帧,不是一字节)
    __HAL_UART_ENABLE_IT(&uart_at, UART_IT_RXNE);   //开启接收中断
    return true;

EXIT_SEMP:
    return false;
}

void uart_at_deinit(void *pri)
{
    __HAL_UART_DISABLE(&uart_at);
    __HAL_UART_DISABLE_IT(&uart_at, UART_IT_IDLE);
    __HAL_UART_DISABLE_IT(&uart_at, UART_IT_RXNE);
}



串口中断处理函数


/*******************************************************************************
function     :use this function to deal the uart interrupt
parameters   :
instruction  :we cached the data in the temp buffer,when the idle interrupt reached,
              then we write the data and the length to the ring if the ring has enough
              space
*******************************************************************************/
/* 当产生串口中断就跳转到该函数进行处理 */
static void atio_irq(void)
{
    unsigned char  value;
    unsigned short ringspace;
    if(__HAL_UART_GET_FLAG(&uart_at, UART_FLAG_RXNE) != RESET)    //如果是接收中断,代表收到了一字节数据
    {
       value = (uint8_t)(uart_at.Instance->RDR & 0x00FF);    //将数据从寄存器中读到value变量中
       g_atio_cb.rcvlen++;                                        //结构体中的接收长度+1
       if(g_atio_cb.w_next < CONFIG_UARTAT_RCVMAX)            //w_next如果小于最大接收长度则正常,反之说明溢出
       {
           g_atio_cb.rcvbuf[g_atio_cb.w_next] = value;
           g_atio_cb.w_next++;                            //w_next变量用于记录下一次收到一个字节的数据该写到rcvbuf数组中的哪个位置
       }
       else                                                    //溢出数据统计
       {
            g_atio_cb.rframeover++;                        
       }
    }
    else if (__HAL_UART_GET_FLAG(&uart_at,UART_FLAG_IDLE) != RESET)    //如果是空闲中断,代表一帧数据全部收到
    {
        __HAL_UART_CLEAR_IDLEFLAG(&uart_at);
        ringspace = CN_RCVMEM_LEN - ring_buffer_datalen(&g_atio_cb.rcvring);    //计算环形结构体中的空闲空间大小
        if(ringspace < g_atio_cb.w_next)  //not enough mem    //如果这帧数据的大小大于环形结构体中的空闲空间大小,丢弃数据
        {
            g_atio_cb.rframedrop++;
        }
        else                        //正常
        {
            //write data to the ring buffer:len+data format
            ringspace = g_atio_cb.w_next;            //将存储这帧的数组“大小”(这是一个下标)写到ringspace变量中
            
            /* 先把这帧数据的大小写到结构体中,再把数据写到结构体中,最终格式|len|data| */
            ring_buffer_write(&g_atio_cb.rcvring,(unsigned char *)&ringspace,sizeof(ringspace));
            ring_buffer_write(&g_atio_cb.rcvring,g_atio_cb.rcvbuf,ringspace);
            
            (void) osal_semp_post(g_atio_cb.rcvsync);
            g_atio_cb.rcvframe++;    //接收到的帧数+1
        }
        g_atio_cb.w_next=0; //write from the head    清空接收缓存区大小
    }
    else ///< clear the flags
    {
        __HAL_UART_CLEAR_PEFLAG(&uart_at);
        __HAL_UART_CLEAR_FEFLAG(&uart_at);
        __HAL_UART_CLEAR_NEFLAG(&uart_at);
        __HAL_UART_CLEAR_OREFLAG(&uart_at);
    }


}

image.png

串口发送函数


/*******************************************************************************
function     :use this function to send a frame to the uart
parameters   :
instruction  :
*******************************************************************************/
static ssize_t uart_at_send(const char  *buf, size_t len,uint32_t timeout)
{
    HAL_UART_Transmit(&uart_at,(unsigned char *)buf,len,timeout);    //直接调用HAL库函数即可发送数据
    g_atio_cb.sndlen += len;
    g_atio_cb.sndframe ++;

    return len;
}


串口接收函数


/*******************************************************************************
function     :use this function to read a frame from the uart
parameters   :
instruction  :
*******************************************************************************/
/* 该函数本质上是去接收结构体(g_atio_cb.rcvsync)中读取数据 */
static ssize_t uart_at_receive(void *buf,size_t len,uint32_t timeout)
{
    unsigned short cpylen;
    unsigned short framelen;
    unsigned short readlen;
    int32_t ret = 0;
    unsigned int lock;
    if(osal_semp_pend(g_atio_cb.rcvsync,timeout))
    {
        lock = LOS_IntLock();
        readlen = sizeof(framelen);
        cpylen = ring_buffer_read(&g_atio_cb.rcvring,(unsigned char *)&framelen,readlen);    //从g_atio_cb.rcvring结构体中读取“长度“数据
        if(cpylen != readlen)    //说明结构体中的数据长度小于需要读取的数据长度,结构体中的数据有问题
        {
            ring_buffer_reset(&g_atio_cb.rcvring);  //bad ring format here 清空该结构体中的数据
            g_atio_cb.rcvringrst++;
        }
        else
        {
            if(framelen > len)    //清空该结构体中的数据
            {
                ring_buffer_reset(&g_atio_cb.rcvring);  //bad ring format here
                g_atio_cb.rcvringrst++;
            }
            else
            {
                readlen = framelen;
                cpylen = ring_buffer_read(&g_atio_cb.rcvring,(unsigned char *)buf,readlen);//从g_atio_cb.rcvring结构体中读取“数据“数据
                if(cpylen != framelen)
                {
                    ring_buffer_reset(&g_atio_cb.rcvring);  //bad ring format here
                    g_atio_cb.rcvringrst++;
                }
                else
                {
                    ret = cpylen;    //读取成功返回实际读到的数据大小
                }
            }
        }
        LOS_IntRestore(lock);
    }
    return ret;
}

用于先driver层注册的函数


/*  通过driver层屏蔽uatr层的底层差异,让driver层之上的层觉得无论什么单片机都是一样的方法操作,使用init、deinit 、read、write即可
 *
 */
//make it as the at device here
static ssize_t  __at_read  (void *pri,size_t offset,void *buf,size_t len, uint32_t timeout)
{
    return uart_at_receive(buf,len, timeout);

}
static ssize_t  __at_write (void *pri, size_t offset,const void *buf,size_t len,uint32_t timeout)
{
    return uart_at_send(buf, len, timeout);

}


static const los_driv_op_t s_at_op = {

        .init = uart_at_init,
        .deinit = uart_at_deinit,
        .read = __at_read,
        .write = __at_write,
};

OSDRIV_EXPORT(uart_at_driv,CONFIG_UARTAT_DEVNAME,(los_driv_op_t *)&s_at_op,NULL,O_RDWR);


举报
分享

分享文章到朋友圈

分享文章到微博

采纳成功

您已采纳当前回复为最佳回复

ttking

发帖: 380粉丝: 22

发消息 + 关注

发表于2020年05月13日 08:42:57
直达本楼层的链接
沙发
显示全部楼层

完整的一个串口收发 感谢分享

评论
樊心昊 2020-5-13 21:13 评论

感谢支持!

... 查看全部
点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

牛穿疯

发帖: 15粉丝: 1

发消息 + 关注

发表于2020年05月14日 20:49:02
直达本楼层的链接
板凳
显示全部楼层

AT 下什么时候出呀版主大大

评论
樊心昊 2020-5-14 21:05 评论

快啦,我准备先出driver再出AT下,你可以先看看这个串口中用到的数据结构,ring_buffer环形数据结构的操作:https://bbs.huaweicloud.com/forum/thread-55254-1-1.html

... 查看全部
点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

极客潇

发帖: 477粉丝: 68

发消息 + 关注

发表于2020年05月21日 08:43:34
直达本楼层的链接
地板
显示全部楼层

图文并茂,很不错的指导文案

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

scu-w

发帖: 333粉丝: 9

发消息 + 关注

发表于2020年05月25日 17:52:04
直达本楼层的链接
5#
显示全部楼层

感谢分享!

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

我是卤蛋

发帖: 121粉丝: 292

级别 : 版主,版块专家

发消息 + 关注

发表于2020年06月26日 11:12:05
直达本楼层的链接
6#
显示全部楼层

感谢分享,持续学习~

点赞 评论 引用 举报

游客

富文本
Markdown
您需要登录后才可以回帖 登录 | 立即注册

结贴

您对问题的回复是否满意?
满意度
非常满意 满意 一般 不满意
我要反馈
0/200