Merge pull request #973 from dhalbert/3.x_onewire_fix

fix OneWire timing and DigitalInOut.switch_to_input()
This commit is contained in:
Scott Shawcroft 2018-06-29 13:43:50 -07:00 committed by GitHub
commit f1248dcdf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 72 additions and 19 deletions

View File

@ -60,7 +60,7 @@ void common_hal_digitalio_digitalinout_deinit(digitalio_digitalinout_obj_t* self
void common_hal_digitalio_digitalinout_switch_to_input( void common_hal_digitalio_digitalinout_switch_to_input(
digitalio_digitalinout_obj_t* self, digitalio_pull_t pull) { digitalio_digitalinout_obj_t* self, digitalio_pull_t pull) {
self->output = false; self->output = false;
// This also sets direction to input.
common_hal_digitalio_digitalinout_set_pull(self, pull); common_hal_digitalio_digitalinout_set_pull(self, pull);
} }
@ -69,13 +69,13 @@ void common_hal_digitalio_digitalinout_switch_to_output(
digitalio_drive_mode_t drive_mode) { digitalio_drive_mode_t drive_mode) {
const uint8_t pin = self->pin->pin; const uint8_t pin = self->pin->pin;
gpio_set_pin_pull_mode(pin, GPIO_PULL_OFF); gpio_set_pin_pull_mode(pin, GPIO_PULL_OFF);
gpio_set_pin_direction(pin, GPIO_DIRECTION_OUT);
// Turn on "strong" pin driving (more current available). See DRVSTR doc in datasheet. // Turn on "strong" pin driving (more current available). See DRVSTR doc in datasheet.
hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(pin), GPIO_PIN(pin)); hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(pin), GPIO_PIN(pin));
self->output = true; self->output = true;
self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN; self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN;
// Direction is set in set_value. We don't need to do it here.
common_hal_digitalio_digitalinout_set_value(self, value); common_hal_digitalio_digitalinout_set_value(self, value);
} }
@ -86,16 +86,22 @@ digitalio_direction_t common_hal_digitalio_digitalinout_get_direction(
void common_hal_digitalio_digitalinout_set_value( void common_hal_digitalio_digitalinout_set_value(
digitalio_digitalinout_obj_t* self, bool value) { digitalio_digitalinout_obj_t* self, bool value) {
const uint8_t pin = self->pin->pin;
const uint8_t port = GPIO_PORT(pin);
const uint32_t pin_mask = 1U << GPIO_PIN(pin);
if (value) { if (value) {
if (self->open_drain) { if (self->open_drain) {
gpio_set_pin_direction(self->pin->pin, GPIO_DIRECTION_IN); // Assertion: pull is off, so the pin is floating in this case.
// We do open-drain high output (no sinking of current)
// by changing the direction to input with no pulls.
hri_port_clear_DIR_DIR_bf(PORT, port, pin_mask);
} else { } else {
gpio_set_pin_level(self->pin->pin, true); hri_port_set_DIR_DIR_bf(PORT, port, pin_mask);
gpio_set_pin_direction(self->pin->pin, GPIO_DIRECTION_OUT); hri_port_set_OUT_OUT_bf(PORT, port, pin_mask);
} }
} else { } else {
gpio_set_pin_level(self->pin->pin, false); hri_port_set_DIR_DIR_bf(PORT, port, pin_mask);
gpio_set_pin_direction(self->pin->pin, GPIO_DIRECTION_OUT); hri_port_clear_OUT_OUT_bf(PORT,port, pin_mask);
} }
} }
@ -105,10 +111,10 @@ bool common_hal_digitalio_digitalinout_get_value(
if (!self->output) { if (!self->output) {
return gpio_get_pin_level(pin); return gpio_get_pin_level(pin);
} else { } else {
if (self->open_drain && hri_port_get_DIR_reg(PORT, (enum gpio_port)GPIO_PORT(pin), 1U << GPIO_PIN(pin)) == 0) { if (self->open_drain && hri_port_get_DIR_reg(PORT, GPIO_PORT(pin), 1U << GPIO_PIN(pin)) == 0) {
return true; return true;
} else { } else {
return hri_port_get_OUT_reg(PORT, (enum gpio_port)GPIO_PORT(pin), 1U << GPIO_PIN(pin)); return hri_port_get_OUT_reg(PORT, GPIO_PORT(pin), 1U << GPIO_PIN(pin));
} }
} }
} }
@ -119,7 +125,7 @@ void common_hal_digitalio_digitalinout_set_drive_mode(
bool value = common_hal_digitalio_digitalinout_get_value(self); bool value = common_hal_digitalio_digitalinout_get_value(self);
self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN; self->open_drain = drive_mode == DRIVE_MODE_OPEN_DRAIN;
// True is implemented differently between modes so reset the value to make // True is implemented differently between modes so reset the value to make
// sure its correct for the new mode. // sure it's correct for the new mode.
if (value) { if (value) {
common_hal_digitalio_digitalinout_set_value(self, value); common_hal_digitalio_digitalinout_set_value(self, value);
} }
@ -148,7 +154,9 @@ void common_hal_digitalio_digitalinout_set_pull(
default: default:
break; break;
} }
// Set pull first to avoid glitches.
gpio_set_pin_pull_mode(self->pin->pin, asf_pull); gpio_set_pin_pull_mode(self->pin->pin, asf_pull);
gpio_set_pin_direction(self->pin->pin, GPIO_DIRECTION_IN);
} }
digitalio_pull_t common_hal_digitalio_digitalinout_get_pull( digitalio_pull_t common_hal_digitalio_digitalinout_get_pull(
@ -158,9 +166,9 @@ digitalio_pull_t common_hal_digitalio_digitalinout_get_pull(
mp_raise_AttributeError("Cannot get pull while in output mode"); mp_raise_AttributeError("Cannot get pull while in output mode");
return PULL_NONE; return PULL_NONE;
} else { } else {
if (hri_port_get_PINCFG_PULLEN_bit(PORT, (enum gpio_port)GPIO_PORT(pin), GPIO_PIN(pin)) == 0) { if (hri_port_get_PINCFG_PULLEN_bit(PORT, GPIO_PORT(pin), GPIO_PIN(pin)) == 0) {
return PULL_NONE; return PULL_NONE;
} if (hri_port_get_OUT_reg(PORT, (enum gpio_port)GPIO_PORT(pin), 1U << GPIO_PIN(pin)) > 0) { } if (hri_port_get_OUT_reg(PORT, GPIO_PORT(pin), 1U << GPIO_PIN(pin)) > 0) {
return PULL_UP; return PULL_UP;
} else { } else {
return PULL_DOWN; return PULL_DOWN;

View File

@ -1,3 +1,29 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Scott Shawcroft
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h> #include <string.h>
#include "lib/mp-readline/readline.h" #include "lib/mp-readline/readline.h"
@ -14,6 +40,7 @@
#include "hal/include/hal_delay.h" #include "hal/include/hal_delay.h"
#include "hal/include/hal_gpio.h" #include "hal/include/hal_gpio.h"
#include "hal/include/hal_sleep.h" #include "hal/include/hal_sleep.h"
#include "sam.h"
#include "mpconfigboard.h" #include "mpconfigboard.h"
#include "mphalport.h" #include "mphalport.h"
@ -22,6 +49,7 @@
#include "usb.h" #include "usb.h"
extern struct usart_module usart_instance; extern struct usart_module usart_instance;
extern uint32_t common_hal_mcu_processor_get_frequency(void);
int mp_hal_stdin_rx_chr(void) { int mp_hal_stdin_rx_chr(void) {
for (;;) { for (;;) {
@ -71,8 +99,23 @@ void mp_hal_delay_ms(mp_uint_t delay) {
} }
} }
// Use mp_hal_delay_us() for timing of less than 1ms.
// Do a simple timing loop to wait for a certain number of microseconds.
// Can be used when interrupts are disabled, which makes tick_delay() unreliable.
//
// Testing done at 48 MHz on SAMD21 and 120 MHz on SAMD51, multiplication and division cancel out.
// But get the frequency just in case.
#ifdef SAMD21
#define DELAY_LOOP_ITERATIONS_PER_US ( (10U*48000000U) / common_hal_mcu_processor_get_frequency())
#endif
#ifdef SAMD51
#define DELAY_LOOP_ITERATIONS_PER_US ( (30U*120000000U) / common_hal_mcu_processor_get_frequency())
#endif
void mp_hal_delay_us(mp_uint_t delay) { void mp_hal_delay_us(mp_uint_t delay) {
tick_delay(delay); for (uint32_t i = delay*DELAY_LOOP_ITERATIONS_PER_US; i > 0; i--) {
asm volatile("nop");
}
} }
void mp_hal_disable_all_interrupts(void) { void mp_hal_disable_all_interrupts(void) {

View File

@ -48,7 +48,6 @@ int receive_usb(void);
void mp_hal_set_interrupt_char(int c); void mp_hal_set_interrupt_char(int c);
void mp_hal_disable_all_interrupts(void); void mp_hal_disable_all_interrupts(void);
void mp_hal_enable_all_interrupts(void); void mp_hal_enable_all_interrupts(void);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_MPHALPORT_H #endif // MICROPY_INCLUDED_ATMEL_SAMD_MPHALPORT_H

View File

@ -64,8 +64,8 @@ void tick_init() {
for (uint16_t i = 0; i < PERIPH_COUNT_IRQn; i++) { for (uint16_t i = 0; i < PERIPH_COUNT_IRQn; i++) {
NVIC_SetPriority(i, (1UL << __NVIC_PRIO_BITS) - 1UL); NVIC_SetPriority(i, (1UL << __NVIC_PRIO_BITS) - 1UL);
} }
// Bump up the systick interrupt. // Bump up the systick interrupt so nothing else interferes with timekeeping.
NVIC_SetPriority(SysTick_IRQn, 1); NVIC_SetPriority(SysTick_IRQn, 0);
#ifdef SAMD21 #ifdef SAMD21
NVIC_SetPriority(USB_IRQn, 1); NVIC_SetPriority(USB_IRQn, 1);
#endif #endif

View File

@ -33,6 +33,8 @@
#include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/Processor.h"
// TODO porting common_hal_mcu // TODO porting common_hal_mcu
// This routine should work even when interrupts are disabled. Used by OneWire
// for precise timing.
void common_hal_mcu_delay_us(uint32_t delay) { void common_hal_mcu_delay_us(uint32_t delay) {
// os_delay_us(delay); // os_delay_us(delay);
} }
@ -58,4 +60,3 @@ const mcu_processor_obj_t common_hal_mcu_processor_obj = {
.type = &mcu_processor_type, .type = &mcu_processor_type,
}, },
}; };

View File

@ -76,4 +76,3 @@ bool mp_hal_stdin_any(void);
#define mp_hal_ticks_cpu() (0) #define mp_hal_ticks_cpu() (0)
#endif #endif

View File

@ -49,6 +49,9 @@ void shared_module_bitbangio_onewire_deinit(bitbangio_onewire_obj_t* self) {
common_hal_digitalio_digitalinout_deinit(&self->pin); common_hal_digitalio_digitalinout_deinit(&self->pin);
} }
// We use common_hal_mcu_delay_us(). It should not be dependent on interrupts
// to do accurate timekeeping, since we disable interrupts during the delays below.
bool shared_module_bitbangio_onewire_reset(bitbangio_onewire_obj_t* self) { bool shared_module_bitbangio_onewire_reset(bitbangio_onewire_obj_t* self) {
common_hal_mcu_disable_interrupts(); common_hal_mcu_disable_interrupts();
common_hal_digitalio_digitalinout_switch_to_output(&self->pin, false, DRIVE_MODE_OPEN_DRAIN); common_hal_digitalio_digitalinout_switch_to_output(&self->pin, false, DRIVE_MODE_OPEN_DRAIN);