主控芯片: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/(D−B)
实际重量 | 初始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);
}
}
}