/* Copyright 2016 Benjamin Vedder benjamin@vedder.se This file is part of the VESC firmware. The VESC firmware is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The VESC firmware is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "encoder.h" #include "ch.h" #include "hal.h" #include "stm32f4xx_conf.h" #include "hw.h" #include "mc_interface.h" #include "utils.h" #include // Defines #define AS5047P_READ_ANGLECOM (0x3FFF | 0x4000 | 0x8000) // This is just ones #define AS5047_SAMPLE_RATE_HZ 20000 #define AD2S1205_SAMPLE_RATE_HZ 20000 //25MHz max spi clk #define MT6816_SAMPLE_RATE_HZ 20000 #define MT6816_NO_MAGNET_ERROR_MASK 0x0002 #define SINCOS_SAMPLE_RATE_HZ 20000 #define SINCOS_MIN_AMPLITUDE 1.0 // sqrt(sin^2 + cos^2) has to be larger than this #define SINCOS_MAX_AMPLITUDE 1.65 // sqrt(sin^2 + cos^2) has to be smaller than this #if (AS5047_USE_HW_SPI_PINS) || (MT6816_USE_HW_SPI_PINS) || (AD2S1205_USE_HW_SPI_PINS) #ifdef HW_SPI_DEV #define SPI_SW_MISO_GPIO HW_SPI_PORT_MISO #define SPI_SW_MISO_PIN HW_SPI_PIN_MISO #define SPI_SW_MOSI_GPIO HW_SPI_PORT_MOSI #define SPI_SW_MOSI_PIN HW_SPI_PIN_MOSI #define SPI_SW_SCK_GPIO HW_SPI_PORT_SCK #define SPI_SW_SCK_PIN HW_SPI_PIN_SCK #define SPI_SW_CS_GPIO HW_SPI_PORT_NSS #define SPI_SW_CS_PIN HW_SPI_PIN_NSS #else // Note: These values are hardcoded. #define SPI_SW_MISO_GPIO GPIOB #define SPI_SW_MISO_PIN 4 #define SPI_SW_MOSI_GPIO GPIOB #define SPI_SW_MOSI_PIN 5 #define SPI_SW_SCK_GPIO GPIOB #define SPI_SW_SCK_PIN 3 #define SPI_SW_CS_GPIO GPIOB #define SPI_SW_CS_PIN 0 #endif #else #define SPI_SW_MISO_GPIO HW_HALL_ENC_GPIO2 #define SPI_SW_MISO_PIN HW_HALL_ENC_PIN2 #if AS504x_USE_SW_MOSI_PIN #ifdef HW_SPI_SW_MOSI_GPIO #define SPI_SW_MOSI_GPIO HW_SPI_SW_MOSI_GPIO #define SPI_SW_MOSI_PIN HW_SPI_SW_MOSI_PIN #else #define SPI_SW_MOSI_GPIO HW_SPI_PORT_MOSI #define SPI_SW_MOSI_PIN HW_SPI_PIN_MOSI #endif #endif #define SPI_SW_SCK_GPIO HW_HALL_ENC_GPIO1 #define SPI_SW_SCK_PIN HW_HALL_ENC_PIN1 #define SPI_SW_CS_GPIO HW_HALL_ENC_GPIO3 #define SPI_SW_CS_PIN HW_HALL_ENC_PIN3 #endif // Private types typedef enum { ENCODER_MODE_NONE = 0, ENCODER_MODE_ABI, ENCODER_MODE_AS5047P_SPI, RESOLVER_MODE_AD2S1205, ENCODER_MODE_SINCOS, ENCODER_MODE_TS5700N8501, ENCODER_MODE_MT6816_SPI } encoder_mode; // Private variables static bool index_found = false; static uint32_t enc_counts = 10000; static encoder_mode mode = ENCODER_MODE_NONE; static float last_enc_angle = 0.0; static uint32_t spi_val = 0; static uint8_t spi_data_err_raised = 0; static uint32_t spi_error_cnt = 0; static uint32_t encoder_no_magnet_error_cnt = 0; static float spi_error_rate = 0.0; static float encoder_no_magnet_error_rate = 0.0; static float resolver_loss_of_tracking_error_rate = 0.0; static float resolver_degradation_of_signal_error_rate = 0.0; static float resolver_loss_of_signal_error_rate = 0.0; static uint32_t resolver_loss_of_tracking_error_cnt = 0; static uint32_t resolver_degradation_of_signal_error_cnt = 0; static uint32_t resolver_loss_of_signal_error_cnt = 0; static uint32_t AS504x_spi_communication_error_count = 0; static AS504x_diag AS504x_sensor_diag = {0}; static float sin_gain = 0.0; static float sin_offset = 0.0; static float cos_gain = 0.0; static float cos_offset = 0.0; static float sincos_filter_constant = 0.0; static uint32_t sincos_signal_below_min_error_cnt = 0; static uint32_t sincos_signal_above_max_error_cnt = 0; static float sincos_signal_low_error_rate = 0.0; static float sincos_signal_above_max_error_rate = 0.0; static SerialConfig TS5700N8501_uart_cfg = { 2500000, 0, USART_CR2_LINEN, 0 }; // SPI1 SPI2/3 #define SPI_BaudRatePrescaler_2 ((uint16_t)0x0000) // 42 MHz 21 MHZ #define SPI_BaudRatePrescaler_4 ((uint16_t)0x0008) // 21 MHz 10.5 MHz #define SPI_BaudRatePrescaler_8 ((uint16_t)0x0010) // 10.5 MHz 5.25 MHz #define SPI_BaudRatePrescaler_16 ((uint16_t)0x0018) // 5.25 MHz 2.626 MHz #define SPI_BaudRatePrescaler_32 ((uint16_t)0x0020) // 2.626 MHz 1.3125 MHz #define SPI_BaudRatePrescaler_64 ((uint16_t)0x0028) // 1.3125 MHz 656.25 KHz #define SPI_BaudRatePrescaler_128 ((uint16_t)0x0030) // 656.25 KHz 328.125 KHz #define SPI_BaudRatePrescaler_256 ((uint16_t)0x0038) // 328.125 KHz 164.06 KHz #define SPI_DATASIZE_16BIT SPI_CR1_DFF #ifdef HW_SPI_DEV //MT6816 max clk freq: 15.625MHz static const SPIConfig mt6816_spi_cfg = { NULL, SPI_SW_CS_GPIO, SPI_SW_CS_PIN, SPI_BaudRatePrescaler_4 | SPI_CR1_CPOL | SPI_CR1_CPHA | SPI_DATASIZE_16BIT}; #endif static THD_FUNCTION(ts5700n8501_thread, arg); static THD_WORKING_AREA(ts5700n8501_thread_wa, 512); static volatile bool ts5700n8501_stop_now = true; static volatile bool ts5700n8501_is_running = false; static volatile uint8_t ts5700n8501_raw_status[8] = {0}; static volatile bool ts5700n8501_reset_errors = false; static volatile bool ts5700n8501_reset_multiturn = false; // Private functions static void spi_transfer(uint16_t *in_buf, const uint16_t *out_buf, int length); static void spi_begin(void); static void spi_end(void); static void spi_delay(void); static void spi_AS5047_cs_delay(void); static void TS5700N8501_send_byte(uint8_t b); static void AS504x_determinate_if_connected(bool was_last_valid); #if AS504x_USE_SW_MOSI_PIN || AS5047_USE_HW_SPI_PINS static uint16_t AS504x_diag_fetch_now_count = 0; static uint8_t AS504x_fetch_diag(void); static uint8_t AS504x_verify_serial(void); static void AS504x_deserialize_diag(void); static void AS504x_fetch_clear_err_diag(void); static uint8_t AS504x_spi_transfer_err_check(uint16_t *in_buf, const uint16_t *out_buf, int length); #else static uint32_t AS504x_data_last_invalid_counter = 0; #endif uint32_t encoder_spi_get_error_cnt(void) { return spi_error_cnt; } uint32_t encoder_spi_get_val(void) { return spi_val; } float encoder_spi_get_error_rate(void) { return spi_error_rate; } uint32_t encoder_get_no_magnet_error_cnt(void) { return encoder_no_magnet_error_cnt; } float encoder_get_no_magnet_error_rate(void) { return encoder_no_magnet_error_rate; } float encoder_resolver_loss_of_tracking_error_rate(void) { return resolver_loss_of_tracking_error_rate; } float encoder_resolver_degradation_of_signal_error_rate(void) { return resolver_degradation_of_signal_error_rate; } float encoder_resolver_loss_of_signal_error_rate(void) { return resolver_loss_of_signal_error_rate; } uint32_t encoder_resolver_loss_of_tracking_error_cnt(void) { return resolver_loss_of_tracking_error_cnt; } uint32_t encoder_resolver_degradation_of_signal_error_cnt(void) { return resolver_degradation_of_signal_error_cnt; } uint32_t encoder_resolver_loss_of_signal_error_cnt(void) { return resolver_loss_of_signal_error_cnt; } uint32_t encoder_sincos_get_signal_below_min_error_cnt(void) { return sincos_signal_below_min_error_cnt; } uint32_t encoder_sincos_get_signal_above_max_error_cnt(void) { return sincos_signal_above_max_error_cnt; } float encoder_sincos_get_signal_below_min_error_rate(void) { return sincos_signal_low_error_rate; } float encoder_sincos_get_signal_above_max_error_rate(void) { return sincos_signal_above_max_error_rate; } uint8_t* encoder_ts5700n8501_get_raw_status(void) { return (uint8_t*)ts5700n8501_raw_status; } int16_t encoder_ts57n8501_get_abm(void) { return (uint16_t)ts5700n8501_raw_status[4] | ((uint16_t)ts5700n8501_raw_status[5] << 8); } void encoder_ts57n8501_reset_errors(void) { ts5700n8501_reset_errors = true; } void encoder_ts57n8501_reset_multiturn(void) { ts5700n8501_reset_multiturn = true; } void encoder_deinit(void) { nvicDisableVector(HW_ENC_EXTI_CH); nvicDisableVector(HW_ENC_TIM_ISR_CH); TIM_DeInit(HW_ENC_TIM); palSetPadMode(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN, PAL_MODE_INPUT_PULLUP); palSetPadMode(SPI_SW_SCK_GPIO, SPI_SW_SCK_PIN, PAL_MODE_INPUT_PULLUP); palSetPadMode(SPI_SW_CS_GPIO, SPI_SW_CS_PIN, PAL_MODE_INPUT_PULLUP); #ifdef HW_SPI_DEV spiStop(&HW_SPI_DEV); #endif palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_INPUT_PULLUP); palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_INPUT_PULLUP); if (mode == ENCODER_MODE_TS5700N8501) { ts5700n8501_stop_now = true; while (ts5700n8501_is_running) { chThdSleepMilliseconds(1); } palSetPadMode(HW_UART_TX_PORT, HW_UART_TX_PIN, PAL_MODE_INPUT_PULLUP); palSetPadMode(HW_UART_RX_PORT, HW_UART_RX_PIN, PAL_MODE_INPUT_PULLUP); #ifdef HW_ADC_EXT_GPIO palSetPadMode(HW_ADC_EXT_GPIO, HW_ADC_EXT_PIN, PAL_MODE_INPUT_ANALOG); #endif } index_found = false; mode = ENCODER_MODE_NONE; last_enc_angle = 0.0; spi_error_rate = 0.0; sincos_signal_low_error_rate = 0.0; sincos_signal_above_max_error_rate = 0.0; } void encoder_init_abi(uint32_t counts) { EXTI_InitTypeDef EXTI_InitStructure; // Initialize variables index_found = false; enc_counts = counts; palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_ALTERNATE(HW_ENC_TIM_AF)); palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_ALTERNATE(HW_ENC_TIM_AF)); // palSetPadMode(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3, PAL_MODE_ALTERNATE(HW_ENC_TIM_AF)); // Enable timer clock HW_ENC_TIM_CLK_EN(); // Enable SYSCFG clock RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); TIM_EncoderInterfaceConfig (HW_ENC_TIM, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_SetAutoreload(HW_ENC_TIM, enc_counts - 1); // Filter HW_ENC_TIM->CCMR1 |= 6 << 12 | 6 << 4; HW_ENC_TIM->CCMR2 |= 6 << 4; TIM_Cmd(HW_ENC_TIM, ENABLE); // Interrupt on index pulse // Connect EXTI Line to pin SYSCFG_EXTILineConfig(HW_ENC_EXTI_PORTSRC, HW_ENC_EXTI_PINSRC); // Configure EXTI Line EXTI_InitStructure.EXTI_Line = HW_ENC_EXTI_LINE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // Enable and set EXTI Line Interrupt to the highest priority nvicEnableVector(HW_ENC_EXTI_CH, 0); mode = ENCODER_MODE_ABI; } void encoder_init_as5047p_spi(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; palSetPadMode(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN, PAL_MODE_INPUT); palSetPadMode(SPI_SW_SCK_GPIO, SPI_SW_SCK_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPadMode(SPI_SW_CS_GPIO, SPI_SW_CS_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); // Set MOSI to 1 #if (AS5047_USE_HW_SPI_PINS || AD2S1205_USE_HW_SPI_PINS || AS504x_USE_SW_MOSI_PIN ) palSetPadMode(SPI_SW_MOSI_GPIO, SPI_SW_MOSI_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPad(SPI_SW_MOSI_GPIO, SPI_SW_MOSI_PIN); #endif // Enable timer clock HW_ENC_TIM_CLK_EN(); // Time Base configuration TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = ((168000000 / 2 / AS5047_SAMPLE_RATE_HZ) - 1); TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(HW_ENC_TIM, &TIM_TimeBaseStructure); // Enable overflow interrupt TIM_ITConfig(HW_ENC_TIM, TIM_IT_Update, ENABLE); // Enable timer TIM_Cmd(HW_ENC_TIM, ENABLE); nvicEnableVector(HW_ENC_TIM_ISR_CH, 6); mode = ENCODER_MODE_AS5047P_SPI; index_found = true; spi_error_rate = 0.0; } void encoder_init_mt6816_spi(void) { #ifdef HW_SPI_DEV TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; palSetPadMode(SPI_SW_SCK_GPIO, SPI_SW_SCK_PIN, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); palSetPadMode(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); palSetPadMode(SPI_SW_CS_GPIO, SPI_SW_CS_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); #if (MT6816_USE_HW_SPI_PINS) palSetPadMode(SPI_SW_MOSI_GPIO, SPI_SW_MOSI_PIN, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); #endif //Start driver with MT6816 SPI settings spiStart(&HW_SPI_DEV, &mt6816_spi_cfg); // Enable timer clock HW_ENC_TIM_CLK_EN(); // Time Base configuration TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = ((168000000 / 2 / MT6816_SAMPLE_RATE_HZ) - 1); TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(HW_ENC_TIM, &TIM_TimeBaseStructure); // Enable overflow interrupt TIM_ITConfig(HW_ENC_TIM, TIM_IT_Update, ENABLE); // Enable timer TIM_Cmd(HW_ENC_TIM, ENABLE); nvicEnableVector(HW_ENC_TIM_ISR_CH, 6); mode = ENCODER_MODE_MT6816_SPI; index_found = true; spi_error_rate = 0.0; encoder_no_magnet_error_rate = 0.0; #endif } void encoder_init_ad2s1205_spi(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; resolver_loss_of_tracking_error_rate = 0.0; resolver_degradation_of_signal_error_rate = 0.0; resolver_loss_of_signal_error_rate = 0.0; resolver_loss_of_tracking_error_cnt = 0; resolver_loss_of_signal_error_cnt = 0; palSetPadMode(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN, PAL_MODE_INPUT); palSetPadMode(SPI_SW_SCK_GPIO, SPI_SW_SCK_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPadMode(SPI_SW_CS_GPIO, SPI_SW_CS_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); // Set MOSI to 1 #if (AS5047_USE_HW_SPI_PINS || AD2S1205_USE_HW_SPI_PINS) palSetPadMode(SPI_SW_MOSI_GPIO, SPI_SW_MOSI_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPad(SPI_SW_MOSI_GPIO, SPI_SW_MOSI_PIN); #endif // TODO: Choose pins on comm port when these are not defined #if defined(AD2S1205_SAMPLE_GPIO) palSetPadMode(AD2S1205_SAMPLE_GPIO, AD2S1205_SAMPLE_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPad(AD2S1205_SAMPLE_GPIO, AD2S1205_SAMPLE_PIN); // Prepare for a falling edge SAMPLE assertion #endif #if defined(AD2S1205_RDVEL_GPIO) palSetPadMode(AD2S1205_RDVEL_GPIO, AD2S1205_RDVEL_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPad(AD2S1205_RDVEL_GPIO, AD2S1205_RDVEL_PIN); // Will always read position #endif // Enable timer clock HW_ENC_TIM_CLK_EN(); // Time Base configuration TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = ((168000000 / 2 / AD2S1205_SAMPLE_RATE_HZ) - 1); TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(HW_ENC_TIM, &TIM_TimeBaseStructure); // Enable overflow interrupt TIM_ITConfig(HW_ENC_TIM, TIM_IT_Update, ENABLE); // Enable timer TIM_Cmd(HW_ENC_TIM, ENABLE); nvicEnableVector(HW_ENC_TIM_ISR_CH, 6); mode = RESOLVER_MODE_AD2S1205; index_found = true; } void encoder_init_sincos(float s_gain, float s_offset, float c_gain, float c_offset, float filter_constant) { //ADC inputs are already initialized in hw_init_gpio() sin_gain = s_gain; sin_offset = s_offset; cos_gain = c_gain; cos_offset = c_offset; sincos_filter_constant = filter_constant; sincos_signal_below_min_error_cnt = 0; sincos_signal_above_max_error_cnt = 0; sincos_signal_low_error_rate = 0.0; sincos_signal_above_max_error_rate = 0.0; // ADC measurements needs to be in sync with motor PWM #ifdef HW_HAS_SIN_COS_ENCODER mode = ENCODER_MODE_SINCOS; index_found = true; #else mode = ENCODER_MODE_NONE; index_found = false; #endif } void encoder_init_ts5700n8501(void) { mode = ENCODER_MODE_TS5700N8501; index_found = true; spi_error_rate = 0.0; spi_error_cnt = 0; ts5700n8501_is_running = true; ts5700n8501_stop_now = false; chThdCreateStatic(ts5700n8501_thread_wa, sizeof(ts5700n8501_thread_wa), NORMALPRIO - 10, ts5700n8501_thread, NULL); } bool encoder_is_configured(void) { return mode != ENCODER_MODE_NONE; } /** * Read angle from configured encoder. * * @return * The current encoder angle in degrees. */ float encoder_read_deg(void) { static float angle = 0.0; switch (mode) { case ENCODER_MODE_ABI: angle = ((float)HW_ENC_TIM->CNT * 360.0) / (float)enc_counts; break; case ENCODER_MODE_AS5047P_SPI: case ENCODER_MODE_MT6816_SPI: case RESOLVER_MODE_AD2S1205: case ENCODER_MODE_TS5700N8501: angle = last_enc_angle; break; #ifdef HW_HAS_SIN_COS_ENCODER case ENCODER_MODE_SINCOS: { float sin = ENCODER_SIN_VOLTS * sin_gain - sin_offset; float cos = ENCODER_COS_VOLTS * cos_gain - cos_offset; float module = SQ(sin) + SQ(cos); if (module > SQ(SINCOS_MAX_AMPLITUDE) ) { // signals vector outside of the valid area. Increase error count and discard measurement ++sincos_signal_above_max_error_cnt; UTILS_LP_FAST(sincos_signal_above_max_error_rate, 1.0, 1./SINCOS_SAMPLE_RATE_HZ); angle = last_enc_angle; } else { if (module < SQ(SINCOS_MIN_AMPLITUDE)) { ++sincos_signal_below_min_error_cnt; UTILS_LP_FAST(sincos_signal_low_error_rate, 1.0, 1./SINCOS_SAMPLE_RATE_HZ); angle = last_enc_angle; } else { UTILS_LP_FAST(sincos_signal_above_max_error_rate, 0.0, 1./SINCOS_SAMPLE_RATE_HZ); UTILS_LP_FAST(sincos_signal_low_error_rate, 0.0, 1./SINCOS_SAMPLE_RATE_HZ); float angle_tmp = RAD2DEG_f(utils_fast_atan2(sin, cos)); UTILS_LP_FAST(angle, angle_tmp, sincos_filter_constant); last_enc_angle = angle; } } break; } #endif default: break; } return angle; } /* * Note: This is not a good solution and needs a proper implementation later... */ float encoder_read_deg_multiturn(void) { if (mode == ENCODER_MODE_TS5700N8501) { encoder_ts57n8501_get_abm(); float ts_mt = (float)encoder_ts57n8501_get_abm(); if (fabsf(ts_mt) > 5000.0) { ts_mt = 0; encoder_ts57n8501_reset_multiturn(); } ts_mt += 5000; return encoder_read_deg() / 10000.0 + (360 * ts_mt) / 10000.0; } else { return encoder_read_deg(); } } /** * Reset the encoder counter. Should be called from the index interrupt. */ void encoder_reset(void) { // Only reset if the pin is still high to avoid too short pulses, which // most likely are noise. __NOP(); __NOP(); __NOP(); __NOP(); if (palReadPad(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3)) { const unsigned int cnt = HW_ENC_TIM->CNT; static int bad_pulses = 0; const unsigned int lim = enc_counts / 20; if (index_found) { // Some plausibility filtering. if (cnt > (enc_counts - lim) || cnt < lim) { HW_ENC_TIM->CNT = 0; bad_pulses = 0; } else { bad_pulses++; if (bad_pulses > 5) { index_found = 0; } } } else { HW_ENC_TIM->CNT = 0; index_found = true; bad_pulses = 0; } } } // returns true for even number of ones (no parity error according to AS5047 datasheet static bool spi_check_parity(uint16_t x) { x ^= x >> 8; x ^= x >> 4; x ^= x >> 2; x ^= x >> 1; return (~x) & 1; } #if AS504x_USE_SW_MOSI_PIN || AS5047_USE_HW_SPI_PINS static uint8_t AS504x_fetch_diag(void) { uint16_t recf[2], senf[2] = {AS504x_SPI_READ_DIAG_MSG, AS504x_SPI_READ_MAGN_MSG}; uint8_t ret = 0; spi_begin(); spi_transfer(0, senf, 1); spi_end(); spi_AS5047_cs_delay(); spi_begin(); ret |= AS504x_spi_transfer_err_check(recf, senf + 1, 1); spi_end(); spi_AS5047_cs_delay(); spi_begin(); ret |= AS504x_spi_transfer_err_check(recf + 1, 0, 1); spi_end(); if(!ret) { if (spi_check_parity(recf[0]) && spi_check_parity(recf[1])) { AS504x_sensor_diag.serial_diag_flgs = recf[0]; AS504x_sensor_diag.serial_magnitude = recf[1]; } } return ret; } /* * This function fetches error flag from AS504x and afterwards clean error flag */ static void AS504x_fetch_clear_err_diag() { uint16_t recf, senf = AS504x_SPI_READ_CLEAR_ERROR_MSG; spi_begin(); spi_transfer(0, &senf, 1); spi_end(); spi_AS5047_cs_delay(); spi_begin(); spi_transfer(&recf, 0, 1); spi_end(); AS504x_sensor_diag.serial_error_flags = recf; } /* * Try verify if the diagnostics are not corrupt * This function can prevent deserialazing corrupted data if the MISO bus is HIGH or LOW */ static uint8_t AS504x_verify_serial() { uint16_t serial_diag_flgs, serial_magnitude, test_magnitude; uint8_t test_AGC_value, test_is_Comp_high, test_is_Comp_low; serial_magnitude = encoder_AS504x_get_diag().serial_magnitude; serial_diag_flgs = encoder_AS504x_get_diag().serial_diag_flgs; test_magnitude = serial_magnitude & AS504x_SPI_EXCLUDE_PARITY_AND_ERROR_BITMASK; test_AGC_value = serial_diag_flgs; test_is_Comp_low = (serial_diag_flgs >> AS504x_SPI_DIAG_COMP_LOW_BIT_POS) & 1; test_is_Comp_high = (serial_diag_flgs >> AS504x_SPI_DIAG_COMP_HIGH_BIT_POS) & 1; if (test_is_Comp_high && test_is_Comp_low) { return 1; } if((uint32_t)test_magnitude + (uint32_t)test_AGC_value == 0) { return 1; } return 0; } static void AS504x_deserialize_diag() { AS504x_sensor_diag.AGC_value = AS504x_sensor_diag.serial_diag_flgs; AS504x_sensor_diag.is_OCF = (AS504x_sensor_diag.serial_diag_flgs >> AS504x_SPI_DIAG_OCF_BIT_POS) & 1; AS504x_sensor_diag.is_COF = (AS504x_sensor_diag.serial_diag_flgs >> AS504x_SPI_DIAG_COF_BIT_POS) & 1; AS504x_sensor_diag.is_Comp_low = (AS504x_sensor_diag.serial_diag_flgs >> AS504x_SPI_DIAG_COMP_LOW_BIT_POS) & 1; AS504x_sensor_diag.is_Comp_high = (AS504x_sensor_diag.serial_diag_flgs >> AS504x_SPI_DIAG_COMP_HIGH_BIT_POS) & 1; AS504x_sensor_diag.magnitude = AS504x_sensor_diag.serial_magnitude & AS504x_SPI_EXCLUDE_PARITY_AND_ERROR_BITMASK; } static uint8_t AS504x_spi_transfer_err_check(uint16_t *in_buf, const uint16_t *out_buf, int length) { spi_transfer(in_buf, out_buf, length); for(int len_count = 0; len_count < length; len_count++) { if(((in_buf[len_count]) >> 14) & 0b01) { return 1; } } return 0; } #endif /* * Determinate if is connected depending on last retieved data. */ static void AS504x_determinate_if_connected(bool was_last_valid) { if(!was_last_valid) { AS504x_spi_communication_error_count++; if(AS504x_spi_communication_error_count >= AS504x_CONNECTION_DETERMINATOR_ERROR_THRESHOLD) { AS504x_spi_communication_error_count = AS504x_CONNECTION_DETERMINATOR_ERROR_THRESHOLD; AS504x_sensor_diag.is_connected = 0; } } else { if(AS504x_spi_communication_error_count) { AS504x_spi_communication_error_count--; } else { AS504x_sensor_diag.is_connected = 1; } } } AS504x_diag encoder_AS504x_get_diag(void) { return AS504x_sensor_diag; } void encoder_tim_isr(void) { uint16_t pos; if(mode == ENCODER_MODE_AS5047P_SPI) { // if MOSI is defined, use diagnostics #if AS504x_USE_SW_MOSI_PIN || AS5047_USE_HW_SPI_PINS spi_begin(); spi_transfer(0, 0, 1); spi_end(); spi_AS5047_cs_delay(); spi_begin(); spi_data_err_raised = AS504x_spi_transfer_err_check(&pos, 0, 1); spi_end(); spi_val = pos; // get diagnostic every AS504x_REFRESH_DIAG_AFTER_NSAMPLES AS504x_diag_fetch_now_count++; if(AS504x_diag_fetch_now_count >= AS504x_REFRESH_DIAG_AFTER_NSAMPLES || spi_data_err_raised) { // clear error flags before getting new diagnostics data AS504x_fetch_clear_err_diag(); if(!AS504x_fetch_diag()) { if(!AS504x_verify_serial()) { AS504x_deserialize_diag(); AS504x_determinate_if_connected(true); } else { AS504x_determinate_if_connected(false); } } else { AS504x_determinate_if_connected(false); } AS504x_diag_fetch_now_count = 0; } #else spi_begin(); spi_transfer(&pos, 0, 1); spi_end(); spi_val = pos; if(0x0000 == pos || 0xFFFF == pos) { AS504x_data_last_invalid_counter++; } else { AS504x_data_last_invalid_counter = 0; AS504x_determinate_if_connected(true); } if (AS504x_data_last_invalid_counter >= AS504x_DATA_INVALID_THRESHOLD) { AS504x_determinate_if_connected(false); AS504x_data_last_invalid_counter = AS504x_DATA_INVALID_THRESHOLD; } #endif if(spi_check_parity(pos) && !spi_data_err_raised) { pos &= 0x3FFF; last_enc_angle = ((float)pos * 360.0) / 16384.0; UTILS_LP_FAST(spi_error_rate, 0.0, 1./AS5047_SAMPLE_RATE_HZ); } else { ++spi_error_cnt; UTILS_LP_FAST(spi_error_rate, 1.0, 1./AS5047_SAMPLE_RATE_HZ); } } #ifdef HW_SPI_DEV if(mode == ENCODER_MODE_MT6816_SPI) { uint16_t reg_data_03; uint16_t reg_data_04; uint16_t reg_addr_03 = 0x8300; uint16_t reg_addr_04 = 0x8400; spi_begin(); reg_data_03 = spiPolledExchange(&HW_SPI_DEV, reg_addr_03); spi_end(); spi_delay(); spi_begin(); reg_data_04 = spiPolledExchange(&HW_SPI_DEV, reg_addr_04); spi_end(); pos = (reg_data_03 << 8) | reg_data_04; spi_val = pos; if( spi_check_parity(pos) ) { if (pos & MT6816_NO_MAGNET_ERROR_MASK) { ++encoder_no_magnet_error_cnt; UTILS_LP_FAST(encoder_no_magnet_error_rate, 1.0, 1./MT6816_SAMPLE_RATE_HZ); } else { pos = pos >> 2; last_enc_angle = ((float)pos * 360.0) / 16384.0; UTILS_LP_FAST(spi_error_rate, 0.0, 1./MT6816_SAMPLE_RATE_HZ); UTILS_LP_FAST(encoder_no_magnet_error_rate, 0.0, 1./MT6816_SAMPLE_RATE_HZ); } } else { ++spi_error_cnt; UTILS_LP_FAST(spi_error_rate, 1.0, 1./MT6816_SAMPLE_RATE_HZ); } } #endif if (mode == RESOLVER_MODE_AD2S1205) { // SAMPLE signal should have been be asserted in sync with ADC sampling #ifdef AD2S1205_RDVEL_GPIO palSetPad(AD2S1205_RDVEL_GPIO, AD2S1205_RDVEL_PIN); // Always read position #endif palSetPad(SPI_SW_SCK_GPIO, SPI_SW_SCK_PIN); spi_delay(); spi_begin(); // CS uses the same mcu pin as AS5047 spi_delay(); spi_transfer(&pos, 0, 1); spi_end(); spi_val = pos; uint16_t RDVEL = pos & 0x0008; // 1 means a position read if((RDVEL != 0)){ bool DOS = ((pos & 0x04) == 0); bool LOT = ((pos & 0x02) == 0); bool LOS = DOS && LOT; bool parity_error = spi_check_parity(pos); //16 bit frame has odd parity bool angle_is_correct = true; if(LOS) { LOT = DOS = 0; } if(!parity_error) { UTILS_LP_FAST(spi_error_rate, 0.0, 1./AD2S1205_SAMPLE_RATE_HZ); } else { angle_is_correct = false; ++spi_error_cnt; UTILS_LP_FAST(spi_error_rate, 1.0, 1./AD2S1205_SAMPLE_RATE_HZ); } pos &= 0xFFF0; pos = pos >> 4; pos &= 0x0FFF; if(LOT) { angle_is_correct = false; ++resolver_loss_of_tracking_error_cnt; UTILS_LP_FAST(resolver_loss_of_tracking_error_rate, 1.0, 1./AD2S1205_SAMPLE_RATE_HZ); } else { UTILS_LP_FAST(resolver_loss_of_tracking_error_rate, 0.0, 1./AD2S1205_SAMPLE_RATE_HZ); } if(DOS) { angle_is_correct = false; ++resolver_degradation_of_signal_error_cnt; UTILS_LP_FAST(resolver_degradation_of_signal_error_rate, 1.0, 1./AD2S1205_SAMPLE_RATE_HZ); } else { UTILS_LP_FAST(resolver_degradation_of_signal_error_rate, 0.0, 1./AD2S1205_SAMPLE_RATE_HZ); } if(LOS) { angle_is_correct = false; ++resolver_loss_of_signal_error_cnt; UTILS_LP_FAST(resolver_loss_of_signal_error_rate, 1.0, 1./AD2S1205_SAMPLE_RATE_HZ); } else { UTILS_LP_FAST(resolver_loss_of_signal_error_rate, 0.0, 1./AD2S1205_SAMPLE_RATE_HZ); } if(angle_is_correct) { last_enc_angle = ((float)pos * 360.0) / 4096.0; } } } } /** * Set the number of encoder counts. * * @param counts * The number of encoder counts */ void encoder_set_counts(uint32_t counts) { if (counts != enc_counts) { enc_counts = counts; TIM_SetAutoreload(HW_ENC_TIM, enc_counts - 1); index_found = false; } } /** * Check if the index pulse is found. * * @return * True if the index is found, false otherwise. */ bool encoder_index_found(void) { return index_found; } // Software SPI static void spi_transfer(uint16_t *in_buf, const uint16_t *out_buf, int length) { for (int i = 0;i < length;i++) { #if AS504x_USE_SW_MOSI_PIN || AS5047_USE_HW_SPI_PINS uint16_t send = out_buf ? out_buf[i] : 0xFFFF; #else (void)out_buf; #endif uint16_t receive = 0; for (int bit = 0;bit < 16;bit++) { #if AS504x_USE_SW_MOSI_PIN || AS5047_USE_HW_SPI_PINS palWritePad(SPI_SW_MOSI_GPIO, SPI_SW_MOSI_PIN, send >> (15 - bit)); #endif palSetPad(SPI_SW_SCK_GPIO, SPI_SW_SCK_PIN); spi_delay(); int samples = 0; samples += palReadPad(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN); __NOP(); samples += palReadPad(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN); __NOP(); samples += palReadPad(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN); __NOP(); samples += palReadPad(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN); __NOP(); samples += palReadPad(SPI_SW_MISO_GPIO, SPI_SW_MISO_PIN); receive <<= 1; if (samples > 2) { receive |= 1; } palClearPad(SPI_SW_SCK_GPIO, SPI_SW_SCK_PIN); spi_delay(); } if (in_buf) { in_buf[i] = receive; } } } static void spi_begin(void) { palClearPad(SPI_SW_CS_GPIO, SPI_SW_CS_PIN); spi_AS5047_cs_delay(); } static void spi_end(void) { palSetPad(SPI_SW_CS_GPIO, SPI_SW_CS_PIN); spi_AS5047_cs_delay(); } static void spi_delay(void) { __NOP(); __NOP(); __NOP(); __NOP(); } static void spi_AS5047_cs_delay(void) { __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP();__NOP();__NOP(); __NOP(); } #pragma GCC push_options #pragma GCC optimize ("O0") void TS5700N8501_delay_uart(void) { __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); } /* * It is important to switch to receive mode immediately after sending the readout command, * as the TS5700N8501 starts sending the reply after 3 microseconds. Therefore use software * UART on TX so that the enable signal can be controlled manually. This function runs while * the system is locked, but it should finish fast enough to not cause problems for other * things due to the high baud rate. */ static void TS5700N8501_send_byte(uint8_t b) { utils_sys_lock_cnt(); #ifdef HW_ADC_EXT_GPIO palSetPad(HW_ADC_EXT_GPIO, HW_ADC_EXT_PIN); #endif TS5700N8501_delay_uart(); palWritePad(HW_UART_TX_PORT, HW_UART_TX_PIN, 0); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); for (int i = 0;i < 8;i++) { palWritePad(HW_UART_TX_PORT, HW_UART_TX_PIN, (b & (0x80 >> i)) ? PAL_HIGH : PAL_LOW); TS5700N8501_delay_uart(); } __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); palWritePad(HW_UART_TX_PORT, HW_UART_TX_PIN, 1); TS5700N8501_delay_uart(); #ifdef HW_ADC_EXT_GPIO palClearPad(HW_ADC_EXT_GPIO, HW_ADC_EXT_PIN); #endif utils_sys_unlock_cnt(); } #pragma GCC pop_options static THD_FUNCTION(ts5700n8501_thread, arg) { (void)arg; chRegSetThreadName("TS5700N8501"); sdStart(&HW_UART_DEV, &TS5700N8501_uart_cfg); palSetPadMode(HW_UART_TX_PORT, HW_UART_TX_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_PULLUP); palSetPadMode(HW_UART_RX_PORT, HW_UART_RX_PIN, PAL_MODE_ALTERNATE(HW_UART_GPIO_AF) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_PULLUP); #ifdef HW_ADC_EXT_GPIO palSetPadMode(HW_ADC_EXT_GPIO, HW_ADC_EXT_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_PULLUP); #endif for(;;) { // Check if it is time to stop. if (ts5700n8501_stop_now) { ts5700n8501_is_running = false; return; } if (ts5700n8501_reset_errors) { for (int i = 0;i < 20;i++) { TS5700N8501_send_byte(0b01011101); chThdSleep(2); } ts5700n8501_reset_errors = false; } if (ts5700n8501_reset_multiturn) { for (int i = 0;i < 20;i++) { TS5700N8501_send_byte(0b01000110); chThdSleep(2); } ts5700n8501_reset_multiturn = false; } TS5700N8501_send_byte(0b01011000); chThdSleep(2); uint8_t reply[11]; int reply_ind = 0; msg_t res = sdGetTimeout(&HW_UART_DEV, TIME_IMMEDIATE); while (res != MSG_TIMEOUT) { if (reply_ind < (int)sizeof(reply)) { reply[reply_ind++] = res; } res = sdGetTimeout(&HW_UART_DEV, TIME_IMMEDIATE); } uint8_t crc = 0; for (int i = 0;i < (reply_ind - 1);i++) { crc = (reply[i] ^ crc); } if (reply_ind == 11 && crc == reply[reply_ind - 1]) { uint32_t pos = (uint32_t)reply[2] + ((uint32_t)reply[3] << 8) + ((uint32_t)reply[4] << 16); spi_val = pos; last_enc_angle = (float)pos / 131072.0 * 360.0; UTILS_LP_FAST(spi_error_rate, 0.0, 1.0 / AS5047_SAMPLE_RATE_HZ); ts5700n8501_raw_status[0] = reply[1]; // SF ts5700n8501_raw_status[1] = reply[2]; // ABS0 ts5700n8501_raw_status[2] = reply[3]; // ABS1 ts5700n8501_raw_status[3] = reply[4]; // ABS2 ts5700n8501_raw_status[4] = reply[6]; // ABM0 ts5700n8501_raw_status[5] = reply[7]; // ABM1 ts5700n8501_raw_status[6] = reply[8]; // ABM2 ts5700n8501_raw_status[7] = reply[9]; // ALMC } else { ++spi_error_cnt; UTILS_LP_FAST(spi_error_rate, 1.0, 1.0 / AS5047_SAMPLE_RATE_HZ); } } }