OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2060|回复: 8

申精!不使用操作系统实现LED的延时(在延时期间不占用CPU资源)

[复制链接]

37

主题

204

帖子

0

精华

高级会员

Rank: 4

积分
717
金钱
717
注册时间
2016-6-10
在线时间
191 小时
发表于 2018-8-25 22:00:36 | 显示全部楼层 |阅读模式
本帖最后由 周亚龙 于 2018-8-25 22:14 编辑

上个月接触到了一套谷雨NB-IOT开发板代码,看的本人颇为费神。代码中使用了大量的结构体嵌套结构体指向函数的指针来封装整个代码。着实让我摸不到头脑。请教大神之后,让我耐心去看,并且要熟悉这种代码框架,刚从本科学校出来,公司才干了四个月,写代码对我来说确实只是袖子撸起来,直接上去干。劳资写代码从来不思考什么程序架构,实现功能再说,我管他那么多。直道遇到这篇文章。
以下为CSDN转载:
C语言编程代码架构搭建——代码分层
/////////////////////////////////////////////////////////////////
底层驱动
初始化配置表
硬件抽象层
GPIO抽象化
中间交换层
接收缓存区设置
变量标志位
系统任务调用层
基本检测测试任务
软件协议处理任务
硬件控制处理任务
DEBUG调试模式
Linux一样打印系统运行时间
/////////////////////////////////////////////////////////////////////////

编程代码前遵循结构设计,大体分为三部分,底层驱动,硬件抽象层,系统任务调用层,程序设计按照这部分来进行设计。
底层驱动:
底层驱动是对应相应的MCU而制定的,与MCU的库函数,开发环境搭建有关,底层驱动是将项目所需要的功能进行一系列的初始化,并将基础的功能封装成一个个函数供顶层任务层调用。以STM32为例,底层驱动设计框架如下图所示。
file:///C:/Users/123/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg
初始化配置表
配置表的设置需要根据项目的需求而添加,例如系统初始化WiFi模块时需要配置IP地址,RTC时间初始化需要给定一个具体时间等,这些可以通过写一个初始化配置表,在初始化驱动的时候将里面的值传进驱动的代码中。例如
//网络配置表struct Network_status Dev_Network_status = {   
.u8gateway          = "192.168.1.1",   
.u8DevIP_addr       = "192.168.1.101",   
.u8Target_host_addr = "192.168.1.100",   
.u32port                = 9090,   
.u8net_mask         = "255.255.255.0",};
//RTC时间配置表
struct RTC_Timer Dev_RTC_Timer_config = {     
.u32Year           = 2016,     
.u32Month          = 7,         
.u32Day            = 15,            
.u32Hour           = 23,          
.u32Minute         = 45,        
.u32Second         = 30,        
.u32DayOfWeek      = RTC_SATURDAY,     
.u32TimeScale      = RTC_CLOCK_24,        };
硬件抽象层
硬件抽象层将任务与底层驱动区分开来,将硬件驱动功能抽象化,把底层硬件抽象成操作函数,供系统层调用
GPIO抽象化
在底层,GPIO模块子程序中将GPIO管脚配置的函数赋值给probe结构体,然后调用硬件抽象层获取函数,使得硬件抽象层获得驱动信息。
底层GPIO.c部分代码。
static void Dev_GPIO_Set_Value(const uint8_t Lock_number,const uint8_t value);
static uint8_t Dev_GPIO_Detect_Value(const uint8_t Lock_number);
void Dev_GPIO_probe(void *PGPIO_struct)
{        …………………………………………………..        
PGPIO_info->GPIO_Set_Value = Dev_GPIO_Set_Value;    //底层驱动接口        
PGPIO_info->GPIO_Detect_Value = Dev_GPIO_Detect_Value;         
Get_Dev_gpio_info(PGPIO_info);                     
//硬件抽象层获取底层驱动资源配置      
………………………………………………}·         1·         2·         3·         4·         5·         6·         7·         8·         9·         10·         11·         12·         13·         14
硬件抽象层将获取的资源封装在函数里面供系统任务调用
硬件抽象层System_Hardware.c部分代码
static struct GPIO_data *PDirver_to_GPIO_data = NULL;
void Get_Dev_gpio_info(void *PDev_gpio_info)            //获取底层驱动资源函数
{   
PDirver_to_GPIO_data = (struct GPIO_data *)PDev_gpio_info;
}
void gpio_set_value(const uint8_t gpio,const uint8_t value)     //任务调用层调用函数
//从此感觉写代码就是写文章,若是你的代码让别人看着没有一些惊喜,一些巧妙的地方。那读起来不过是大白话,没有任何艺术可言本人的LED灯函数:请大家批评指针
使用了滴答定时器作为系统任务总的时间控制
//LED 流动驱动函数(三个LED灯的参数)
//参数 LED 使能参数
//led0_ms  LED0 的延时时间
//例如  110   LED2使能 LED1使能 LED0失能
void LED_drive_Poll(unsigned char LED_enable,int led0_ms,int led1_ms,int led2_ms)
{        
      static unsigned int LED0_time_ms = 0;    //记录LED0任务时间  
      static unsigned int LED0_time_s = 0;         
      static unsigned int LED1_time_ms = 0;    //记录LED1任务时间  
      static unsigned int LED1_time_s = 0;         
     static unsigned int LED2_time_ms = 0;    //记录LED2任务时间  
     static unsigned int LED2_time_s = 0;         
     static unsigned char i = 1;               
if(LED_enable <= 7)  //参数判断        
{               
   if(i == 1)     //只获取第一次               
   {                       
     LED0_time_s = TASK_time_s;      //获取时间戳                        
     LED0_time_ms = TASK_time_ms;                       
     LED1_time_s = TASK_time_s;      //获取时间戳                        
     LED1_time_ms = TASK_time_ms;                        
     LED2_time_s = TASK_time_s;      //获取时间戳                        
     LED2_time_ms = TASK_time_ms;                       
     i++;               
   }               
  if(LED_enable & 0x01)   //LED0使能               
  {                        
      if((TASK_time_s*1000+TASK_time_ms) - (LED0_time_s*1000 + LED0_time_ms) >= led0_ms)  //是延时时间的整倍数                       
       { //延时之后                                
          LED0 = !LED0;   //延时时间到就取反                                       
         //重新获取时间戳                          
          LED0_time_s = TASK_time_s;      //获取时间戳                          
          LED0_time_ms = TASK_time_ms;                        
       }               
   }               
   if(LED_enable & 0x02)   //LED1使能               
   {                       
       if((TASK_time_s*1000+TASK_time_ms) - (LED1_time_s*1000 + LED1_time_ms) >= led1_ms)  //是延时时间的整倍数                        
        { //延时之后                              
           LED1 = !LED1;   //延时时间到就取反                                       
          //重新获取时间戳                        
           LED1_time_s = TASK_time_s;      //获取时间戳                          
           LED1_time_ms = TASK_time_ms;                        
        }                                       
    }                        
    if(LED_enable & 0x04)   //LED1使能               
    {                        
        if((TASK_time_s*1000+TASK_time_ms) - (LED2_time_s*1000 + LED2_time_ms) >= led2_ms)  //是延时时间的整倍数                        
          { //延时之后                                
             LED2 = !LED2;   //延时时间到就取反                                       
             //重新获取时间戳                          
             LED2_time_s = TASK_time_s;      //获取时间戳                        
             LED2_time_ms = TASK_time_ms;                        
          }                                       
     }        
  }        
}
以下为滴答设置与中断
unsigned int TASK_time_ms = 0;    //任务总时间
unsigned int TASK_time_s = 0;   //滴答定时器时钟配置初始化
void systick_init()   //滴答定时器时钟初始化
{        //if(SysTick_Config(SystemFrequency/100000))               
    if(SysTick_Config(SystemCoreClock/1000))   //1ms中断一次               
    {                        
         //ERROR                        
           while(1);                                       
     }
}
//滴答定时器中断服务函数
//硬件自动重载
void SysTick_Handler(void)
{        
       TASK_time_ms++;        
        if(TASK_time_ms >= 1000)        
        {               
           TASK_time_s++;   //最大为65535S  约为 18个小时               
           TASK_time_ms = 0;        
        }
}
//抛转引玉,因为心有所悟,感觉自己的境界稍微进步,请批评指针(打了半天空格!)

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

6

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
196
金钱
196
注册时间
2017-7-18
在线时间
31 小时
发表于 2018-8-27 09:39:15 | 显示全部楼层
回复 支持 反对

使用道具 举报

3

主题

67

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
5009
金钱
5009
注册时间
2014-10-24
在线时间
980 小时
发表于 2018-8-27 10:34:00 | 显示全部楼层
这种延时方式一直在用,很不错的。
回复 支持 反对

使用道具 举报

6

主题

315

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1669
金钱
1669
注册时间
2018-1-29
在线时间
160 小时
发表于 2018-8-27 10:36:25 | 显示全部楼层
我要向楼主学习!
回复 支持 反对

使用道具 举报

37

主题

204

帖子

0

精华

高级会员

Rank: 4

积分
717
金钱
717
注册时间
2016-6-10
在线时间
191 小时
 楼主| 发表于 2018-8-27 15:24:38 | 显示全部楼层

学习嘛
回复 支持 反对

使用道具 举报

37

主题

204

帖子

0

精华

高级会员

Rank: 4

积分
717
金钱
717
注册时间
2016-6-10
在线时间
191 小时
 楼主| 发表于 2018-8-27 15:26:38 | 显示全部楼层
小小的爱 发表于 2018-8-27 10:34
这种延时方式一直在用,很不错的。

在没有操作系统的情况下,用这种方式调整任务之间的运行权限还是挺好的
回复 支持 反对

使用道具 举报

22

主题

79

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
246
金钱
246
注册时间
2018-8-1
在线时间
43 小时
发表于 2018-8-27 18:24:02 | 显示全部楼层
systick延时实用
回复 支持 反对

使用道具 举报

17

主题

587

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4467
金钱
4467
注册时间
2013-6-27
在线时间
565 小时
发表于 2018-8-27 19:55:46 | 显示全部楼层
赋予变量一个调用周期时间,可以达到延时计数;赋予函数一个调用周期时间,功能会更加强大
让我们的思维驾驭在电的速度之上!
回复 支持 反对

使用道具 举报

17

主题

587

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4467
金钱
4467
注册时间
2013-6-27
在线时间
565 小时
发表于 2018-8-27 19:56:02 | 显示全部楼层
赋予变量一个调用周期时间,可以达到延时计数;赋予函数一个调用周期时间,功能会更加强大
让我们的思维驾驭在电的速度之上!
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-7-11 03:09

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表