Merge pull request #393 from tannewt/m4_neopixel

Now with more blinky!!!
This commit is contained in:
Dan Halbert 2017-11-01 08:17:41 -04:00 committed by GitHub
commit 0076a3c868
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 132 additions and 33 deletions

2
main.c
View File

@ -146,7 +146,7 @@ bool start_mp(safe_mode_t safe_mode) {
// Wait for connection or character. // Wait for connection or character.
bool serial_connected_before_animation = false; bool serial_connected_before_animation = false;
rgb_status_animation_t animation; rgb_status_animation_t animation;
prep_rgb_status_animation(&result, found_main, &animation); prep_rgb_status_animation(&result, found_main, safe_mode, &animation);
while (true) { while (true) {
#ifdef MICROPY_VM_HOOK_LOOP #ifdef MICROPY_VM_HOOK_LOOP
MICROPY_VM_HOOK_LOOP MICROPY_VM_HOOK_LOOP

View File

@ -251,6 +251,7 @@ SRC_COMMON_HAL = \
microcontroller/__init__.c \ microcontroller/__init__.c \
microcontroller/Pin.c \ microcontroller/Pin.c \
microcontroller/Processor.c \ microcontroller/Processor.c \
neopixel_write/__init__.c \
os/__init__.c \ os/__init__.c \
time/__init__.c time/__init__.c
# analogio/__init__.c \ # analogio/__init__.c \

View File

@ -61,6 +61,6 @@ void reset_board(void) {
common_hal_digitalio_digitalinout_construct(&neopixel_pin, &pin_PB23); common_hal_digitalio_digitalinout_construct(&neopixel_pin, &pin_PB23);
common_hal_digitalio_digitalinout_switch_to_output(&neopixel_pin, false, common_hal_digitalio_digitalinout_switch_to_output(&neopixel_pin, false,
DRIVE_MODE_PUSH_PULL); DRIVE_MODE_PUSH_PULL);
// common_hal_neopixel_write(&neopixel_pin, empty, 30); common_hal_neopixel_write(&neopixel_pin, empty, 30);
common_hal_digitalio_digitalinout_deinit(&neopixel_pin); common_hal_digitalio_digitalinout_deinit(&neopixel_pin);
} }

View File

@ -26,8 +26,16 @@
#include "boards/board.h" #include "boards/board.h"
#include "mpconfigboard.h" #include "mpconfigboard.h"
#include "hal/include/hal_gpio.h"
void board_init(void) { void board_init(void) {
gpio_set_pin_function(MICROPY_HW_LED_TX, GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(MICROPY_HW_LED_TX, GPIO_DIRECTION_OUT);
gpio_set_pin_level(MICROPY_HW_LED_TX, true);
gpio_set_pin_function(MICROPY_HW_LED_RX, GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(MICROPY_HW_LED_RX, GPIO_DIRECTION_OUT);
gpio_set_pin_level(MICROPY_HW_LED_RX, true);
} }
bool board_requests_safe_mode(void) { bool board_requests_safe_mode(void) {

View File

@ -6,7 +6,7 @@
#define MICROPY_HW_LED_TX PIN_PA27 #define MICROPY_HW_LED_TX PIN_PA27
#define MICROPY_HW_LED_RX PIN_PB06 #define MICROPY_HW_LED_RX PIN_PB06
// #define MICROPY_HW_NEOPIXEL (&pin_PB17) #define MICROPY_HW_NEOPIXEL (&pin_PB17)
#define SPI_FLASH_BAUDRATE (1000000) #define SPI_FLASH_BAUDRATE (1000000)

View File

@ -23,13 +23,27 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "hpl_gpio.h"
#include "mphalport.h" #include "py/mphal.h"
#include "shared-bindings/neopixel_write/__init__.h" #include "shared-bindings/neopixel_write/__init__.h"
#include "asf/common2/services/delay/delay.h" #include "tick.h"
#include "asf/sam0/drivers/port/port.h"
#ifdef SAMD51
static inline void delay_cycles(uint8_t cycles) {
uint32_t start = SysTick->VAL;
uint32_t stop = start - cycles;
if (start < cycles) {
stop = 0xffffff + start - cycles;
}
while (SysTick->VAL > stop) {}
}
#endif
uint64_t next_start_tick_ms = 0;
uint32_t next_start_tick_us = 1000;
void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, uint32_t numBytes) { void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, uint32_t numBytes) {
// This is adapted directly from the Adafruit NeoPixel library SAMD21G18A code: // This is adapted directly from the Adafruit NeoPixel library SAMD21G18A code:
@ -38,14 +52,21 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout,
uint32_t pinMask; uint32_t pinMask;
PortGroup* port; PortGroup* port;
// This must be called while interrupts are on in case we're waiting for a
// future ms tick.
wait_until(next_start_tick_ms, next_start_tick_us);
// Turn off interrupts of any kind during timing-sensitive code. // Turn off interrupts of any kind during timing-sensitive code.
mp_hal_disable_all_interrupts(); mp_hal_disable_all_interrupts();
#ifdef SAMD21
// Make sure the NVM cache is consistently timed. // Make sure the NVM cache is consistently timed.
NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_DETERMINISTIC_Val; NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_DETERMINISTIC_Val;
#endif
uint32_t pin = digitalinout->pin->pin; uint32_t pin = digitalinout->pin->pin;
port = port_get_group_from_gpio_pin(pin); port = &PORT->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register
pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code. pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code.
ptr = pixels; ptr = pixels;
end = ptr + numBytes; end = ptr + numBytes;
@ -57,31 +78,75 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout,
for(;;) { for(;;) {
*set = pinMask; *set = pinMask;
// This is the time where the line is always high regardless of the bit.
// For the SK6812 its 0.3us +- 0.15us
#ifdef SAMD21
asm("nop; nop;"); asm("nop; nop;");
#endif
#ifdef SAMD51
delay_cycles(18);
#endif
if(p & bitMask) { if(p & bitMask) {
// This is the high delay unique to a one bit.
// For the SK6812 its 0.3us
#ifdef SAMD21
asm("nop; nop; nop; nop; nop; nop; nop;"); asm("nop; nop; nop; nop; nop; nop; nop;");
#endif
#ifdef SAMD51
delay_cycles(25);
#endif
*clr = pinMask; *clr = pinMask;
} else { } else {
*clr = pinMask; *clr = pinMask;
// This is the low delay unique to a zero bit.
// For the SK6812 its 0.3us
#ifdef SAMD21
asm("nop; nop;"); asm("nop; nop;");
#endif
#ifdef SAMD51
delay_cycles(25);
#endif
} }
if((bitMask >>= 1) != 0) { if((bitMask >>= 1) != 0) {
// This is the delay between bits in a byte and is the 1 code low
// level time from the datasheet.
// For the SK6812 its 0.6us +- 0.15us
#ifdef SAMD21
asm("nop; nop; nop; nop; nop;"); asm("nop; nop; nop; nop; nop;");
#endif
#ifdef SAMD51
delay_cycles(44);
#endif
} else { } else {
if(ptr >= end) break; if(ptr >= end) break;
p = *ptr++; p = *ptr++;
bitMask = 0x80; bitMask = 0x80;
// This is the delay between bytes. Its similar to the other branch
// in the if statement except its tuned to account for the time the
// above operations take.
// For the SK6812 its 0.6us +- 0.15us
#ifdef SAMD51
delay_cycles(50);
#endif
} }
} }
#ifdef SAMD21
// Speed up! (But inconsistent timing.) // Speed up! (But inconsistent timing.)
NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_NO_MISS_PENALTY_Val; NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_NO_MISS_PENALTY_Val;
#endif
// ticks_ms may be out of date at this point because we stopped the
// interrupt. We'll risk it anyway.
current_tick(&next_start_tick_ms, &next_start_tick_us);
if (next_start_tick_us < 100) {
next_start_tick_ms += 1;
next_start_tick_us = 100 - next_start_tick_us;
} else {
next_start_tick_us -= 100;
}
// Turn on interrupts after timing-sensitive code. // Turn on interrupts after timing-sensitive code.
mp_hal_enable_all_interrupts(); mp_hal_enable_all_interrupts();
// 50us delay to let pixels latch to the data that was just sent.
// This could be optimized to only occur before pixel writes when necessary,
// like in the Arduino library.
delay_us(50);
} }

View File

@ -215,7 +215,6 @@ extern const struct _mp_obj_module_t usb_hid_module;
// { MP_OBJ_NEW_QSTR(MP_QSTR_analogio), (mp_obj_t)&analogio_module }, // { MP_OBJ_NEW_QSTR(MP_QSTR_analogio), (mp_obj_t)&analogio_module },
// { MP_OBJ_NEW_QSTR(MP_QSTR_busio), (mp_obj_t)&busio_module }, // { MP_OBJ_NEW_QSTR(MP_QSTR_busio), (mp_obj_t)&busio_module },
// { MP_OBJ_NEW_QSTR(MP_QSTR_gamepad),(mp_obj_t)&gamepad_module }, // { MP_OBJ_NEW_QSTR(MP_QSTR_gamepad),(mp_obj_t)&gamepad_module },
// { MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write),(mp_obj_t)&neopixel_write_module },
// { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module }, // { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module },
// { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, // { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module },
// { MP_OBJ_NEW_QSTR(MP_QSTR_samd),(mp_obj_t)&samd_module }, // { MP_OBJ_NEW_QSTR(MP_QSTR_samd),(mp_obj_t)&samd_module },
@ -225,6 +224,7 @@ extern const struct _mp_obj_module_t usb_hid_module;
{ MP_OBJ_NEW_QSTR(MP_QSTR_board), (mp_obj_t)&board_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_board), (mp_obj_t)&board_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_digitalio), (mp_obj_t)&digitalio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_digitalio), (mp_obj_t)&digitalio_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_microcontroller), (mp_obj_t)&microcontroller_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_microcontroller), (mp_obj_t)&microcontroller_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write),(mp_obj_t)&neopixel_write_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&os_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&os_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&struct_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&struct_module }, \

View File

@ -66,5 +66,17 @@ void tick_delay(uint32_t us) {
start_ms = ticks_ms; start_ms = ticks_ms;
us_between_ticks = 1000; us_between_ticks = 1000;
} }
while (SysTick->VAL > ((1000 - us) * ticks_per_us)) {} while (SysTick->VAL > ((us_between_ticks - us) * ticks_per_us)) {}
}
// us counts down!
void current_tick(uint64_t* ms, uint32_t* us_until_ms) {
uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000;
*ms = ticks_ms;
*us_until_ms = SysTick->VAL / ticks_per_us;
}
void wait_until(uint64_t ms, uint32_t us_until_ms) {
uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000;
while(ticks_ms <= ms && SysTick->VAL / ticks_per_us >= us_until_ms) {}
} }

View File

@ -36,4 +36,9 @@ void tick_init(void);
void tick_delay(uint32_t us); void tick_delay(uint32_t us);
void current_tick(uint64_t* ms, uint32_t* us_until_ms);
// Do not call this with interrupts disabled because it may be waiting for
// ticks_ms to increment.
void wait_until(uint64_t ms, uint32_t us_until_ms);
#endif // MICROPY_INCLUDED_ATMEL_SAMD_TICK_H #endif // MICROPY_INCLUDED_ATMEL_SAMD_TICK_H

View File

@ -309,8 +309,7 @@ void usb_msc_background(void) {
// Load more blocks from USB if they are needed. // Load more blocks from USB if they are needed.
if (active_nblocks > 0) { if (active_nblocks > 0) {
int32_t result = mscdf_xfer_blocks(false, sector_buffer, 1); int32_t result = mscdf_xfer_blocks(false, sector_buffer, 1);
while (result != ERR_NONE) {} usb_busy = result != ERR_NONE;
usb_busy = true;
} else { } else {
mscdf_xfer_blocks(false, NULL, 0); mscdf_xfer_blocks(false, NULL, 0);
active_write = false; active_write = false;

View File

@ -199,12 +199,15 @@ void set_rgb_status_brightness(uint8_t level){
rgb_status_brightness = level; rgb_status_brightness = level;
} }
void prep_rgb_status_animation(const pyexec_result_t* result, bool found_main, rgb_status_animation_t* status) { void prep_rgb_status_animation(const pyexec_result_t* result,
bool found_main,
safe_mode_t safe_mode,
rgb_status_animation_t* status) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
new_status_color(ALL_DONE); new_status_color(ALL_DONE);
status->pattern_start = ticks_ms; status->pattern_start = ticks_ms;
status->safe_mode = safe_mode;
uint32_t total_exception_cycle = 0; status->total_exception_cycle = 0;
status->ones = result->exception_line % 10; status->ones = result->exception_line % 10;
status->ones += status->ones > 0 ? 1 : 0; status->ones += status->ones > 0 ? 1 : 0;
status->tens = (result->exception_line / 10) % 10; status->tens = (result->exception_line / 10) % 10;
@ -224,7 +227,7 @@ void prep_rgb_status_animation(const pyexec_result_t* result, bool found_main, r
} }
status->ok = result->return_code != PYEXEC_EXCEPTION; status->ok = result->return_code != PYEXEC_EXCEPTION;
if (!status->ok) { if (!status->ok) {
status->total_exception_cycle = EXCEPTION_TYPE_LENGTH_MS * 3 + LINE_NUMBER_TOGGLE_LENGTH * digit_sum + LINE_NUMBER_TOGGLE_LENGTH * num_places; status->total_exception_cycle = EXCEPTION_TYPE_LENGTH_MS * 3 + LINE_NUMBER_TOGGLE_LENGTH * status->digit_sum + LINE_NUMBER_TOGGLE_LENGTH * num_places;
} }
if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_IndentationError)) { if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_IndentationError)) {
status->exception_color = INDENTATION_ERROR; status->exception_color = INDENTATION_ERROR;
@ -244,11 +247,11 @@ void prep_rgb_status_animation(const pyexec_result_t* result, bool found_main, r
void tick_rgb_status_animation(rgb_status_animation_t* status) { void tick_rgb_status_animation(rgb_status_animation_t* status) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
uint32_t tick_diff = ticks_ms - pattern_start; uint32_t tick_diff = ticks_ms - status->pattern_start;
if (status->ok) { if (status->ok) {
// All is good. Ramp ALL_DONE up and down. // All is good. Ramp ALL_DONE up and down.
if (tick_diff > ALL_GOOD_CYCLE_MS) { if (tick_diff > ALL_GOOD_CYCLE_MS) {
pattern_start = ticks_ms; status->pattern_start = ticks_ms;
tick_diff = 0; tick_diff = 0;
} }
@ -256,14 +259,14 @@ void tick_rgb_status_animation(rgb_status_animation_t* status) {
if (brightness > 255) { if (brightness > 255) {
brightness = 511 - brightness; brightness = 511 - brightness;
} }
if (safe_mode == NO_SAFE_MODE) { if (status->safe_mode == NO_SAFE_MODE) {
new_status_color(color_brightness(ALL_DONE, brightness)); new_status_color(color_brightness(ALL_DONE, brightness));
} else { } else {
new_status_color(color_brightness(SAFE_MODE, brightness)); new_status_color(color_brightness(SAFE_MODE, brightness));
} }
} else { } else {
if (tick_diff > total_exception_cycle) { if (tick_diff > status->total_exception_cycle) {
pattern_start = ticks_ms; status->pattern_start = ticks_ms;
tick_diff = 0; tick_diff = 0;
} }
// First flash the file color. // First flash the file color.
@ -275,34 +278,34 @@ void tick_rgb_status_animation(rgb_status_animation_t* status) {
} }
// Next flash the exception color. // Next flash the exception color.
} else if (tick_diff < EXCEPTION_TYPE_LENGTH_MS * 2) { } else if (tick_diff < EXCEPTION_TYPE_LENGTH_MS * 2) {
new_status_color(exception_color); new_status_color(status->exception_color);
// Finally flash the line number digits from highest to lowest. // Finally flash the line number digits from highest to lowest.
// Zeroes will not produce a flash but can be read by the absence of // Zeroes will not produce a flash but can be read by the absence of
// a color from the sequence. // a color from the sequence.
} else if (tick_diff < (EXCEPTION_TYPE_LENGTH_MS * 2 + LINE_NUMBER_TOGGLE_LENGTH * digit_sum)) { } else if (tick_diff < (EXCEPTION_TYPE_LENGTH_MS * 2 + LINE_NUMBER_TOGGLE_LENGTH * status->digit_sum)) {
uint32_t digit_diff = tick_diff - EXCEPTION_TYPE_LENGTH_MS * 2; uint32_t digit_diff = tick_diff - EXCEPTION_TYPE_LENGTH_MS * 2;
if ((digit_diff % LINE_NUMBER_TOGGLE_LENGTH) < (LINE_NUMBER_TOGGLE_LENGTH / 2)) { if ((digit_diff % LINE_NUMBER_TOGGLE_LENGTH) < (LINE_NUMBER_TOGGLE_LENGTH / 2)) {
new_status_color(BLACK); new_status_color(BLACK);
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * thousands) { } else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * status->thousands) {
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH) { if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH) {
new_status_color(BLACK); new_status_color(BLACK);
} else { } else {
new_status_color(THOUSANDS); new_status_color(THOUSANDS);
} }
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds)) { } else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + status->hundreds)) {
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + 1)) { if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + 1)) {
new_status_color(BLACK); new_status_color(BLACK);
} else { } else {
new_status_color(HUNDREDS); new_status_color(HUNDREDS);
} }
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + tens)) { } else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + status->hundreds + status->tens)) {
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + 1)) { if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + status->hundreds + 1)) {
new_status_color(BLACK); new_status_color(BLACK);
} else { } else {
new_status_color(TENS); new_status_color(TENS);
} }
} else { } else {
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + tens + 1)) { if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + status->hundreds + status->tens + 1)) {
new_status_color(BLACK); new_status_color(BLACK);
} else { } else {
new_status_color(ONES); new_status_color(ONES);

View File

@ -31,6 +31,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "lib/utils/pyexec.h" #include "lib/utils/pyexec.h"
#include "supervisor/port.h"
#include "mpconfigport.h" #include "mpconfigport.h"
#include "rgb_led_colors.h" #include "rgb_led_colors.h"
@ -59,6 +60,8 @@ typedef struct {
bool ok; bool ok;
uint32_t pattern_start; uint32_t pattern_start;
uint32_t total_exception_cycle; uint32_t total_exception_cycle;
safe_mode_t safe_mode;
uint8_t digit_sum;
uint8_t ones; uint8_t ones;
uint8_t tens; uint8_t tens;
uint8_t hundreds; uint8_t hundreds;
@ -67,7 +70,10 @@ typedef struct {
bool found_main; bool found_main;
} rgb_status_animation_t; } rgb_status_animation_t;
void prep_rgb_status_animation(const pyexec_result_t* result, bool found_main, rgb_status_animation_t* status); void prep_rgb_status_animation(const pyexec_result_t* result,
bool found_main,
safe_mode_t safe_mode,
rgb_status_animation_t* status);
void tick_rgb_status_animation(rgb_status_animation_t* status); void tick_rgb_status_animation(rgb_status_animation_t* status);
#endif // MICROPY_INCLUDED_SUPERVISOR_RGB_LED_STATUS_H #endif // MICROPY_INCLUDED_SUPERVISOR_RGB_LED_STATUS_H