您好, 登录| 注册|
论坛导航
您好, 登录| 注册|
子站:
论坛首页    单片机MCU/嵌入式
  •  发帖
  • 收藏

【RT-thread学习记】: 设备层框架设计浅析
阅读: 9940 |  回复: 16 楼层直达

2018/12/06 13:59:47
1
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长

QQ截图20160321155901  【活动进行中……】礼遇感恩节!参与顶楼  免费送仪器

QQ截图20160321155901   【风采汇】参与有奖!获奖作品分享大赛



关于设备层的问题,先暂缓讨论,接下来先讲一下单片机编程界的两大难题,曾困扰我N久,第一你是否需要通晓你说设计的软件代码的每一个细节,理由:这样做,不论哪一部分出现问题,你都可以以最快的速度找到问题点,然后改一改,问题解决。第二你是否能接受你的代码中嵌套或者加载别人的代码或者库,源代码可能看不到,就算开源了,你发现源代码看不懂,先来讲看不懂的问题,看不懂并不代表别人写的晦涩高深,连语法都看不懂,而是说,别人做的工作过于抽象,你很难将它在脑海中实例化,所以看不懂。
标签 STM32 MCU
2018/12/06 14:00:45
2
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
反过来讲,别人为什么能做出很多抽象化的工作,实际上都是大量的实例应用后,总结
与重构的结果,就像当年读过的一本书,设计模式,很经典的著作,看完依旧没有任何感触
那时候也在做底层的工作,感觉要从中抽象出一个框架太难太难,还是写一个特例的驱动更加
简单,虽然不能重用,但开发起来相对更快一些。接触过很多做软件的工程师都是这样,包括我。
2018/12/06 14:02:17
3
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
RTT中引入了设备的概念,其实这并不是个全新的东西,相反在计算机时代已经普及了,但是对于单片机
开发而言却并没有那么顺利,首先,大家总会考虑一个问题,资源,单片机自身的资源非常的小,
这时候你引入os和设备层等,会加重资源的开销,举个栗子,假如有两个单片机,一个2kROM一个4k
ROM,价格差一元,会有人告诉你,你不用那些东西,裸编程,紧吧紧吧2k就够了,这样产品出货量越多
你能节省的成本就越多,这没毛病吧,其实哪怕价格差10元,我推荐你用后者,你节省了一元钱可能
损失的是更多的市场。
2018/12/06 14:03:14
4
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
怎么讲,硬件的发展速度还是远超我的想象的,当年昂贵的硬件,如今也平常价格,但是资源的丰富
却给开发带来了更多的机遇,对于一个工程师来讲,开发的速度,稳定性是非常重要的因素。
假如每一个项目你都从头来过,不光你开发的东西很浪费精力,而且开发新品会越来越困难,你不得不去
研究那些细节,如何实现等,重新走过开发未知的坑。但是假如有现成的底层可以利用,仅仅通过简单的
接口功能就能完成驱动的访问,读写等功能,你就只需要醉心于上层应用的设计,是不是更有效率。
的确有人做出了设备层,RTT就是其中之一。
2018/12/06 14:04:56
5
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
将驱动中可重用可设置的量提取出来,使用时,你只需要配置相关参数,调用接口就可以完成相关功能,
虽然底层的实现机制变复杂了,但是应用层却简单了,假如底层经过了验证,那么在以后的开发中,当你
需要某一部分驱动支持的时候,或许仅仅需要连接一下设备层就可以了。
并不是所有的设备层的实现都很完美,有的简单有的复杂,有的甚至在特定情况下出现bug
但这并不是重点,重点是有了它的助力,开发将会变得简单有趣。
2018/12/06 14:05:25
6
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
这里举得例子并不来自RTT,但是相对结构简单,我还能分析的了,所以给大家展开一下,
讲一讲这个设备层实现的机制吧,有兴趣的筒子们,可以看看RTT的源码,会收获更多。
2018/12/06 14:11:51
7
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
这里以stm32的串口为例,进行一些浅析,
将串口看作是一个设备以后,串口号就是串口的身份识别了。
我们再用这个串口设备之前,需要先行配置串口的波特率校验位等
参数,然后通过初始化函数进行配置就可以了,而在这一部分中
相当于是注册一个设备。likethis,这里的代码处理并不好,通过参数
传入的方式会更好些,仅做理解参考。
    USART_InitTypeDef USART_InitStructure;
    USART_ClockInitTypeDef  USART_ClockInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
 
    /****************************************************
    USART1
    ******************************************************/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init( GPIOA, &GPIO_InitStructure );   //PA10接收端
 
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init( GPIOA, &GPIO_InitStructure );  //PA9发送端
    //串口数据格式设置
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    //时钟波形配置
    USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
    USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
    USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;
    USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
 
    USART_Init( USART1, &USART_InitStructure );  //初始化串口1
    USART_ClockInit( USART1, &USART_ClockInitStructure ); //初始化串口1时钟
 
    USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );
    USART_ITConfig( USART1, USART_IT_TC, DISABLE );   //
   
    //中断优先级设置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 15;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init( &NVIC_InitStructure );
 
    USART_Cmd( USART1, ENABLE );
2018/12/06 14:41:01
8
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
这里分为两层进行框架处理,底层真正的收发机制,及对接用户接口设计
先看底层的收发机制,实际为收发buff的控制,设计中仅采用中断方式设计
真正的设计中要比这里的框架复杂的多。
typedef struct
{
  unsigned char datas_buffer[MAX_REC_SIZE];
  unsigned char *write_pointer;
  unsigned char *read_pointer;
  unsigned char write_full_flag;
  unsigned char read_full_flag;
}SERIAL_STR;
对接收和发送buff的管理。
void init_serial(void)
{
    int i;
    xSerialPortInitMinimal_UARTS();
 
    for(i=0; i<SERIAL_NUM; i++)
    {
        serial_rec_g[i].write_pointer=serial_rec_g[i].datas_buffer;
        serial_rec_g[i].read_pointer=serial_rec_g[i].datas_buffer;
        serial_rec_g[i].write_full_flag=0;
        serial_rec_g[i].read_full_flag=0;
        serial_snd_g[i].write_pointer=serial_snd_g[i].datas_buffer;
        serial_snd_g[i].read_pointer=serial_snd_g[i].datas_buffer;
        serial_snd_g[i].write_full_flag=0;
        serial_snd_g[i].read_full_flag=0;
    }
 
}
2018/12/06 14:44:30
9
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
中断服务程序处理
/*-----------------------------------------------------------
  串口1接收发送中断函数
  -----------------------------------------------------------*/
void USART1_IRQHandler(void)
{
    if( USART_GetITStatus( USART1, USART_IT_TC ) == SET )
    {
        //发送中断
        serial_snd(USART1,&serial_snd_g[0]);
    }
   
    if( USART_GetITStatus( USART1, USART_IT_RXNE ) == SET )
    {
        unsigned char ch;
        ch=serial_rec(USART1,&serial_rec_g[0]);
    }
}
/*-----------------------------------------------------------
  串口2接收发送中断函数
  -------------------------------------------------------------*/
void USART2_IRQHandler(void)
{
    if( USART_GetITStatus( USART2, USART_IT_TXE ) == SET )
    {
        serial_snd(USART2,&serial_snd_g[1]);
    }
   
    if( USART_GetITStatus( USART2, USART_IT_RXNE ) == SET )
    {
        serial_rec(USART2,&serial_rec_g[1]); //读取接收寄存器
    }
}
2018/12/06 14:46:05
10
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
中断服务程序中调用的函数
/*
 *中断中串口数据的接收
 */
static unsigned char serial_rec(USART_TypeDef* USARTx,SERIAL_STR *serial_rec)
{
    unsigned char ch;
    ch=*serial_rec->write_pointer = USART_ReceiveData( USARTx ); //读取接收寄存器
    serial_rec->write_pointer++;
    if((serial_rec->write_pointer-serial_rec->datas_buffer)>MAX_REC_SIZE-1)
    {
        serial_rec->write_pointer=serial_rec->datas_buffer;
        serial_rec->write_full_flag=1;
    }
 
    return ch;
}
/*
 *中断中串口数据的发送
 */
static void serial_snd(USART_TypeDef* USARTx,SERIAL_STR *serial_snd)
{
  //---------------------------------------------------------------------
  //分析:发送机制,读缓存为发送机制
    if(serial_snd->write_pointer!=serial_snd->read_pointer ||
        (serial_snd->read_full_flag==0 && serial_snd->write_full_flag==1))
    {
        USART_SendData( USARTx, *serial_snd->read_pointer ); //写入发送寄存器
   
        serial_snd->read_pointer++;
        if((serial_snd->read_pointer-serial_snd->datas_buffer)>(MAX_REC_SIZE-1))
        {
            serial_snd->read_pointer=serial_snd->datas_buffer;
            serial_snd->read_full_flag=1;
        }
    }
    else
    {
      //-----------------------------------------------------------------
      //分析:无可读缓存,结束发送机制
        if(serial_snd->read_full_flag==1 && serial_snd->write_full_flag==1)
        {
          serial_snd->read_full_flag=0;
          serial_snd->write_full_flag=0;
        }
     
        //发送结束
        if(USARTx==USART1)
        {
            USART_ITConfig( USARTx, USART_IT_TC, DISABLE );
        }
        else
        {
            USART_ITConfig( USARTx, USART_IT_TXE, DISABLE );
        }
 
        #if 0
            if(USARTx==USART3)      //485庚
            {
                USART_ITConfig( USARTx, USART_IT_TC, DISABLE );//关闭发送
            }
            else
            {
                USART_ITConfig( USARTx, USART_IT_TXE, DISABLE );//关闭发送
            }
        #endif
    }
}
2018/12/06 14:47:17
11
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长

用户接口部分处理
/*串口数据接收处理结构*/
struct rcv_deal_pro_st
{
  char rcv_buff[1024];
  int total_len;
  int port;
 
  void *result;  

  int (*resolve_pro_func)(unsigned char *, int, void *);
  int (*deal_pro_func)(int, void *);
};

2018/12/06 14:50:10
12
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长

用户接口实现细节
/*
 *函数功能:启动串口数据发送功能
 * des:要写入参数的地址
 * len:要写入参数的长度
 * port:要写入的串口号
 * 返回值:实际写入的数据长度,这个参数有点鸡肋啊。。。
 */
int write_serial(unsigned char *des,int len,int port)
{
      int i,temp_len=0;
      SERIAL_STR *serial_snd = &serial_snd_g[port -1];
   
      if(serial_snd!=0)
      {
          for(i=0;i<len;i++)
          {
              *serial_snd->write_pointer = des[temp_len++];
              serial_snd->write_pointer++;
              if((serial_snd->write_pointer-serial_snd->datas_buffer)>MAX_REC_SIZE-1)
              {
                  serial_snd->write_pointer=serial_snd->datas_buffer;
                  serial_snd->write_full_flag=1;  
              }
          }
          if(port==1)
          {
              USART_ITConfig( USART1, USART_IT_TC, ENABLE );
          }
          else if(port==2)
          {
              USART_ITConfig( USART2, USART_IT_TXE, ENABLE );
          }  
          else if(port==3)
          {
              USART_ITConfig( USART3, USART_IT_TXE, ENABLE );    
          }
      }
   
      return temp_len;
}


int rcv_deal_datas(void *datas, void *param, void *flag)
{
    struct rcv_deal_pro_st *pro = (struct rcv_deal_pro_st *)datas;
    int len = 0;
    int type = 0;
 
    len=read_serial( (unsigned char *)(pro->rcv_buff+pro->total_len), sizeof(pro->rcv_buff)-len-1, pro->port);
    if(len)
    {
        pro->total_len += len;
    }
    else if(pro->total_len)
    {
        unsigned char *point = (unsigned char *)pro->rcv_buff;
       
        while(point < pro->rcv_buff + pro->total_len)
        {
            type = pro->resolve_pro_func(pro->rcv_buff, pro->total_len, pro->result);
            //清除缓存数据
            if(type && pro->deal_pro_func)
            {
                pro->deal_pro_func(type, pro->result);
            }
     
            point = (unsigned char *)&pro->result;
             
     
            memset(pro->rcv_buff,0,1024);
        }
       
        pro->total_len=0;
    }
 
    return 1;
}

2018/12/06 15:22:12
13
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长

接下来看下RTT内部对于设备层的管理是如何定义的:

struct rt_device
{
  struct rt_object parent;
  
  /*设备类型*/
  enum rt_device_class_type type;
  /*设备参数及打开参数*/
  rt_uint16_t flag,open_flag;
  
  /*提供给上层应用的回调函数*/
  rt_err_t (*rx_indicate)(rt_device_t dev,rt_size_t size);
  rt_err_t (*tx_complete)(rt_device_t dev,void* buffer);
  
  /*公共的设备接口(由驱动程序提供)*/
  rt_err_t (*init)(rt_device_t dev);
  rt_err_t (*open)(rt_device_t dev,rt_uint16_t oflag);
  rt_err_t (*close)(rt_device dev);
  rt_size_t (*read)(rt_device_t dev,rt_off_t pos,void* buffer,rt_size_t size);
  rt_size_t (*write)(rt_device_t dev,rt_off_t pos,const void* buffer,rt_size_t size);
  rt_err_t (*control)(rt_device_t dev,rt_uint8_t cmd,void* args);
  
  /*设备的私有数据*/
  void* user_data;
};
typedef struct rt_device* rt_device_t;

2018/12/06 15:22:47
14
s453208[版主]
电源币:607 | 积分:104 主题帖:48 | 回复帖:171
LV7
旅长
/*当前RT_Thread支持的设备类型包括:*/
enum rt_device_class_type
{
  RT_Device_Class_Char = 0,     /*字符设备*/
  RT_Device_Class_Block,      /*块设备*/
  RT_Device_Class_NetIf,      /*网络接口设备*/
  RT_Device_Class_MTD,              /*内存设备*/
  RT_Device_Class_CAN,              /*CAN设备*/
  RT_Device_Class_RTC,              /*RTC设备*/
  RT_Device_Class_Sound,            /*声音设备*/
  RT_Device_Class_Graphic,          /*图形设备*/
  RT_Device_Class_I2BUS,            /*I2C总线*/
  RT_Device_Class_USBDevice,    /*USB device设备*/
  RT_Device_Class_USBHost,     /*USB host设备*/
  RT_Device_Class_SPIBUS,           /*SPI总线*/
  RT_Device_Class_SPIDevice,        /*SPI设备*/
  RT_Device_Class_SDIO,             /*SDIO设备*/
  RT_Device_Class_PM,               /*电源管理设备*/
  RT_Device_Class_Pipe,             /*管道设备*/
  RT_Device_Class_Portal,           /*双向管道设备*/
  RT_Device_Class_Timer,
  RT_Device_Class_Miscellaneous,    /*杂类设备*/
  RT_Device_Class_Unknown,          /*未知设备*/
  
};
2018/12/25 17:55:02
15
电源网-璐璐
电源币:1092 | 积分:86 主题帖:295 | 回复帖:1519
LV10
司令
RT-thread学习系列更多精彩内容】PS:点击可直接跳转阅读

               本帖内容】RT-thread学习之设备层框架设计浅析

               RT-thread学习之一直走下去
               RT-thread学习之RTT 的与众不同
               RT-thread学习之object对象管理机制
               RT-thread学习之Thread线程机制及应用
               RT-thread学习之线程间的同步

2019/03/12 15:43:41
16
sabrina9988
电源币:34 | 积分:0 主题帖:163 | 回复帖:305
LV7
旅长
顶一下
2019/12/02 23:33:40
17
飞翔2004
电源币:3479 | 积分:7 主题帖:183 | 回复帖:830
LV9
军长
还没搞过操作系统,顶一下
客服热线
服务时间:周一至周五9:00-18:00
微信关注
免费技术研讨会
获取一手干货分享

互联网违法不良信息举报

Reporting Internet Illegal and Bad Information
editor@netbroad.com
400-003-2006