在STM32控制的智能猫砂盆项目中的问题总结

主控芯片:STM32F103RCT6
编程软件:keil5;sourseinsight4
串口调试工具:XCOM V2.5

1定时器问题

1.1有关于UCOSIII无法进入软件定时器的回调函数且不报错的情况

发生这种情况的原因是由于定义ucosiii自带的软件单次定时器错误的时间设置导致的,如下所示:

图中有两个OS_TICK类型,单次定时器的定时时间应设置在第一个OS_TICK上。上面的是初始定时时间设置,可用于单次定时器周期定时器中;下面的是重装载时间设置,仅用于周期定时器中。(之后在周期定时器的设置时将两个参数都设置为0也无法正确触发)

//错误代码
OSTmrCreate((OS_TMR		*)&tmra_20s,			//定时器tmra_20s
            (CPU_CHAR	*)"tmra_20s",			//定时器名字
            (OS_TICK	 )0,					//0ms
            (OS_TICK	 )200,		            //2000*10=20000ms=20s
            (OS_OPT		 )OS_OPT_TMR_ONE_SHOT,  //单次定时器
            (OS_TMR_CALLBACK_PTR)tmra_20s_callback,//定时器回调函数
            (void	    *)0,					//参数为0
            (OS_ERR	    *)&err);				//返回的错误码
//正确代码
OSTmrCreate((OS_TMR		*)&tmra_20s,			//定时器tmra_20s
            (CPU_CHAR	*)"tmra_20s",			//定时器名字
            (OS_TICK	 )200,	           	    //2000*10=20000ms=20s
            (OS_TICK	 )0,					//0ms
            (OS_OPT		 )OS_OPT_TMR_ONE_SHOT,  //单次定时器
            (OS_TMR_CALLBACK_PTR)tmra_20s_callback,//定时器回调函数
            (void	    *)0,					//参数为0
            (OS_ERR	    *)&err);				//返回的错误码

以下是源代码中对于ucosiii软件定时器的描述。

************************************************************************************************************************
*                                                   CREATE A TIMER
*
* Description: This function is called by your application code to create a timer.
*
* Arguments  : p_tmr           Is a pointer to a timer control block
*
*              p_name          Is a pointer to an ASCII string that is used to name the timer.  Names are useful for
*                              debugging.
*
*              dly             Initial delay.
*                              If the timer is configured for ONE-SHOT mode, this is the timeout used
*                              If the timer is configured for PERIODIC mode, this is the first timeout to wait for
*                              before the timer starts entering periodic mode
*
*              period          The 'period' being repeated for the timer.
*                              If you specified 'OS_OPT_TMR_PERIODIC' as an option, when the timer expires, it will
*                              automatically restart with the same period.
*
*              opt             Specifies either:
*
*                                  OS_OPT_TMR_ONE_SHOT       The timer counts down only once
*                                  OS_OPT_TMR_PERIODIC       The timer counts down and then reloads itself
*
*              p_callback      Is a pointer to a callback function that will be called when the timer expires.  The
*                              callback function must be declared as follows:
*
*                                  void  MyCallback (OS_TMR *p_tmr, void *p_arg);
*
*              p_callback_arg  Is an argument (a pointer) that is passed to the callback function when it is called.
*
*              p_err           Is a pointer to an error code.  '*p_err' will contain one of the following:
*
*                                 OS_ERR_NONE
*                                 OS_ERR_ILLEGAL_CREATE_RUN_TIME if you are trying to create the timer after you called
*                                                                  OSSafetyCriticalStart().
*                                 OS_ERR_OBJ_CREATED             if the timer has already been created
*                                 OS_ERR_OBJ_PTR_NULL            is 'p_tmr' is a NULL pointer
*                                 OS_ERR_OBJ_TYPE                if the object type is invalid
*                                 OS_ERR_OPT_INVALID             you specified an invalid option
*                                 OS_ERR_TMR_INVALID_DLY         you specified an invalid delay
*                                 OS_ERR_TMR_INVALID_PERIOD      you specified an invalid period
*                                 OS_ERR_TMR_ISR                 if the call was made from an ISR
*
* Returns    : none
*
* Note(s)    : 1) This function only creates the timer.  In other words, the timer is not started when created.  To
*                 start the timer, call OSTmrStart().
************************************************************************************************************************

如果还有疑问可以参考详细的关于定时器用法的中文描述:
UCOSIII软件定时器的使用

1.2有关于UCOSIII打开定时器之后不能按时进入回调函数的问题

在此智能猫砂盆项目中,由于设计需求,上电以后要持续对目标进行称重,从而获得如下信息:①猫进入猫砂盆②猫离开猫砂盆③猫的重量④猫屎的重量⑤猫砂的重量。
原定计划在主任务函数中直接打开一个UCOSIII的软件周期定时器,预计以500ms为一个周期持续称重。而在实际调试过程中,则发现原有的其他软件定时器不能按时触发其自身的回调函数,而当我调整持续定时器的周期时,发现称重软件定时器周期越长,其他定时器回调速度越趋于正常,由此,判断称重软件定时器占用软件定时器的资源过多,另开了一个任务函数专门进行称重,测试以后正常运行。
由此,我愈发感到嵌入式软件工程师要对软件硬件的资源有具体的了解,才能更好地利用有限的资源。

2PWM问题

2.1有关于UCOSIII同一定时器的不同LINE的PWM输出不能共存的情况

发生这种情况的原因是由于定义初始化函数时加入了TIM_DeInit()函数导致的,如下所示:

	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	
	TIM_DeInit(TIM4);//把TIM4的设置清空
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); 
	
	//设置该引脚为复用输出功能,输出TIM4,CH3的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure); 

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值	
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性:TIM输出比较极性高	
	TIM_OC3Init(TIM4, &TIM_OCInitStructure); //初始化外设TIM4
	TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);//CH3预装载使能 
	TIM_Cmd(TIM4, ENABLE);	//使能TIM3
	TIM_SetCompare3(TIM4, 0);
*****************以下为部分TIM_DeInit()函数代码**********************************
void TIM_DeInit(TIM_TypeDef* TIMx)
{
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx)); 
 
  if (TIMx == TIM1)
  {
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, ENABLE);
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, DISABLE);  
  }     

TIM_DeInit(TIM4)会将其他初始化函数中对于TIM4的设置清空,导致我们设置的其他通道的PWM设置被清空,因而无法在TIM4中输出2个PWM信号。

经过此次问题,我明白了每一个函数都要好好看清楚具体的功能作用,不能一抄了之,写初始化函数的时候需要保证每一句都明白它的功能作用,不然会在后续的调试过程中产生隐患。

2.2有关于PWM关闭之后电平不一的问题

在实际的使用过程中,我发现TIM_Cmd(TIMX, DISABLE)仅仅只是关闭了PWM的产生,而并不是将PWM波拉低,PWM的高低决定于关闭前一刻电平的高低,因此,想要得到恒定的高电平或者低电平,可以考虑2种方法:
①利用TIM_SetCompareX()//X为通道编号,例如CH3通道X为3(X为通道编号,例如CH3通道X为3)将占空比调整到100% 或者 0%,这种方法无需关闭PWM,之后还可以调整占空比。
②关闭PWM并强制输出高低电平,这种方法要关掉PWM,打开PWM时需要重新使能通道以及PWM模式,但是不需要重新初始化TIM。(此方法参考:STM32 PWM功能在关闭时GPIO电平不确定的情况

//关闭PWM并强制输出低电平
TIM_Cmd(TIM4, DISABLE);//关闭PWM
TIM_ForcedOC3Config(TIM4, TIM_ForcedAction_InActive);//强制输出低电平

//打开TIM4,CH3的PWM,并设置为PWM1模式
TIM_SelectOCxM(TIM4, TIM_Channel_3, TIM_OCMode_PWM1);
TIM_CCxCmd(TIM4, TIM_Channel_3, TIM_CCx_Enable);
TIM_Cmd(TIM4, ENABLE);

3在猫砂项目中称重模块的调试过程

3.1称重模块的校准模式

使用标准10kg砝码进行校准,每隔30ms采集一次ad值,一共采集20个数据,利用冒泡排序取数值大小中间的4个数据,取其平均值,带入计算公式(1) ,得到其称重比例系数mWeightCoe,将其写入空闲的flash当中,多次使用砝码验证过后保存最佳值,最佳值会根据称重模块各有不同。

(1)计算公式:
C = A / ( D − B ) C=A/(D-B) C=A/(DB)

实际重量初始ad值称重系数校准ad值
weight( A )mZeroAdj( B )mWeightCoe( C )count( D )
void adjust_weight(u16 weight)
{
	int i=0;
	u32 count=0,sum=0;
	float w = weight*1000;
	u32 countArray[20]={0};
	for(i=0; i<20;i++)
	{
		delay_ms(30);
		countArray[i] =HX711_Read_AD();
	}
	arrsort(countArray,20);//冒泡排序
	//去掉8个最大的和8个最小的
	for(i=8;i<12;i++){
		sum+=countArray[i];
	}
	myfree(countArray);
	count = sum/4;
	printf("count:%d\n",count);
	mWeightCoe = w/(count-mZeroAdj);
	printf("mWeightCoe:%f Read_Weight:%d\n",mWeightCoe,HX711_Read_Weight()); 
	STMFLASH_Write(INFO_FLASH_ADDR, (u16*)&mWeightCoe, 2);
}

3.2称重模块的工作模式

由于本项目要求使用红外传感器HC_SR501来感应猫的进入作为安全机制,而此模块需要有1分钟左右的初始化时间,因此在代码中有一个屏蔽红外的标志量以及一个800毫秒的延时。
而称重部分考虑到猫作为一个活物所以判断条件是以3次称重都大于500g(不含猫砂)判断猫的进入,3次称重小于500g(不含猫砂)判断猫的离开,同时能称出猫的体重以及猫屎的重量。猫的体重由于考虑到猫的活动,所以利用方差选取相对平稳的体重作为猫的真实体重。

void weight_task(void * p_arg)
{
	重量(g)///
	int a_weight=0;
	int b_weight=0;
	int c_weight=0;
	int fangcha;
	OS_ERR err;
	//int类型重量///
	u32 time_num= 0;

	while(1)
	{
		///计时60s左右屏蔽人体红外热释电初始化///
		if(time_num<=80)
		{
			time_num++;
		}
		if(time_num>80)
		{
			mDeviceStatus.infrareCatWork=1;
		}

		if(mDeviceStatus.workingStatus==WORKING_IDLE){
			c_weight = b_weight;
			b_weight = a_weight;
			a_weight = HX711_Read_Weight_2();
			if(a_weight<-800)
			{
				mDeviceStatus.potStatus=1;//盆在
			}else{
				mDeviceStatus.potStatus=0;//盆不在
			}
			if(mDeviceStatus.catWeightStatus ==WEIGHT_OUT &&
			  (a_weight > mDeviceStatus.litterWeight + 500 && 
			  b_weight > mDeviceStatus.litterWeight + 500&&
			  c_weight > mDeviceStatus.litterWeight + 500))
			{
				mDeviceStatus.catWeightStatus=WEIGHT_IN;
				printf("猫进入\n");
				mDeviceStatus.poopTime=0;
				OSTmrStart(&poop_time, &err);
			}
			if(mDeviceStatus.catWeightStatus==WEIGHT_IN){
				fangcha = pow(a_weight-b_weight,2)+pow(a_weight-c_weight,2)+pow(b_weight-c_weight,2);
				if(fangcha<50){
					Cat_In_Weight= b_weight - mDeviceStatus.litterWeight;
					printf("猫重:%d\n",Cat_In_Weight);
				}
			}
			if(mDeviceStatus.catWeightStatus==WEIGHT_IN &&
				(a_weight < mDeviceStatus.litterWeight + 500 &&
				b_weight < mDeviceStatus.litterWeight + 500&&
				c_weight < mDeviceStatus.litterWeight + 500)){
				mDeviceStatus.catWeightStatus = WEIGHT_OUT;
				if(a_weight>mDeviceStatus.litterWeight)
					mDeviceStatus.poopWeight = a_weight - mDeviceStatus.litterWeight;
				else
					mDeviceStatus.poopWeight=0;
				
				OSTmrStart(&cat_out_wait,&err);//去铲屎
				OSTmrStop(&poop_time,OS_OPT_TMR_NONE,0,&err);
				printf("猫离开,如厕时长%.1f 屎重:%d\n",mDeviceStatus.poopTime,mDeviceStatus.poopWeight);
			}
		}else{
			delay_ms(800);
		}	
	}
}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值