
There is a small interactive gift at the end of the article, don’t miss it❤️



import sensor, image, time, math,pybfrom pyb import UART
sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.QQVGA) # we run out of memory if the resolution is much bigger...sensor.skip_frames(time = 2000)sensor.set_auto_gain(False) # must turn this off to prevent image washout...sensor.set_auto_whitebal(False) # must turn this off to prevent image washout...clock = time.clock()uart = UART(3, 115200)#Serial port baud rate
f_x = (2.8 / 3.984) * 160 # find_apriltags defaults to this if not setf_y = (2.8 / 2.952) * 120 # find_apriltags defaults to this if not setc_x = 160 * 0.5 # find_apriltags defaults to this if not set (the image.w * 0.5)c_y = 120 * 0.5 # find_apriltags defaults to this if not set (the image.h * 0.5)
def degrees(radians):return (180 * radians) / math.pi
while(True): clock.tick() img = sensor.snapshot()for tag in img.find_apriltags(fx=f_x, fy=f_y, cx=c_x, cy=c_y): # defaults to TAG36H11 img.draw_rectangle(tag.rect(), color = (255, 0, 0)) img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0)) print_args = (tag.x_translation(), tag.y_translation(), tag.z_translation())#degrees(tag.x_rotation()), degrees(tag.y_rotation()), degrees(tag.z_rotation()))# Translation units are unknown. Rotation units are in degrees.# print("Tx %f, Ty %f, Tz %f" % print_args) uart.write("A%.2f,B%.2f,C%.2f," % print_args+'\r\n')#Set specific format for STM32 to segment and obtain data#pyb.delay(500)# print(clock.fps())



TIM8_PWM_Init(400-1,20-1); //Used to control motors, 168M/20=8.4Mhz counting frequency, reload value is 400, so PWM frequency is 8.4M/400=21Khz. TIM3_PWM_Init(200-1,8400-1);//Used to control servos, 50HZ TIM2_Int_Init(400-1,20-1);//Timer interrupt, 21KHZ TIM7_Int_Init(500-1,8400-1);//Used for encoder counting, 20HZ, interrupt once every 50ms uart_init(115200); //Initialize serial port 1 baud rate to 115200 Encoder_Init_TIM4();//Encoder interface initialization
void uart_init(u32 bound){//GPIO port settings GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //Enable GPIOA clock RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//Enable USART1 clock
//Serial port 1 corresponding pin remapping GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9 remapping to USART1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10 remapping to USART1
//USART1 port configuration GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9 and GPIOA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//Multiplexing function GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //Speed 50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Push-pull multiplexing output GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //Pull-up GPIO_Init(GPIOA,&GPIO_InitStructure); //Initialize PA9, PA10
//USART1 Initialization Settings USART_InitStructure.USART_BaudRate = bound;//Baud rate settings USART_InitStructure.USART_WordLength = USART_WordLength_8b;//Word length is 8 bits data format USART_InitStructure.USART_StopBits = USART_StopBits_1;//One stop bit USART_InitStructure.USART_Parity = USART_Parity_No;//No parity bit USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//No hardware data flow control USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //Receive and transmit mode USART_Init(USART1, &USART_InitStructure); //Initialize serial port 1
USART_Cmd(USART1, ENABLE); //Enable serial port 1
USART_ClearFlag(USART1, USART_FLAG_TC);
#if EN_USART1_RX USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//Enable related interrupts
//Usart1 NVIC configuration NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//Serial port 1 interrupt channel NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//Preemptive priority 3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //Sub-priority 3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ channel enable NVIC_Init(&NVIC_InitStructure); //Initialize VIC register according to specified parameters,
#endif
}
void USART1_IRQHandler(void) //Serial port 1 interrupt service program{ u8 Res;#ifdef OS_TICKS_PER_SEC //If the clock tick count is defined, it indicates that ucosII is to be used. OSIntEnter(); #endifif(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //Receive interrupt (the received data must end with 0x0d 0x0a) { Res =USART_ReceiveData(USART1);//(USART1->DR); //Read the received data
if((USART_RX_STA&0x8000)==0)//Receiving not completed {if(USART_RX_STA&0x4000)//Received 0x0d {if(Res!=0x0a)USART_RX_STA=0;//Reception error, restart else USART_RX_STA|=0x8000; //Reception completed }else //Not yet received 0x0D { if(Res==0x0d)USART_RX_STA|=0x4000;else { USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//Reception data error, restart reception } } } } #ifdef OS_TICKS_PER_SEC //If the clock tick count is defined, it indicates that ucosII is to be used. OSIntExit(); #endif}
#endif
/*Global variables involved float data[3];//Distances in x, y, z directions, in floating point formatunsigned char data_string[3][7];//Distances in x, y, z directions, in string format*/if(USART_RX_STA&0x8000) { //Empty stringfor(i=0;i<2;i++) {for(j=0;j<6;j++) { data_string[i][j]=' '; } } len=USART_RX_STA&0x3fff;//Get the length of the data received this timefor(t=0,j=0;t<len;t++) { j=0;if(USART_RX_BUF[t]=='A') { t++;//Remove the first letterwhile(USART_RX_BUF[t]!=',') { data_string[0][j]=USART_RX_BUF[t]; t++; j++; } }if(USART_RX_BUF[t]=='B') { t++;//Remove the first letterwhile(USART_RX_BUF[t]!=',') { data_string[1][j]=USART_RX_BUF[t]; t++; j++; } }if(USART_RX_BUF[t]=='C') { t++;//Remove the first letterwhile(USART_RX_BUF[t]!=',') { data_string[2][j]=USART_RX_BUF[t]; t++; j++; } } }//Convert string to floating point data[0]=myatof(data_string[0])/100.0;//xdata[1]=myatof(data_string[1])/100.0;//ydata[2]=myatof(data_string[2])*(-1)/100.0;//z, input is negative, convert to positive//USART2 send data// Usart_SendString( RS232_USART, (uint8_t *)USART_RX_BUF );
//LCD update display//Show xyz// CLEAR(10);// Gui_DrawFont_GBK16(30,0,BLACK,WHITE,clear);// Gui_DrawFont_GBK16(30,0,BLACK,WHITE,data_string[0]);// Gui_DrawFont_GBK16(30,20,BLACK,WHITE,clear);// Gui_DrawFont_GBK16(30,20,BLACK,WHITE,data_string[1]);// Gui_DrawFont_GBK16(30,40,BLACK,WHITE,clear);// Gui_DrawFont_GBK16(30,40,BLACK,WHITE,data_string[2]);
USART_RX_STA=0;//Clear the flag bit }}
int myatof(const char *str)//This function is only suitable for floating point numbers with two decimal places, returns the int value multiplied by 100, as float return has errors{int flag = 1;//Indicates positive numberint res =0; u8 i=1; //Two decimal places after the decimal pointwhile(*str != '\0') {if( !(*str >= '0' && *str <= '9'))//Find the first number in the string { str++;continue; }if(*(str-1) == '-') { flag=-1;//Indicates a negative number }
while(*str >= '0' && *str <= '9') { res = res *10 + (*str - '0'); str++; }if(*str == '.') { str++; res = res *10 + (*str - '0'); str++; res = res *10 + (*str - '0');//Keep two digits, so add twicereturn res*flag; }}
void LCD_GPIO_Init(void);void Lcd_WriteIndex(u8 Index);void Lcd_WriteData(u8 Data);void Lcd_WriteReg(u8 Index,u8 Data);u16 Lcd_ReadReg(u8 LCD_Reg);void Lcd_Reset(void);void Lcd_Init(void);void Lcd_Clear(u16 Color);void Lcd_SetXY(u16 x,u16 y);void Gui_DrawPoint(u16 x,u16 y,u16 Data);unsigned int Lcd_ReadPoint(u16 x,u16 y);void Lcd_SetRegion(u16 x_start,u16 y_start,u16 x_end,u16 y_end);void LCD_WriteData_16Bit(u16 Data);
void TFT_Init_Show(void){ Lcd_Clear(WHITE); Gui_DrawFont_GBK16(16,70,BLACK,WHITE,"by WILL CHAN"); delay_ms(1000); Lcd_Clear(WHITE); Gui_DrawFont_GBK16(3,0,RED,WHITE,"X:"); Gui_DrawFont_GBK16(3,20,RED,WHITE,"Y:"); Gui_DrawFont_GBK16(3,40,RED,WHITE,"Z:"); Gui_DrawFont_GBK16(3,60,RED,WHITE,"speed:");}
void Gui_DrawFont_GBK16(u16 x, u16 y, u16 fc, u16 bc, u8 *s){ unsigned char i,j; unsigned short k,x0; x0=x;
while(*s) { if((*s) < 128) { k=*s;if (k==13) { x=x0; y+=16; }else {if (k>32) k-=32; else k=0;
for(i=0;i<16;i++)for(j=0;j<8;j++) {if(asc16[k*16+i]&(0x80>>j)) Gui_DrawPoint(x+j,y+i,fc);else {if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc); } } x+=8; } s++; }
else {
for (k=0;k<hz16_num;k++) {if ((hz16[k].Index[0]==*(s))&&(hz16[k].Index[1]==*(s+1))) { for(i=0;i<16;i++) {for(j=0;j<8;j++) {if(hz16[k].Msk[i*2]&(0x80>>j)) Gui_DrawPoint(x+j,y+i,fc);else {if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc); } }for(j=0;j<8;j++) {if(hz16[k].Msk[i*2+1]&(0x80>>j)) Gui_DrawPoint(x+j+8,y+i,fc);else {if (fc!=bc) Gui_DrawPoint(x+j+8,y+i,bc); } } } } } s+=2;x+=16; }
}}
//Generic timer 2 interrupt initialization//arr: auto reload value.//psc: clock prescaler//Timer overflow time calculation method: Tout=((arr+1)*(psc+1))/Ft us.//Ft=Timer working frequency, unit:Mhz//Here Timer 2 is used!void TIM2_Int_Init(u16 arr,u16 psc){ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); ///Enable TIM2 clock
TIM_TimeBaseInitStructure.TIM_Period = arr; //Auto reload value TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //Timer division TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //Up count mode TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//Initialize TIM3
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //Allow timer 2 update interrupt TIM_Cmd(TIM2,ENABLE); //Enable timer 2
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; //Timer 2 interrupt NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //Preemptive priority 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority=2; //Sub-priority 3 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure);
}
void TIM2_IRQHandler(void){ if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//Overflow interrupt {if(motor_flag==1)//Reverse { TIM_SetCompare1(TIM8,motor_duty*PID_val_motor*400.0);//Compare with the timer's auto reload value to set the duty cycle, pin:PC6 TIM_SetCompare2(TIM8,0); }if(motor_flag==0)//Forward { TIM_SetCompare1(TIM8,0); TIM_SetCompare2(TIM8,motor_duty*PID_val_motor*400.0);//Compare with the timer's auto reload value to set the duty cycle, pin:PC7 } TIM_SetCompare1(TIM3,200-(servo_angle/45.0+1)*5);//Set servo angle, pin:PA6 } TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //Clear interrupt flag}
void TIM8_PWM_Init(u32 arr,u32 psc){ //This part needs manual modification of IO port settings
GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8,ENABLE); //Enable TIM8 clock RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC, ENABLE); //Enable PORTA clock
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIOFA GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //Multiplexing function GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //Speed 100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Push-pull multiplexing output GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //Pull-up GPIO_Init(GPIOA,&GPIO_InitStructure); //Initialize PA7
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8; GPIO_Init(GPIOC,&GPIO_InitStructure);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM8); //GPIOA8 remapping to timer 1 GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM8); //GPIOA9 remapping to timer 1 GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_TIM8); //GPIOA10 remapping to timer 1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM8); //GPIOB13 remapping to timer 1 GPIO_PinAFConfig(GPIOB,GPIO_PinSource0,GPIO_AF_TIM8); //GPIOB14 remapping to timer 1 GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM8); //GPIOB15 remapping to timer 1
TIM_TimeBaseStructure.TIM_Prescaler=psc; //Timer division TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //Up count mode TIM_TimeBaseStructure.TIM_Period=arr; //Auto reload value TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM8,&TIM_TimeBaseStructure);//Initialize timer 1
//Initialize PWM mode TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC1Init(TIM8, &TIM_OCInitStructure); TIM_OC2Init(TIM8, &TIM_OCInitStructure); TIM_OC3Init(TIM8, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM8,TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM8,TIM_OCPreload_Enable); TIM_OC3PreloadConfig(TIM8,TIM_OCPreload_Enable);
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF; TIM_BDTRInitStructure.TIM_DeadTime = 0; TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low; TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; TIM_BDTRConfig(TIM8,&TIM_BDTRInitStructure);
TIM_Cmd(TIM8,ENABLE); TIM_CCPreloadControl(TIM8,ENABLE); TIM_CtrlPWMOutputs(TIM8,ENABLE);
}
void Encoder_Init_TIM4(void){ GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//Enable TIM4 clock RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//Enable GPIOB clock
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);//PB6 pin remapping GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4);//PB7 pin remapping
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //GPIOB6,GPIOB7 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB,&GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /*Its preemptive priority can be set lower*/ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;//Disable interrupt to prevent counting overflow without corresponding function, causing deadlock NVIC_Init(&NVIC_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 4095; //Set the value loaded into the active auto-reload register for the next update event TIM_TimeBaseStructure.TIM_Prescaler = 0; //Set the prescaler value for the TIMx clock frequency, no division TIM_TimeBaseStructure.TIM_ClockDivision = 0; //Set clock division: TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM counts up mode TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Falling); TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 10; //Input filter TIM_ICInit(TIM4, &TIM_ICInitStructure); TIM_ClearFlag(TIM4, TIM_FLAG_Update); //Clear all flags TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE); //Allow update interrupt TIM4->CNT = 0; TIM_Cmd(TIM4, ENABLE); //Enable TIM4}
/**************************************************************************Function function: Read encoder count within unit time Entry parameters: Timer returns Value: speed value**************************************************************************/float Read_Encoder_Speed(uint8_t TIMX){ int32_t Encoder_TIM; float res = 0; switch (TIMX) { case 5: Encoder_TIM = TIM_GetCounter(TIM5); TIM5->CNT = ENCODER_BASE_COUNT; res = (int32_t)Encoder_TIM - ENCODER_BASE_COUNT; break; case 4: Encoder_TIM = TIM_GetCounter(TIM4); TIM4->CNT = ENCODER_BASE_COUNT; res = (int32_t)Encoder_TIM - ENCODER_BASE_COUNT; break; default: Encoder_TIM = 0; res = 0; } if(res>2048.0f) res-=4096.0f; return res*360.0f/4096.0f;}
void TIM7_IRQHandler(void)//Frequency 20HZ, used for encoder counting{ if(TIM_GetITStatus(TIM7,TIM_IT_Update)==SET)//Overflow interrupt { speed=Read_Encoder_Speed(4); } TIM_ClearITPendingBit(TIM7,TIM_IT_Update); //Clear interrupt flag}
#define N 2 //How many variables need PID adjustment
const float KP[N]={1.3,1.0};//Only proportional adjustment is used hereconst float KI[N]={0,0};const float KD[N]={0,0};
struct _pid{float SetVol; //Define set valuefloat ActVol; //Define actual valuefloat Err; //Define errorfloat Err_Next; //Define previous errorfloat Err_Last; //Define the error before the last onefloat Kp,Ki,Kd; //Define proportional, integral, derivative coefficientsfloat integral; //Define integral valuefloat actuator; //Define controller execution variable}pid[N];
void PID_Init(void){for(int i=0;i<N;i++) { pid[i].SetVol=0.0; pid[i].ActVol=0.0; pid[i].Err=0.0; pid[i].Err_Next=0.0; pid[i].Err_Last=0.0; pid[i].integral=0.0; pid[i].actuator=0.0; pid[i].Kp=KP[i]; pid[i].Ki=KI[i]; pid[i].Kd=KD[i]; }}
float PID_realize(float set_val,float get_val,int i) //Position type PID algorithm implementation{ pid[i].SetVol=set_val; pid[i].ActVol=get_val; pid[i].Err=pid[i].SetVol-pid[i].ActVol;float IncVol; //Define increment pid[i].integral+=pid[i].Err;// IncVol=pid[i].Kp*(pid[i].Err-pid[i].Err_Next)+pid[i].Ki*pid[i].Err+pid[i].Kd*(pid[i].Err-2*pid[i].Err_Next+pid[i].Err_Last); pid[i].actuator=pid[i].Kp* pid[i].Err+pid[i].Ki*pid[i].integral+pid[i].Kd*(pid[i].Err-pid[i].Err_Next);// pid[i].actuator=adc_val+IncVol; pid[i].ActVol=pid[i].actuator; pid[i].Err_Last=pid[i].Err_Next; pid[i].Err_Next=pid[i].Err;
return pid[i].actuator;}
z_get=data[2]; x_get=data[0];if(z_get-z_set>0.5||z_get-z_set<-0.5)//Motor PID { LED1=0; //The light is on during adjustment PID_val_motor=PID_realize(z_set,z_get,0); PID_val_motor=PID_val_motor/10.0;if(PID_val_motor<=0) motor_flag=0;//motor_flag controls the motor forward and reverse, PID_val_motor is used to change the duty cycle, range 0~1if(PID_val_motor>0) motor_flag=1; PID_val_motor=abs_float(PID_val_motor);if(PID_val_motor>2)PID_val_motor=0;//If the mark is too far, stop the carif(PID_val_motor>1&&PID_val_motor<=2)PID_val_motor=1;if(PID_val_motor<0.2)PID_val_motor=0; }if(x_get-x_set>0.1||x_get-x_set<-0.1)//Servo PID { LED1=0; PID_val_servo=PID_realize(x_set,x_get,1); servo_angle=((140-35)/6)*PID_val_servo+35;//Linear mapping, converting PID value to angle 35~140 of servo rotationif(servo_angle<35)servo_angle=35;if(servo_angle>140)servo_angle=140; } LED1=1;
void TIM2_IRQHandler(void){ if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//Overflow interrupt {if(motor_flag==1)//Reverse { TIM_SetCompare1(TIM8,motor_duty*PID_val_motor*400.0);//Compare with the timer's auto reload value to set the duty cycle, pin:PC6 TIM_SetCompare2(TIM8,0); }if(motor_flag==0)//Forward { TIM_SetCompare1(TIM8,0); TIM_SetCompare2(TIM8,motor_duty*PID_val_motor*400.0);//Compare with the timer's auto reload value to set the duty cycle, pin:PC7 } TIM_SetCompare1(TIM3,200-(servo_angle/45.0+1)*5);//Set servo angle, according to the servo manual to obtain the relationship between duty cycle and angle, pin:PA6 } TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //Clear interrupt flag}






A small gift, hope you like it❤️


1
《【真香】嵌入式物联网/硬件开发工程师们的工作台原来是这样的……》
2
《搞嵌入式Linux,做底层还是应用?做底层要掌握哪些技能?》
3
《有一点嵌入式基础不想做单片机,如何打怪升级?》




