blob: 881f2a81c0911ad03d4ad88f4a3941e24b4c104b [file] [log] [blame]
#include "cape/encoder.h"
#include <STM32F2XX.h>
#include "cape/util.h"
// Here is where each encoder is hooked up:
// 0: PC6,PC7 TIM8(APB2)
// 1: PC0,PC1 EXTI0,EXTI1
// 2: PA0,PA1 TIM5(32)
// 3: PA2,PA3 TIM9.1,EXTI3
// 4: PA8,PB0 TIM1.1,TIM3.3
// 5: PA5,PB3 TIM2(32)
// 6: PA6,PB5 TIM3
// 7: PB6,PB7 TIM4
volatile int32_t encoder1_value = 0;
volatile int32_t encoder3_value = 0;
volatile int32_t encoder4_value = 0;
// 1.A
void EXTI0_IRQHandler(void) {
uint32_t inputs = GPIOC->IDR;
EXTI->PR = EXTI_PR_PR0;
int32_t old_value = encoder1_value;
int32_t new_value;
// This looks like a weird way to XOR the 2 inputs, but it compiles down to
// just 2 instructions, which is hard to beat.
if (((inputs >> 1) ^ inputs) & (1 << 0)) {
new_value = old_value + 1;
} else {
new_value = old_value - 1;
}
encoder1_value = new_value;
}
// 1.B
void EXTI1_IRQHandler(void) {
uint32_t inputs = GPIOC->IDR;
EXTI->PR = EXTI_PR_PR1;
int32_t old_value = encoder1_value;
int32_t new_value;
if (((inputs >> 1) ^ inputs) & (1 << 0)) {
new_value = old_value - 1;
} else {
new_value = old_value + 1;
}
encoder1_value = new_value;
}
// 3.A
void TIM1_BRK_TIM9_IRQHandler(void) {
uint32_t inputs = GPIOA->IDR;
TIM9->SR = ~TIM_SR_CC1IF;
int32_t old_value = encoder3_value;
int32_t new_value;
if (((inputs >> 1) ^ inputs) & (1 << 2)) {
new_value = old_value + 1;
} else {
new_value = old_value - 1;
}
encoder3_value = new_value;
}
// 3.B
void EXTI3_IRQHandler(void) {
uint32_t inputs = GPIOA->IDR;
EXTI->PR = EXTI_PR_PR3;
int32_t old_value = encoder3_value;
int32_t new_value;
if (((inputs >> 1) ^ inputs) & (1 << 2)) {
new_value = old_value - 1;
} else {
new_value = old_value + 1;
}
encoder3_value = new_value;
}
#if 0
TODO(brians): Figure out a good way to let robot_comp override this.
// 4.A
void TIM1_CC_IRQHandler(void) {
uint32_t a_inputs = GPIOA->IDR, b_inputs = GPIOB->IDR;
TIM1->SR = ~TIM_SR_CC1IF;
int32_t old_value = encoder4_value;
int32_t new_value;
if (((a_inputs >> 8) ^ b_inputs) & (1 << 0)) {
new_value = old_value + 1;
} else {
new_value = old_value - 1;
}
encoder4_value = new_value;
}
#endif
// 4.B
void TIM3_IRQHandler(void) {
uint32_t a_inputs = GPIOA->IDR, b_inputs = GPIOB->IDR;
TIM3->SR = ~TIM_SR_CC3IF;
int32_t old_value = encoder4_value;
int32_t new_value;
if (((a_inputs >> 8) ^ b_inputs) & (1 << 0)) {
new_value = old_value - 1;
} else {
new_value = old_value + 1;
}
encoder4_value = new_value;
}
static void encoder_setup(TIM_TypeDef *timer, int fast) {
timer->CR1 =
TIM_CR1_URS | /* don't generate spurious update interrupts that
might be shared with other timers */
(fast ? (1 << 8) : 0) /* divide filter clock by 2 on fast encoders */;
timer->SMCR = 3; // 4x quadrature encoder mode
timer->CCER = 0;
timer->CCMR1 =
TIM_CCMR1_CC2S_0 | /* input pin 2 -> timer input 2 */
TIM_CCMR1_CC1S_0 | /* input pin 1 -> timer input 1 */
(0xE << 4) | /* divide filter clock by 32, need 6 in a row to trigger */
(0xE << 12) /* same for other input */;
timer->PSC = 0;
timer->EGR = TIM_EGR_UG;
timer->CR1 |= TIM_CR1_CEN;
}
void encoder_init(void) {
// Set up the 3 simple software encoder inputs.
EXTI_set(0, 2);
EXTI_set(1, 2);
EXTI_set(3, 0);
EXTI->IMR |= EXTI_IMR_MR0 | EXTI_IMR_MR1 | EXTI_IMR_MR3;
EXTI->RTSR |= EXTI_RTSR_TR0 | EXTI_RTSR_TR1 | EXTI_RTSR_TR3;
EXTI->FTSR |= EXTI_FTSR_TR0 | EXTI_FTSR_TR1 | EXTI_FTSR_TR3;
NVIC_EnableIRQ(EXTI0_IRQn);
NVIC_EnableIRQ(EXTI1_IRQn);
NVIC_EnableIRQ(EXTI3_IRQn);
// Set up the A2 software encoder input through TIM9 input 1.
gpio_setup_alt(GPIOA, 2, 3);
RCC->APB2ENR |= RCC_APB2ENR_TIM9EN;
TIM9->CR1 = 0;
TIM9->DIER = TIM_DIER_CC1IE;
TIM9->CCMR1 = TIM_CCMR1_CC1S_0; /* input pin 1 -> timer input 1 */
TIM9->CCER = TIM_CCER_CC1NP | TIM_CCER_CC1P | TIM_CCER_CC1E;
TIM9->EGR = TIM_EGR_UG;
TIM9->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);
// Set up the A4 software encoder input through TIM1 input 1.
gpio_setup_alt(GPIOA, 8, 1);
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
TIM1->CR1 = 0;
TIM1->DIER = TIM_DIER_CC1IE;
TIM1->CCMR1 = TIM_CCMR1_CC1S_0; /* input pin 1 -> timer input 1 */
TIM1->CCER = TIM_CCER_CC1NP | TIM_CCER_CC1P | TIM_CCER_CC1E;
TIM1->EGR = TIM_EGR_UG;
TIM1->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(TIM1_CC_IRQn);
// Set up the B4 software encoder input through TIM3 input 3.
gpio_setup_alt(GPIOB, 0, 2);
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
TIM3->CR1 = 0;
TIM3->DIER = TIM_DIER_CC3IE;
TIM3->CCMR2 = TIM_CCMR2_CC3S_0; /* input pin 3 -> timer input 3 */
TIM3->CCER = TIM_CCER_CC3NP | TIM_CCER_CC3P | TIM_CCER_CC3E;
TIM3->EGR = TIM_EGR_UG;
TIM3->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(TIM3_IRQn);
gpio_setup_alt(GPIOA, 5, 1);
gpio_setup_alt(GPIOB, 3, 1);
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
encoder_setup(TIM2, 0);
gpio_setup_alt(GPIOA, 6, 2);
gpio_setup_alt(GPIOB, 5, 2);
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
encoder_setup(TIM3, 0);
gpio_setup_alt(GPIOB, 6, 2);
gpio_setup_alt(GPIOB, 7, 2);
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
encoder_setup(TIM4, 0);
gpio_setup_alt(GPIOA, 0, 2);
gpio_setup_alt(GPIOA, 1, 2);
RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
encoder_setup(TIM5, 0);
gpio_setup_alt(GPIOC, 6, 3);
gpio_setup_alt(GPIOC, 7, 3);
RCC->APB2ENR |= RCC_APB2ENR_TIM8EN;
encoder_setup(TIM8, 1);
}