(软件01)时基处理,单片机一种不错的代码思路

本文目录

  • 软件学习前言
  • 代码思路
  • 实操练习

软件学习前言

        今天和大家分享一下我的软件学习心得,刚毕业那会,我只能写简单写点51的C语言代码,功能无非是那些按键、数码管、LED灯这块,也不注重代码的可读性,多次编译、烧录、看效果、再调整、最终拼拼凑凑地把功能实现了就行了。16年参加工作后的第一年,我接触到了用STM32F103C8T6做的一些产品,开始接触到32位单片机,第一感觉是编译环境好麻烦呀,点了编译按键总是报一些很奇怪的错误,不是软件代码的那一类的,更多的是找不到XXXX文件这种。后来也是在同事的帮忙下一一地将头文件关联,包含路径,到后来还是慢慢修改调试,才能把这一套的编译环境给配好,到了代码端的编程。ARM单片机和51单片机相比,复杂了很多,就拿GPIO配置来说,51的直接就可以让IO输出高低电平,但ARM单片机的话,需要对管脚进行配置,时钟使能这些步骤。在写应用函数的时候也是不怎么注重时序,都是在while循环中加延时或等待按键释放这些操作,单片机的资源浪费得严重。后来随着接触的深入,开始了解到变量标志位这种方法,再到后来的17年,接触到现在仍很喜欢使用的定时器时基处理法,也是本文要正式介绍给大家的一直方法。后面其实也有了解到uCOSII这种操作系统(只跑通了demo功能),后面的工作中也使用过FreeRTOS操作系统,其实原理都是差不多的,创建任务,设置优先级,通过全局变量互相调用这类。每个任务都是单独的函数,代码可读性也还不错,这些便捷性的东西都是建立在单片机资源足够的基础上,另外的,跑系统单片机的网上教学资源也是较少的,裸机编程的话,我还是推荐大家使用时基处理法,让大家的裸机编程变得更简单高效。

(一张图片进入今天的分享)

代码思路

        定义一个全局变量uint64_t  gTimeBase ;     

        配置定时器,让定时器每ms产生一次中断

        定时器中断后,清中断标记,gTimeBase ++;

        定义多个时刻的变量

        举例:

        TimeBaseMs、TimeBase10ms、TimeBase50ms、TimeBase100ms、TimeBase500ms、TimeBase1000ms、TimeBase5000ms、TimeBase6000ms、TimeBase10000ms、TimeBase60000ms

        在while循环中开始获取全局变量的值,一开始,当gTimeBase  自增到10时,满足gTimeBase  - TimeBase10ms >9 (10-0=10)的时候,我们给TimeBase10ms变量+10,然后执行一次10ms时基处理函数TimeProcess_10MS();由此可见,当gTimeBase  自增到20时,又满足gTimeBase  - TimeBase10ms >9 (20-10=10),又会执行一次0ms时基处理函数TimeProcess_10MS();其他的时基也是如此类推,我们就可以准确地知道每10ms我们要处理一次什么函数,每50ms、100ms、1000ms.....等时间到了之后我们该做什么了。

        这个时候我们就可以在10ms中处理我们的按键扫描函数了,按下后,按键计数变量+1,加到2的时候还处于按下,那么这个2*10ms就是我们知道的按键防抖时间。

        每到50ms我们处理一下软件看门狗的喂狗操作。

        每5秒切换一下LED灯的亮灭状态。我每次都喜欢这样操作,一边手按在手机的秒表计时器开始上,一边等待灯熄灭,看到灯熄灭了,开始计时,等灯亮起来的时候我再点击手机上秒表计时器的暂停键,观察记录的时间是否为5秒,从而来判断的我时基配置是否准确。      

        在while循环中每次获取全局变量时,我们也可以执行一个Process();函数,如此,也相当于有一个接近while循环的处理了,比较关键的处理放在这里执行。

实操练习

        就以我刚刚举例的LED灯控制来实操吧。

        单片机硬件平台为雅特力AT32F421C8T7,库函数和STM32有一点点不同,不过大同小异。

       头文件zj_public.h

//时基定时器
#define BSP_TIME_BASE_TIMER  			TMR6
#define BSP_TIME_BASE_TIMER_ARR  		10-1
#define BSP_TIME_BASE_TIMER_PSC  		7200-1
#define BSP_TIME_BASE_IRQN  			TMR6_GLOBAL_IRQn
#define BSP_TIME_BASE_IRQN_LEVEL  		0
#define BSP_TIME_BASE_IRQN_HANDLER  	TMR6_GLOBAL_IRQHandler

//LED灯
#define BSP_LED_RUN_GPIO_PIN      		GPIO_Pins_0
#define BSP_LED_RUN_GPIO_PORT     		GPIOA
#define BSP_LED_RUN_ON      			GPIO_ResetBits(BSP_LED_RUN_GPIO_PORT, BSP_LED_RUN_GPIO_PIN)
#define BSP_LED_RUN_OFF      			GPIO_SetBits(BSP_LED_RUN_GPIO_PORT, BSP_LED_RUN_GPIO_PIN)

//时基全局变量
extern volatile uint64_t gTimeBase;


void zj_bsp_config(void);

void zj_app_timebase_process(void);
void zj_app_timebase_1ms_process(void);

   配置函数zj_bsp.c

#include "zj_public.h"


volatile uint64_t gTimeBase = 0;

void RCC_Configuration(void)
{   
    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_TMR6, ENABLE);//时基
}

void GPIO_Configuration(void)
{
    GPIO_InitType GPIO_InitStructure;
	GPIO_StructInit(&GPIO_InitStructure);

	GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
	//灯
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OutType = GPIO_OutType_PP;
	GPIO_InitStructure.GPIO_Pins = BSP_LED_RUN_GPIO_PIN;
    GPIO_Init(BSP_LED_RUN_GPIO_PORT, &GPIO_InitStructure); 
	BSP_LED_RUN_ON;
}


void NVIC_Configuration(void)
{
  NVIC_InitType NVIC_InitStructure;

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

	// 0 TIMER6_TIMEBASE 
  NVIC_InitStructure.NVIC_IRQChannel = BSP_TIME_BASE_IRQN;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = BSP_TIME_BASE_IRQN_LEVEL;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void TIMER_BASE_Configuration(TMR_Type * mTimer ,u16 mArr, u16 mPsc)
{
  TMR_TimerBaseInitType TIM_TimeBaseStructure;
  
  //定时器初始化
  TMR_Reset(mTimer);
  TIM_TimeBaseStructure.TMR_Period = mArr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 
  TIM_TimeBaseStructure.TMR_DIV = mPsc; //设置用来作为TIMx时钟频率除数的预分频值
  TIM_TimeBaseStructure.TMR_ClockDivision = TMR_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
  TIM_TimeBaseStructure.TMR_CounterMode = TMR_CounterDIR_Up; //TIM向上计数模式
  TMR_TimeBaseInit(mTimer, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
	
  TMR_ClearITPendingBit(mTimer, TMR_INT_Overflow);
  TMR_INTConfig(mTimer, TMR_INT_Overflow, ENABLE);
  TMR_Cmd(mTimer, ENABLE); //使能TIMx 
}


void BSP_TIME_BASE_IRQN_HANDLER(void)
{
  if (TMR_GetINTStatus(BSP_TIME_BASE_TIMER, TMR_INT_Overflow) != RESET)//是更新中断
  {
    TMR_ClearITPendingBit(BSP_TIME_BASE_TIMER, TMR_INT_Overflow); //清除TIM更新中断标志 
	
    zj_app_timebase_1ms_process();
  }
}

void zj_bsp_config(void)
{
  SystemCoreClockUpdate();

  RCC_Configuration();      
  NVIC_Configuration();
  GPIO_Configuration();

TIMER_BASE_Configuration(BSP_TIME_BASE_TIMER,BSP_TIME_BASE_TIMER_ARR,BSP_TIME_BASE_TIMER_PSC);
}

    时基处理函数

#include "zj_public.h"


uint64_t TimeBaseMs=0,TimeBase10ms=0,TimeBase50ms=0,TimeBase100ms=0,TimeBase500ms=0,TimeBase1000ms=0,TimeBase5000ms=0,TimeBase6000ms=0,TimeBase10000ms=0,TimeBase60000ms=0;

static uint64_t Time_GetTimeMs(void)
{
    return gTimeBase;
}

static void Process(void)
{

}

void zj_app_timebase_1ms_process(void)
{
	gTimeBase++;
}
	
static void TimeProcess_10MS(void)
{

}
static void TimeProcess_50MS(void)
{

}
static void TimeProcess_100MS(void)
{

}
static void TimeProcess_500MS(void)
{
}                  
static void TimeProcess_1000MS(void)
{
}
static void TimeProcess_5000MS(void)
{   
	static uint8_t sToggleFlag = 0;

	if(sToggleFlag)
	{
		sToggleFlag = FALSE;
		
		BSP_LED_RUN_ON;
	}
	else
	{
		sToggleFlag = TRUE;
	
		BSP_LED_RUN_OFF;
	}
}
static void TimeProcess_10000MS(void)
{   
}
static void TimeProcess_60000MS(void)
{   
}



void zj_app_timebase_process(void)
{
	
  Process();
  TimeBaseMs=Time_GetTimeMs();
 
  if(((TimeBaseMs-TimeBase10ms))>9)//10ms
  {
    TimeBase10ms+=10;
    TimeProcess_10MS();
  }
  if(((TimeBaseMs-TimeBase50ms))>49)//50ms
  {
    TimeBase50ms+=50;
    TimeProcess_50MS();
  }
  if(((TimeBaseMs-TimeBase100ms))>99)//100ms
  {
    TimeBase100ms+=100;
    TimeProcess_100MS();
  }
  if(((TimeBaseMs-TimeBase500ms))>499)//500ms
  {
    TimeBase500ms+=500;
    TimeProcess_500MS();
  }
  if(((TimeBaseMs-TimeBase1000ms))>999)//1s
  {
    TimeBase1000ms+=1000;
    TimeProcess_1000MS();
  }
  if(((TimeBaseMs-TimeBase5000ms))>4999)//5s
  {
    TimeBase5000ms+=5000;
    TimeProcess_5000MS();
  }
  if(((TimeBaseMs-TimeBase10000ms))>9999)//10s
  {
    TimeBase10000ms+=10000;
		TimeProcess_10000MS();
  }
  if(((TimeBaseMs-TimeBase60000ms))>59999)//60s
  {
    TimeBase60000ms+=60000;
		TimeProcess_60000MS();
  }
}

主函数main.c

#include "zj_public.h"


int main(void)
{  
  zj_bsp_config();

  while(1)
  {
	zj_app_timebase_process();
  }
	
}

        代码中特别注重了通用性的问题,时基这块应用完全可以移植到所有的单片机中去。烧录运行后就可以按到灯按照亮5秒灭5秒交替地运行了。

        volatile,提一下这个关键字吧,修饰变量是不稳定的意思,用法就是确保访问这个变量时不会被其他的优化或改写,确保数据准确,很适合这类毫秒级别访问的变量。

小弟感谢大家的关注!

      (利他之心,原创分享)

  • 33
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BEXZJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值