Files
beyon-motion/TMC2209/lib/tmc/tmc/IdDetection_LandungsbrueckeV3.c
2026-03-31 13:10:37 +02:00

447 lines
14 KiB
C

/*******************************************************************************
* Copyright © 2023 Analog Devices Inc. All Rights Reserved. This software is
* proprietary & confidential to Analog Devices, Inc. and its licensors.
*******************************************************************************/
/*
* Calling IDDetection_detect(IdAssignmentTypeDef *result) will start the ID detection process.
* The function returns the ID Results through the IdAssignmentTypeDef struct result points to.
* The detection will be done by monoflop pulse duration measurement or via EEPROM readout.
* While this process is still ongoing the function will return ID_STATE_WAIT_HIGH. Once the
* ID detection of both channels has been finished, ID_STATE_DONE will be returned.
*
* Calling the function again after the detection has finished will start another scan.
*/
#include "IdDetection.h"
#include "hal/derivative.h"
#include "hal/HAL.h"
#include "BoardAssignment.h"
#include "EEPROM.h"
#include "IdDetection.h"
#include "VitalSignsMonitor.h"
#include "TMCL.h"
// Helper functions
static int32_t detectID_Monoflop(IdAssignmentTypeDef *ids);
static int32_t detectID_EEPROM(IdAssignmentTypeDef *ids);
// Helper macros
#define ID_CLK_LOW() HAL.IOs->config->setLow(&HAL.IOs->pins->ID_CLK) // set id clk signal to low
#define ID_CLK_HIGH() HAL.IOs->config->setHigh(&HAL.IOs->pins->ID_CLK) // set id clk signal to high
#define ID_CLK_STATE (HAL.IOs->config->getState(&HAL.IOs->pins->ID_CLK) == IOS_HIGH) // get id clk signal level
#define ID_CH0_STATE (HAL.IOs->config->getState(&HAL.IOs->pins->ID_CH0) == IOS_HIGH) // get id signal level for this channel
#define ID_CH1_STATE (HAL.IOs->config->getState(&HAL.IOs->pins->ID_CH1) == IOS_HIGH) // get id signal level for this channel
#define IDSTATE_SCAN_DONE(ID_STATE) \
( \
(ID_STATE.ch1.state != ID_STATE_WAIT_LOW) && \
(ID_STATE.ch2.state != ID_STATE_WAIT_LOW) && \
(ID_STATE.ch1.state != ID_STATE_WAIT_HIGH) && \
(ID_STATE.ch2.state != ID_STATE_WAIT_HIGH) \
)
static uint8_t assign(uint32_t pulse);
typedef enum {
MONOFLOP_INIT,
MONOFLOP_SCANNING,
MONOFLOP_DONE,
MONOFLOP_END
} State_MonoflopDetection;
State_MonoflopDetection monoflopState = MONOFLOP_INIT;
IdAssignmentTypeDef IdState = { 0 };
/* pin changed interrupt to detect edges of ID pulse for both channels */
void PC7_8_IRQHandler(void)
{
// Abort if the EXTI line isn't set
if((exti_flag_get(EXTI_8) != SET) && (exti_flag_get(EXTI_7) != SET)){
return;
}
if(exti_flag_get(EXTI_8) == SET)
{
exti_flag_clear(EXTI_8);
// Abort if we're not scanning
if(monoflopState != MONOFLOP_SCANNING)
return;
if(ID_CH0_STATE) // Capture time of rising edge on ID_CH0
{
TIMER_SWEVG(TIMER1) |= TIMER_SWEVG_CH0G;
IdState.ch1.state = ID_STATE_WAIT_HIGH;
}
else // Capture time of falling edge on ID_CH0
{
TIMER_SWEVG(TIMER1) |= TIMER_SWEVG_CH1G;
IdState.ch1.state = ID_STATE_DONE;
}
}
else if(exti_flag_get(EXTI_7) == SET)
{
exti_flag_clear(EXTI_7);
// Abort if we're not scanning
if(monoflopState != MONOFLOP_SCANNING)
return;
if(ID_CH1_STATE) // Capture time of rising edge on ID_CH1
{
TIMER_SWEVG(TIMER1) = TIMER_SWEVG_CH2G;
IdState.ch2.state = ID_STATE_WAIT_HIGH;
}
else // Capture time of falling edge on ID_CH1
{
TIMER_SWEVG(TIMER1) = TIMER_SWEVG_CH3G;
IdState.ch2.state = ID_STATE_DONE;
}
}
}
void __attribute__ ((interrupt)) EXTI5_9_IRQHandler(void)
{
if(GET_BITS(SYSCFG_EXTISS2,0,3) == 3){
PD8_IRQHandler(); // For TMC6140-eval diagnostics
}
else if(GET_BITS(SYSCFG_EXTISS2,0,3) == 2 || GET_BITS(SYSCFG_EXTISS1,12,15) == 2){
PC7_8_IRQHandler(); // For idDetection
}
}
/* timer interrupt to determine timeout on ID detection (missing boards, errors) */
void TIMER1_IRQHandler(void)
{
// Abort if the interrupt isn't set
if(timer_flag_get(TIMER1, TIMER_FLAG_UP) != SET)
return;
timer_flag_clear(TIMER1, TIMER_FLAG_UP);
if(monoflopState == MONOFLOP_SCANNING)
{
monoflopState = MONOFLOP_DONE;
return;
}
}
/* Initialise timer and GPIO edge-triggered interrupts */
void IDDetection_init(void)
{
monoflopState = MONOFLOP_INIT;
// ====== Pin initialisation ======
// Pin ID_CLK
HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CLK);
// Pin ID_CH0
gpio_mode_set(HAL.IOs->pins->ID_CH0.port, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, HAL.IOs->pins->ID_CH0.bitWeight);
syscfg_exti_line_config(EXTI_SOURCE_GPIOC, EXTI_SOURCE_PIN8);
exti_init(EXTI_8, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
nvic_irq_enable(EXTI5_9_IRQn, 0, 1);
exti_interrupt_flag_clear(EXTI_8);
// Pin ID_CH1
gpio_mode_set(HAL.IOs->pins->ID_CH1.port, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, HAL.IOs->pins->ID_CH1.bitWeight);
syscfg_exti_line_config(EXTI_SOURCE_GPIOC, EXTI_SOURCE_PIN7);
exti_init(EXTI_7, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
nvic_irq_enable(EXTI5_9_IRQn, 0, 1);
exti_interrupt_flag_clear(EXTI_7);
// ====== Timer initialisation
/* Timer Frequency:
* CLK_TIMER 120MHz
* ----------- = ----- = 10MHz = 0.1us <= COUNTER_CLK
* Prescaler 12
*
*/
rcu_periph_clock_enable(RCU_TIMER1);
rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL2); // CK_TIMER1 = 2 x CK_APB1
timer_internal_clock_config(TIMER1);
timer_deinit(TIMER1);
timer_counter_value_config(TIMER1, 0);
timer_ic_parameter_struct *icpara_rising;
timer_ic_parameter_struct *icpara_falling;
icpara_rising->icpolarity = TIMER_IC_POLARITY_RISING;
icpara_rising->icselection = TIMER_IC_SELECTION_DIRECTTI;
icpara_rising->icprescaler = TIMER_IC_PSC_DIV1;
icpara_rising->icfilter = 0U;
icpara_falling->icpolarity = TIMER_IC_POLARITY_FALLING;
icpara_falling->icselection = TIMER_IC_SELECTION_DIRECTTI;
icpara_falling->icprescaler = TIMER_IC_PSC_DIV1;
icpara_falling->icfilter = 0U;
timer_input_capture_config(TIMER1, TIMER_CH_0, icpara_rising);
timer_input_capture_config(TIMER1, TIMER_CH_1, icpara_falling);
timer_input_capture_config(TIMER1, TIMER_CH_2, icpara_rising);
timer_input_capture_config(TIMER1, TIMER_CH_3, icpara_falling);
// Setting the prescaler i.e 11
timer_prescaler_config(TIMER1, 11, TIMER_PSC_RELOAD_NOW); // Divides the timer freq by (prescaler + 1)
timer_autoreload_value_config(TIMER1,100000); // timeout -> 10ms --TIM auto-reload register
timer_update_event_enable(TIMER1);
TIMER_SWEVG(TIMER1) |= (uint32_t)TIMER_SWEVG_UPG; //generate update event
timer_interrupt_enable(TIMER1, TIMER_INT_UP);
// Enable timer interrupt
nvic_irq_enable(TIMER1_IRQn, 0xF, 0xF);
}
void IDDetection_deInit()
{
nvic_irq_disable(TIMER1_IRQn);
nvic_irq_disable(EXTI5_9_IRQn);
timer_deinit(TIMER1);
exti_deinit();
}
// Returns ID assigned to given pulse (length in 0.1us)
static uint8_t assign(uint32_t pulse)
{
if( pulse < 5) return 0; // error
else if(pulse < 110) return 1;
else if(pulse < 135) return 2;
else if(pulse < 165) return 3;
else if(pulse < 200) return 4;
else if(pulse < 245) return 5;
else if(pulse < 300) return 6;
else if(pulse < 360) return 7;
else if(pulse < 430) return 8;
else if(pulse < 515) return 9;
else if(pulse < 620) return 10;
else if(pulse < 750) return 11;
else if(pulse < 910) return 12;
else if(pulse < 1100) return 13;
else if(pulse < 1350) return 14;
else if(pulse < 1650) return 15;
else if(pulse < 2000) return 16;
else if(pulse < 2450) return 17;
else if(pulse < 3000) return 18;
else if(pulse < 3600) return 19;
else if(pulse < 4300) return 20;
else if(pulse < 5150) return 21;
else if(pulse < 6200) return 22;
else if(pulse < 7500) return 23;
else if(pulse < 9100) return 24;
else if(pulse < 11000) return 25;
else if(pulse < 13500) return 26;
else if(pulse < 16500) return 27;
else if(pulse < 20000) return 28;
else if(pulse < 24500) return 29;
else if(pulse < 30000) return 30;
else if(pulse < 36000) return 31;
else if(pulse < 43000) return 32;
else if(pulse < 51500) return 33;
else if(pulse < 62000) return 34;
else if(pulse < 75000) return 35;
else if(pulse < 91000) return 36;
return 0; // error
}
// Detect IDs of attached boards - returns true when done
uint8_t IDDetection_detect(IdAssignmentTypeDef *ids)
{
// Change the exti source back to PC8 (It could be changed by TMC6140-eval to PD8)
// syscfg_exti_line_config(EXTI_SOURCE_GPIOC, EXTI_SOURCE_PIN8);
// Try to identify the IDs via monoflop pulse duration
if (!detectID_Monoflop(ids))
return false;
// Try to identify the IDs via EEPROM readout
detectID_EEPROM(ids);
// Detection finished
return true;
}
void IDDetection_initialScan(IdAssignmentTypeDef *ids)
{
while(!IDDetection_detect(ids))
{
vitalsignsmonitor_checkVitalSigns();
tmcl_process();
}
}
static int32_t detectID_Monoflop(IdAssignmentTypeDef *ids)
{
switch(monoflopState)
{
case MONOFLOP_INIT:
timer_disable(TIMER1); // stop the timer
timer_counter_value_config(TIMER1, 0); // clear counter
IdState.ch1.state = ID_STATE_WAIT_LOW;
IdState.ch1.detectedBy = FOUND_BY_NONE;
IdState.ch2.state = ID_STATE_WAIT_LOW;
IdState.ch2.detectedBy = FOUND_BY_NONE;
// Update the monoflop state before activating the timer. Otherwise bad
// luck with other unrelated interrupts might cause enough delay to
// trigger the timer overflow after starting the timer before updating
// this state - which results in the timeout no longer working.
monoflopState = MONOFLOP_SCANNING;
timer_enable(TIMER1); // start timer
ID_CLK_HIGH();
break;
case MONOFLOP_SCANNING:
if (IDSTATE_SCAN_DONE(IdState))
{
monoflopState = MONOFLOP_DONE;
}
break;
case MONOFLOP_DONE:
// Scan complete, disable ID_CLK and the timer
ID_CLK_LOW();
timer_disable(TIMER1);
// ======== CH1 ==========
// Assign ID detection state for this channel
ids->ch1.state = IdState.ch1.state;
if(IdState.ch1.state == ID_STATE_DONE)
{
uint32_t pulse=timer_channel_capture_value_register_read(TIMER1, TIMER_CH_1) - timer_channel_capture_value_register_read(TIMER1, TIMER_CH_0);
// Assign the ID derived from the ID pulse duration
ids->ch1.id = assign(pulse);
if(ids->ch1.id)
IdState.ch1.detectedBy = FOUND_BY_MONOFLOP;
else
ids->ch1.state = ID_STATE_INVALID; // Invalid ID pulse detected
}
else if(IdState.ch1.state == ID_STATE_WAIT_HIGH)
{ // Only detected ID pulse rising edge -> Timeout
IdState.ch1.state = ID_STATE_TIMEOUT;
}
else if(IdState.ch1.state == ID_STATE_WAIT_LOW)
{ // Did not detect any edge -> No answer
IdState.ch1.state = ID_STATE_NO_ANSWER;
}
else
{
ids->ch1.id = 0;
}
// ======== CH2 ==========
// Assign ID detection state for this channel
ids->ch2.state = IdState.ch2.state;
if(IdState.ch2.state == ID_STATE_DONE)
{
// Assign the ID derived from the ID pulse duration
ids->ch2.id = assign(timer_channel_capture_value_register_read(TIMER1, TIMER_CH_3) - timer_channel_capture_value_register_read(TIMER1, TIMER_CH_2));
if(ids->ch2.id)
IdState.ch2.detectedBy = FOUND_BY_MONOFLOP;
else
ids->ch2.state = ID_STATE_INVALID; // Invalid ID pulse detected
}
else if(IdState.ch2.state == ID_STATE_WAIT_HIGH)
{ // Only detected ID pulse rising edge -> Timeout
IdState.ch2.state = ID_STATE_TIMEOUT;
}
else if(IdState.ch2.state == ID_STATE_WAIT_LOW)
{ // Did not detect any edge -> No answer
IdState.ch2.state = ID_STATE_NO_ANSWER;
}
else
{
ids->ch2.id = 0;
}
monoflopState = MONOFLOP_INIT;
return true;
break;
default:
break;
}
return false;
}
static int32_t detectID_EEPROM(IdAssignmentTypeDef *ids)
{
// ====== EEPROM Check ======
// EEPROM spec reserves 2 bytes for the ID buffer.
// Currently we only use one byte for IDs, both here in the firmware
// and in the IDE - once we deplete that ID pool, this needs to be extended
// (uint8_t to uint16_t and change EEPROM read to read two bytes instead of one)
uint8_t idBuffer[2];
// ====== CH1 ======
if(ids->ch1.state != ID_STATE_DONE)
{
// EEPROM is not ready -> assume it is not connected -> skip EEPROM ID read
if(!eeprom_check(&SPI.ch1))
{
eeprom_read_array(&SPI.ch1, EEPROM_ADDR_ID, &idBuffer[0], 1);
ids->ch1.id = idBuffer[0];
// ID was correctly detected via EEPROM
if(ids->ch1.id)
{
ids->ch1.state = ID_STATE_DONE;
IdState.ch1.detectedBy = FOUND_BY_EEPROM;
}
}
// EEPROM access changes the ID_CH0 pin configuration -> write it again // todo CHECK 2: workaround, do this better later (LH) #3
HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CH0);
}
// ====== CH2 ======
if(ids->ch2.state != ID_STATE_DONE)
{
// EEPROM is not ready -> assume it is not connected -> skip EEPROM ID read
if(!eeprom_check(&SPI.ch2))
{
eeprom_read_array(&SPI.ch2, EEPROM_ADDR_ID, &idBuffer[0], 1);
ids->ch2.id = idBuffer[0];
// ID was correctly detected via EEPROM
if(ids->ch2.id)
{
ids->ch2.state = ID_STATE_DONE;
IdState.ch2.detectedBy = FOUND_BY_EEPROM;
}
}
// EEPROM access changes the ID_CH1 pin configuration -> write it again // todo CHECK 2: workaround, do this better later (LH) #4
HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CH1);
}
return true;
}