End switches an interrupts

Hi,

is there any progress integrating the end switches?

I tried to implement a interrupt routine for GPIO_5, but without luck. Maybe somebody can tell me what I’m doing wrong. I’m not really confirmed with the STM32. In the layout of my 3.5 board, GPIO_5 is connected to PC4, so I have to use EXTI4_IRQHandler.

First I added a 10k pullup resistor to the GPIO port and 3.3V.

In the code (main.cpp) (int odrive_main(void) )
I added the code below to activate the interrupt routine for GPIO_5.

I saw, that the initialization is done. But when I push the end switch, the voltage at the port changes from 3.3 to 0V, but no interrupt was fired.

#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 5
GPIO_InitStruct.Pin = GPIO_5_Pin; //GPIO5 ist PC4
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
HAL_GPIO_Init(GPIO_5_GPIO_Port, &GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI4_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
#endif

And a Interrupt routine:

void EXTI4_IRQHandler(void)
{
if (__HAL_GPIO_EXTI_GET_FLAG(GPIO_PIN_5))
{
HAL_NVIC_DisableIRQ(EXTI4_IRQn);
//if during control loop, do a emergency stop,
//else set position to zero
}
//IRQ löschen
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
}

Any idea?
Thanks Zennix

I was about to look into endstops this week. Don’t let me forget.

Great :clap: My switches are connected. I’ m ready for testing.
@Wetmelon Do you think one GPIO for both switches at one actuator is enough or do you prefere to use two seperate GPIOS for bottom and top position?

Greetings Zennix

You can probably just use one switch for home per actuator. Don’t really need two switches on each.

Hi Wetmelon,

I have seen you are working at this topic.
Task List

  • [ ] Add hardware support
  • [ ] Add callbacks
  • [ ] Homing speed limit
  • [ ] Homing function
  • [ ] Fault on endstop pressed flag

One question:
The last item in the list is for emergency. My actuators have two limit switches each. So one for home position (down limit) and the other for the upper limit. Could you please add an additional input to detect the upper limit too?
Otherwise I have to detect it externaly.

Thanks / Zennix

For reference, here is the task list.

Hi Zennix,

The software as written supports MIN and MAX endstops. So it’ll support both (2 per axis) if configured :slight_smile:

Hi,

great :+1:
Is it ready for testing? Devel branch?

Greetings / Zennix

Hey, sorry about the delay. Please try this branch https://github.com/Wetmelon/ODrive/tree/Endstops

You’ll need to compile from scratch and upload with an STLink, unless there’s some way for you to use DFU to pull this pull request? https://github.com/madcowswe/ODrive/pull/218

Once you flash the controller, you can configure it normally, with the addition of the following endstop commands:

odrv#.axis#.config.max_endstop.gpio_num = <1, 2, 3, 4, 5>
odrv#.axis#.config.max_endstop.enabled = <True, False>
odrv#.axis#.config.max_endstop.offset = <int>
odrv#.axis#.config.max_endstop.is_active_high = <True, False>

odrv#.axis#.config.min_endstop.gpio_num = <1, 2, 3, 4, 5>
odrv#.axis#.config.min_endstop.enabled = <True, False>
odrv#.axis#.config.min_endstop.offset = <int>
odrv#.axis#.config.min_endstop.is_active_high = <True, False>

gpio_num and enabled should be self explanatory. Make sure to disable step/dir or UART communications as necessary.

offset can be set as a number of counts. It is used as the “home switch position”. In other words, if your min_endstop is 100 counts further than what you want 0 to be, set this value to -100. This is really only currently valid for min_endstop.

is_active_high sets the polarity of the endstop. If when the switch is pressed it pulls the pin LOW (this is very common for endstop switches), you want to set this to “False”.

1 Like

Hi Wetmelon,

thanks for the branch. It compiles and the ODrive is running as expected.
My configuration is:

odrv#.axis#.config.max_endstop.gpio_num = 6
odrv#.axis#.config.max_endstop.enabled = True
odrv#.axis#.config.max_endstop.offset = -100
odrv#.axis#.config.max_endstop.is_active_high = False
_
odrv#.axis#.config.min_endstop.gpio_num = 5
odrv#.axis#.config.min_endstop.enabled = True
odrv#.axis#.config.min_endstop.offset = -100
odrv#.axis#.config.min_endstop.is_active_high = False

my switches are active low, but I have no external pullup.

Is there a command for homing the axis?
I tried to press a switch during a running position command, but nothing happend. The motor was still running.

Greetings / Zennix

Looking at the code from the PR, I believe you want to set requested_state to AXIS_STATE_HOMING to begin the process.

Please pull the newest version of the firmware, there was a bug where it wouldn’t initialize any GPIO above #5. The internal pull up / down is set automatically depending on the polarity of the endstops.

I’ve added <axis>.config.startup_homing = <True, False> as an option. If both <axis>.config.startup_closed_loop_control and <axis>.config.startup_homing are True, then ODrive will home the axis after calibration. Otherwise, you can enter homing by setting <axis>.requested_state = AXIS_STATE_HOMING

After setting up your configuration, please remember to do <odrv>.save_configuration() then power cycle the controller.

1 Like

Hi Wetmelon,

Thank you.
I pulled the last version and here is my configuration

startup_motor_calibration = True (bool)
startup_encoder_index_search = True (bool)
startup_encoder_offset_calibration = True (bool)
startup_closed_loop_control = True (bool)
startup_sensorless_control = False (bool)
startup_homing = True (bool)
enable_step_dir = False (bool)
counts_per_step = 2.0 (float)
ramp_up_time = 0.4000000059604645 (float)
ramp_up_distance = 12.566370964050293 (float)
spin_up_current = 10.0 (float)
spin_up_acceleration = 400.0 (float)
spin_up_target_vel = 400.0 (float)
min_endstop:
gpio_num = 5 (int)
enabled = True (bool)
offset = -100 (int)
is_active_high = False (bool)
max_endstop:
gpio_num = 6 (int)
enabled = True (bool)
offset = -100 (int)
is_active_high = False (bool)

I set .config.startup_homing = True and .config.startup_closed_loop_control to True. Saved it and start. It does only the full calibration sequence. Nothing more.

The command .requested_state = AXIS_STATE_HOMING ends with:

In [11]: odrv0.axis0.requested_state = AXIS_STATE_HOMING
NameError Traceback (most recent call last)
d:\tools\Anaconda3\lib\site-packages\fibre\shell.py in ()
----> 1 odrv0.axis0.requested_state =
NameError: name ‘AXIS_STATE_HOMING’ is not defined

Is there a way to see if the switches are pressed?

Greetings / Zennix

Hi Wetmelon,

I tried the switches also at Pin 3 and 4. The same result. I pressed one of the switches and checked odrv.axis0.
I thought min_ or max_endstop_state should be true.

B [25]: odrv0.axis0
Out[25]:
error = 0x0000 (int)
enable_step_dir = False (bool)
current_state = 1 (int)
requested_state = 0 (int)
loop_counter = 318529 (int)
min_endstop_state = False (bool)
max_endstop_state = False (bool)

Greetings / Zennix

Make sure you’re using the odrivetool that comes with the repository (v0.4.2.dev) rather than the one that comes from pip. That should fix the AXIS_STATE_HOMING issue. If you don’t want to do that, you can use .requested_state = 9

As for it not seeing the endstops… will investigate.

EDIT: I found a few issues… It will take me another day to clean up, but it’s definitely working (at least on GPIO 5). There’s still a bug where it immediately faults out because it goes into AXIS_STATE_CLOSED_LOOP_CONTROL while the homing switch is still pressed

Ok, the absolute newest build should work. I tested it on my bench and it seems to work correctly.

I recommend using a GPIO pin above #4 (so 5, 6, 7, or 8 on v3.5)

Hi Wetmelon,

looks good, thank you.

I tested GPIO 3, 4, 5 and 6, there are recognised.
Automatic homing is working too.
Reaching an endstop during a run is also working. It stops the motor.
manual homing is also working.

one finding:
I saw, that about every second try the homing function will not work. The motor initializes, searches home. If the min button is pressed the motor stops and will not go to it’s offset.
In [2]: odrv0.axis0.error
Out[2]: 2049

BTW: how to start the new ODrivetool in a Windows environment without installing it through pip?

Great work!!!

Greetings / Zennix

Hi,

another finding:
endstop_max gpio 8 and endstop_min gpio 7 are not working correct. Config is showing correct, pressing the max switch, the axis will show the min switch is pressed. The min switch at 7 will not be recognised.

Greetings / Zennix

Error 2049 is 0x801, which is what happens when the min endstop is pressed while in “run” state. I suspect what’s happening is that it has insufficient debouncing so it moves through the entire set of states all in one press. I may be able to fix this in software, but one quick and easy way to fix it is to put a decoupling capacitor or little RC filter on the GPIO.

You can start the new ODrivetool by manually navigating to the ODrive/tools folder and calling ipython -i ./odrivetool

What happens if you set max_endstop.enabled = False?

Hi,

OK, thank you. I will try to use a RC filter later on.

Here is the test with disabled max_endstop.

min_endstop:
gpio_num = 7 (int)
enabled = True (bool)
offset = -10000 (int)
is_active_high = False (bool)
max_endstop:
gpio_num = 8 (int)
enabled = False (bool)
offset = -100 (int)
is_active_high = False (bool)

Max_endstop switch is pressed:

In [9]: odrv0.axis0
Out[9]:
error = 0x0801 (int)
enable_step_dir = False (bool)
current_state = 1 (int)
requested_state = 0 (int)
loop_counter = 175924 (int)
min_endstop_state = True (bool)
max_endstop_state = False (bool)

Greetings / Zennix