本帖最后由 suncat0504 于 2022-8-4 19:10 编辑
#申请原创#
@21小跑堂
在上次测试驱动步进马达的基础上,把相关部件组装起来,装置如下图: 整个装置分为两层: 上层电源板提供整个电路的电源、以及光强采集用太阳能板。光强采集由一小片太阳能板完成,在明亮状态下,可以采集到0.5V左右的电压。这个电压在PIC18F57Q43的ADC的处理下,得到300左右的数值,不需要增加放大器就能处理了。电源由两个18650电池,经7805稳压后提供给主电路板。 下层电路板由PIC18F57Q43评估板、步进电机驱动板、步进电机组成。因为太阳能电池板要连线到评估板上,为了避免连接线缆产生缠绕,把太阳能电池和步进电机绑定到一起,因此把步进电机倒置,让电机转动轴固定到底板上,把整个两层电路装置悬空。 为了简化测试,太阳能板被固定为一个倾角,因此本次测试没有对倾角进行控制。 程序处理以以下逻辑进行处理: 主程序循环中: 1、采集当前位置的光强电压信号,完成指定组数的ADC数据做平均值处理,这样得到的数据和最大值比较,从而得到新的最大值并保存。 2、驱动电机步进一个角度,然后按照步骤1继续处理,刷新最大值。 3、当太阳板完成360度的旋转后,我们得到了这一圈中光强电压最大的数据。 4、在3的基础上,重新旋转,同时按照1重新获取对应位置的光电转换数据,并和3中的最大值对比,数值接近的就视为光强最大的检测角度,停止转动。 主程序代码如下: // 步进电机正反转标志
extern uint8_t fb_flag;
// 预定步进数
extern uint16_t plan_step;
// 总步数(用来定位最强的位置)
volatile uint16_t step_all=0;
// ADC最大值
uint16_t adc_max=0;
/*
Main application
*/
void main(void) {
// ADC转换用高、低8位数据
uint8_t resultHigh=0;
uint8_t resultLow=0;
uint16_t result=0;
double avrs[ADC_AVR_CNT];
uint8_t idx=0;
uint16_t avr_result=0; // 本次采集平均值
// Initialize the device
SYSTEM_Initialize();
//Setup ADC
ADCON0bits.FM = 1; //right justify
ADCON0bits.CS = 1; //ADCRC Clock
ADPCH = 0x00; //指定RA0为模拟数据输入通道
TRISAbits.TRISA0 = 1; //Set RA0 to input
ANSELAbits.ANSELA0 = 1; //Set RA0 to analog
ADCON0bits.ON = 1; //Turn ADC On
// Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable();
int i;
// LED指示
LED0_SetLow();
// 初始化动作,表明程序运转
fb_flag=1;
plan_step=512;
__delay_ms(1000);
fb_flag=2;
plan_step=512;
// LED指示
LED0_SetHigh();
__delay_ms(2000);
fb_flag=0;
// 启动转动,同时收集ADC数据
step_all=0;
fb_flag=1; // 正转方式
idx=0;
// 主循环处理
while (1) {
// 触发ADC转换,并取得转换结果
ADCON0bits.GO = 1; //Start conversion
while (ADCON0bits.GO); //Wait for conversion done
resultHigh = ADRESH; //Read result
resultLow = ADRESL; //Read result
// 合成为16位数据
result=resultHigh;
result=result*256 + resultLow;
// 计算平均值,并存储
avrs[idx]=result/ADC_AVR_CNT;
idx=(idx+1)%ADC_AVR_CNT;
if (idx==0) {
// 数据初始化
avr_result=0;
// 满指定组数据了,求出平均结果
for (i = 0; i < ADC_AVR_CNT; i++) {
avr_result=avr_result+avrs[i];
}
if (avr_result > adc_max && step_all < STEPS_CYCLE) {
adc_max = avr_result;
}
if (step_all >= STEPS_CYCLE && fb_flag>0) {
step_all=STEPS_CYCLE; // 避免累加溢出
printf("Complete a lap.Search max.\r\n"); //
// 超过1圈了,反转找最大值位置
fb_flag=2;
plan_step=STEPS_1DU;
//if (avr_result > adc_max-10 && avr_result < adc_max+10) {
if (avr_result > adc_max-10) {
// 找到大值,停止。过指定时间之后,再重新定位最大值 TODO
printf("Finded max-pointer.\r\n"); //
LED0_SetLow();
fb_flag=0;
plan_step=0;
}
} else {
printf("adc=%d, max=%d, step_all=%d\r\n", avr_result, adc_max, step_all); //
// 转一圈,找最大值
plan_step=STEPS_1DU;
}
}
}
}
定时器1中断处理代码如下: // 定时器1中断,1ms一次
void TMR1_DefaultInterruptHandler(void){
uint8_t next_step=0;
// add your TMR1 interrupt custom code
// or set custom function using TMR1_SetInterruptHandler()
// 交替LED作为指示
//LATFbits.LATF3 = ~LATFbits.LATF3;
if (fb_flag>0 && plan_step>0) {
// 允许转动,并且预定了步进步数的场合,启动转动
// 每间隔固定时间让步进马达走一步
timer1_int_cnt++;
if (timer1_int_cnt==step_period_ms) {
timer1_int_cnt=0;
if (fb_flag==1) {
// 正转场合,取得本次步进数据的数据索引
stepmotor_pos=(stepmotor_pos+1)%STEP_MAX;
} else if (fb_flag==2) {
// 反转场合,取得本次步进数据的数据索引
stepmotor_pos=(stepmotor_pos+STEP_MAX-1)%STEP_MAX;
}
// 取得本次步进用时序数据
next_step=F_Rotation[stepmotor_pos];
// 执行一次步进
LATF = (LATF & 0x0F) | next_step;
// 预定步进总数减一
plan_step--;
// 总步数加1
step_all++;
}
} else {
timer1_int_cnt=0;
LATF = LATF & 0x0F;
}
}
测试时按照两种模式: 1、连接电脑测试,通过串口监测中间处理数据,检查程序逻辑。 2、离线测试 ①在1的测试OK的情况下,把整个装置放置在正常光线下测试。 ②在正常光线下,在与正常光强最强角度不同的某一个角度上利用强光LED手电筒,设置为最强角度,测试装置是否能停留在这个角度。 经过这三种简单的测试,证明了程序可以使装置转到最佳光强位置。实际应用中,可以利用这种思路动态调整太阳能在充电装置,保证在任意时刻找到最合理的角度完成太阳能到电能的转换。
project.zip
(1007.48 KB)
|