W tym poście chciałbym opisać sposób obsługi serwomechanizmu za pomocą jednego z timerów skonfigurowanego w trybie PWM.
[Źródło: http://www.st.com/en/evaluation-tools/32f746gdiscovery.html]
Opis funkcji
Poniżej przejdę przez opis poszczególnych funkcji odpowiedzialnych za poszczególne etapy uruchomienia.
Dane dla danego timera są przechowywane w strukturze:
Dane dla danego timera są przechowywane w strukturze:
- typedef struct {
- PWM_TIM_NAME_t ServoPinGenerateName;
- TIM_TypeDef* selectedTim;
- const TimerPwmChannel_Typedef ServoPwmChannel;
- GPIO_TypeDef* ServoPwmPort;
- const uint16_t ServoPwmPin;
- float servoPosition;
- float servoMicros;
- }ServoTimerPwmSetting_Typedef;
Zdefiniowane dane wyglądają następująco:
Uruchomienie timera:
- ServoTimerPwmSetting_Typedef servoPwmTimer2[] = {
- {PWM_CH1_TIM2_PA0, TIM2, 1, GPIOA, GPIO_PIN_0, 0, 0},
- };
Uruchomienie timera:
- static void servo_tim2_init(void)
- {
- #if SERVO_TIMER_2_ENABLE == 1
- /* Enable Pin */
- GPIO_InitTypeDef GPIO_InitStruct;
- uint8_t enableCount;
- for(enableCount=0; enableCount<SERVO_TIMER_2_EN_CHANEL_COUNT; enableCount++)
- {
- servo_EnableClock(servoPwmTimer2[enableCount].ServoPwmPort);
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
- GPIO_InitStruct.Pin = servoPwmTimer2[enableCount].ServoPwmPin;
- HAL_GPIO_Init(servoPwmTimer2[enableCount].ServoPwmPort, &GPIO_InitStruct);
- }
- /* Enable Pin For Timer */
- TIM_HandleTypeDef TimHandle;
- TIM_OC_InitTypeDef sConfig;
- __HAL_RCC_TIM2_CLK_ENABLE();
- TimHandle.Instance = TIM2;
- TimHandle.Init.Prescaler = SERVO_TIM2_PRESCALE;
- TimHandle.Init.Period = SERVO_TIM2_PERIODE;
- TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
- TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
- TimHandle.Init.RepetitionCounter = 0;
- HAL_TIM_PWM_Init(&TimHandle);
- for(enableCount=0; enableCount<SERVO_TIMER_2_EN_CHANEL_COUNT; enableCount++)
- {
- sConfig.OCMode = TIM_OCMODE_PWM1;
- sConfig.OCPolarity = SERVO_TIM2_POLARITY;
- sConfig.OCFastMode = TIM_OCFAST_DISABLE;
- sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
- sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
- sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
- sConfig.Pulse = 0;
- if(servoPwmTimer2[enableCount].ServoPwmChannel==1)
- {
- HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1);
- HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_1);
- }
- else if(servoPwmTimer2[enableCount].ServoPwmChannel==2)
- {
- HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_2);
- HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_2);
- }
- else if(servoPwmTimer2[enableCount].ServoPwmChannel==3)
- {
- HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_3);
- HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_3);
- }
- else if(servoPwmTimer2[enableCount].ServoPwmChannel==4)
- {
- HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_4);
- HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_4);
- }
- }
- #endif
- }
Najpierw uruchamiany jest pin GPIO przypisany do danego kanału, następnie timera oraz PWM.
Ustawienie wartości PWM dla danego kanału oraz timera:
- static void servo_tim2_SetPWM(TimerPwmChannel_Typedef pwmChannel, uint16_t value)
- {
- #if SERVO_TIMER_2_ENABLE == 1
- if(value>SERVO_TIM2_PERIODE) { value=SERVO_TIM2_PERIODE; }
- if(pwmChannel==Servo_Channel_1) { TIM2->CCR1 = value; }
- if(pwmChannel==Servo_Channel_2) { TIM2->CCR2 = value; }
- if(pwmChannel==Servo_Channel_3) { TIM2->CCR3 = value; }
- if(pwmChannel==Servo_Channel_4) { TIM2->CCR4 = value; }
- #endif
- }
Główna funkcja uruchamiająca uruchamia timer oraz wprowadza wartość 0 dla generowanego PWM:
- void Servo_Enable(void)
- {
- servo_enable_timer();
- servo_setStartPWMValue();
- }
Ustawienie stopni przesunięcia:
- SetServoResult_Typedef Servo_SetRotDegrees(ServoTimerPwmSetting_Typedef* servoStr, float positToSet) {
- if (positToSet < 0 || positToSet > 180) {
- return SET_SERVO_ERROR;
- }
- uint16_t microSeconds = 0;
- if(servoStr->selectedTim == TIM2)
- {
- microSeconds = (SERVO_MICSEK_MAX_TIM2_PULSE - SERVO_MICSEK_MIN_TIM2_PULSE) * positToSet / 180 + SERVO_MICSEK_MIN_TIM2_PULSE;
- }
- else if(servoStr->selectedTim == TIM3)
- {
- microSeconds = (SERVO_MICSEK_MAX_TIM3_PULSE - SERVO_MICSEK_MIN_TIM3_PULSE) * positToSet / 180 + SERVO_MICSEK_MIN_TIM3_PULSE;
- }
- else if(servoStr->selectedTim == TIM4)
- {
- microSeconds = (SERVO_MICSEK_MAX_TIM4_PULSE - SERVO_MICSEK_MIN_TIM4_PULSE) * positToSet / 180 + SERVO_MICSEK_MIN_TIM4_PULSE;
- }
- else if(servoStr->selectedTim == TIM5)
- {
- microSeconds = (SERVO_MICSEK_MAX_TIM5_PULSE - SERVO_MICSEK_MIN_TIM5_PULSE) * positToSet / 180 + SERVO_MICSEK_MIN_TIM5_PULSE;
- }
- else
- {
- return SET_SERVO_ERROR;
- }
- /* Save current position into structures */
- servoStr->servoMicros = microSeconds;
- servoStr->servoPosition = positToSet;
- servo_SetPwm(servoStr, microSeconds);
- return SET_SERVO_OK;
- }
Na samym początku przerabiana jest wartość z stopni na mikrosekundy, po czym ustawiana jest wartość PWM.
- SetServoResult_Typedef Servo_SetPulseLengMicrosec(ServoTimerPwmSetting_Typedef* servoStr, uint16_t microSeconds)
- {
- float setDegres = 0.0;
- if(servoStr->selectedTim == TIM2)
- {
- if (microSeconds < SERVO_MICSEK_MIN_TIM2_PULSE || microSeconds > SERVO_MICSEK_MAX_TIM2_PULSE) {
- return SET_SERVO_ERROR;
- }
- setDegres = (float)(microSeconds - SERVO_MICSEK_MIN_TIM2_PULSE) * (float)180 / (float)(SERVO_MICSEK_MAX_TIM2_PULSE -SERVO_MICSEK_MIN_TIM2_PULSE);
- }
- else if(servoStr->selectedTim == TIM3)
- {
- if (microSeconds < SERVO_MICSEK_MIN_TIM3_PULSE || microSeconds > SERVO_MICSEK_MAX_TIM3_PULSE) {
- return SET_SERVO_ERROR;
- }
- setDegres = (float)(microSeconds - SERVO_MICSEK_MIN_TIM3_PULSE) * (float)180 / (float)(SERVO_MICSEK_MAX_TIM3_PULSE -SERVO_MICSEK_MIN_TIM3_PULSE);
- }
- else if(servoStr->selectedTim == TIM4)
- {
- if (microSeconds < SERVO_MICSEK_MIN_TIM4_PULSE || microSeconds > SERVO_MICSEK_MAX_TIM4_PULSE) {
- return SET_SERVO_ERROR;
- }
- setDegres = (float)(microSeconds - SERVO_MICSEK_MIN_TIM4_PULSE) * (float)180 / (float)(SERVO_MICSEK_MAX_TIM4_PULSE -SERVO_MICSEK_MIN_TIM4_PULSE);
- }
- else if(servoStr->selectedTim == TIM5)
- {
- if (microSeconds < SERVO_MICSEK_MIN_TIM5_PULSE || microSeconds > SERVO_MICSEK_MAX_TIM5_PULSE) {
- return SET_SERVO_ERROR;
- }
- setDegres = (float)(microSeconds - SERVO_MICSEK_MIN_TIM5_PULSE) * (float)180 / (float)(SERVO_MICSEK_MAX_TIM5_PULSE -SERVO_MICSEK_MIN_TIM5_PULSE);
- }
- else
- {
- return SET_SERVO_ERROR;
- }
- servoStr->servoMicros = microSeconds;
- servoStr->servoPosition = setDegres;
- servo_SetPwm(servoStr, microSeconds);
- return SET_SERVO_OK;
- }
Funkcja podobna do poprzedniej z tą różnicą, że przerabiana jest wartość mikrosekund na stopnie.
Pozostałe funkcje można znaleźć w plikach na dysku Google pod tym linkiem.