Skip to content
Snippets Groups Projects
Select Git revision
  • a124a36d0cf83390f6328ac1a8a46006d98bd1d6
  • master default protected
  • fix-warnings
  • tvbgone-fixes
  • genofire/ble-follow-py
  • schneider/ble-stability-new-phy-adv
  • schneider/ble-stability
  • msgctl/gfx_rle
  • schneider/ble-stability-new-phy
  • add_menu_vibration
  • plaetzchen/ios-workaround
  • blinkisync-as-preload
  • schneider/max30001-pycardium
  • schneider/max30001-epicaridum
  • schneider/max30001
  • schneider/stream-locks
  • schneider/fundamental-test
  • schneider/ble-buffers
  • schneider/maxim-sdk-update
  • ch3/splashscreen
  • koalo/bhi160-works-but-dirty
  • v1.11
  • v1.10
  • v1.9
  • v1.8
  • v1.7
  • v1.6
  • v1.5
  • v1.4
  • v1.3
  • v1.2
  • v1.1
  • v1.0
  • release-1
  • bootloader-v1
  • v0.0
36 results

ble.c

Blame
  • Forked from card10 / firmware
    Source project has a limited visibility.
    mphalport.c 8.95 KiB
    #include "epicardium.h"
    #include "api/common.h"
    
    #include "max32665.h"
    #include "mxc_delay.h"
    #include "tmr.h"
    
    /* stdarg.h must be included before mpprint.h */
    #include <stdarg.h>
    
    #include "py/lexer.h"
    #include "py/mpconfig.h"
    #include "py/mperrno.h"
    #include "py/mpstate.h"
    #include "py/obj.h"
    #include "py/runtime.h"
    #include "py/mpprint.h"
    
    #include <stdint.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <string.h>
    
    /* Smallest integer us interval which can be reached exactly due
     * to the 32768 Hz systick frequency */
    #define SYSTICK_INTERVAL_US_MIN 15625
    
    /* Target systick interval is 1 second */
    #define SYSTICK_INTERVAL_US                                                    \
    	(SYSTICK_INTERVAL_US_MIN * (1000000 / SYSTICK_INTERVAL_US_MIN))
    
    /*
     * Copied from core_cm4.h and modified to select the
     * 32768 Hz RTC crystal as the clock source */
    static uint32_t systick_config(uint32_t ticks)
    {
    	if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)
    		return (1); /* Reload value impossible */
    
    	SysTick->LOAD = ticks - 1; /* set reload register */
    	NVIC_SetPriority(
    		SysTick_IRQn,
    		(1 << __NVIC_PRIO_BITS) -
    			1); /* set Priority for Systick Interrupt */
    	SysTick->VAL = 0;   /* Load the SysTick Counter Value */
    	SysTick->CTRL =
    		SysTick_CTRL_TICKINT_Msk |
    		SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
    	return (0);                      /* Function successful */
    }
    
    /* Initialize everything for MicroPython */
    void pycardium_hal_init(void)
    {
    	/* TMR5 is used for interrupts from Epicardium */
    	NVIC_EnableIRQ(TMR5_IRQn);
    
    	/*
    	 * Enable UART RX Interrupt so Pycardium can sleep until
    	 * a character becomes available.
    	 */
    	epic_interrupt_enable(EPIC_INT_UART_RX);
    
    	/*
    	 * Configure SysTick timer for SYSTICK_INTERVAL_US period.
    	 */
    	systick_config(SYSTICK_INTERVAL_US * 32768LL / 1000000);
    }
    
    /******************************************************************************
     * Serial Communication
     */
    
    /* Receive single character */
    int mp_hal_stdin_rx_chr(void)
    {
    	int chr;
    	while ((chr = epic_uart_read_char()) < 0) {
    		__WFI();
    	}
    	return chr;
    }
    
    /* Send a string */
    void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len)
    {
    	epic_uart_write_str(str, len);
    }
    
    static char exception_lines[2][80];
    static bool exception;
    static size_t exception_line_index;
    
    static void exception_feed(char c)
    {
    	if (c == '\n') {
    		exception_lines[1][exception_line_index] = 0;
    		if (exception_lines[1][0] == ' ') {
    			memcpy(exception_lines[0],
    			       exception_lines[1],
    			       sizeof(exception_lines[0]));
    			exception_line_index = 0;
    		} else {
    			exception = false;
    			epic_disp_open();
    			epic_disp_clear(0);
    			epic_disp_print_adv(
    				1, 0, 0, "Exception:", 0xF800, 0x0000
    			);
    			char buf               = exception_lines[0][26];
    			exception_lines[0][26] = 0;
    			epic_disp_print_adv(
    				1, 0, 12, exception_lines[0], 0xFFFF, 0x0000
    			);
    			exception_lines[0][26] = buf;
    			if (strlen(exception_lines[0]) > 26) {
    				epic_disp_print_adv(
    					1,
    					0,
    					24,
    					exception_lines[0] + 26,
    					0xFFFF,
    					0x0000
    				);
    			}
    
    			buf                    = exception_lines[1][26];
    			exception_lines[1][26] = 0;
    			epic_disp_print_adv(
    				1, 0, 40, exception_lines[1], 0xF800, 0x0000
    			);
    			exception_lines[1][26] = buf;
    			if (strlen(exception_lines[1]) > 26) {
    				epic_disp_print_adv(
    					1,
    					0,
    					52,
    					exception_lines[1] + 26,
    					0xF800,
    					0x0000
    				);
    			}
    			epic_disp_update();
    		}
    	} else {
    		if (exception_line_index < sizeof(exception_lines[0]) - 1) {
    			exception_lines[1][exception_line_index++] = c;
    		}
    	}
    }
    
    /* Send a string, but replace \n with \n\r */
    void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len)
    {
    	/*
    	 * Only print one line at a time.  Insert `\r` between lines so
    	 * they are properly displayed on the serial console.
    	 */
    
    	if (strncmp(str, "Traceback (most recent call last):\n", len) == 0) {
    		exception            = true;
    		exception_line_index = 0;
    	} else if (exception) {
    		for (size_t i = 0; i < len; i++) {
    			exception_feed(str[i]);
    		}
    	}
    
    	size_t i, last = 0;
    	for (i = 0; i < len; i++) {
    		if (str[i] == '\n') {
    			epic_uart_write_str(&str[last], i - last);
    			epic_uart_write_str("\r", 1);
    			last = i;
    		}
    	}
    	epic_uart_write_str(&str[last], len - last);
    }
    
    /* Send a zero-terminated string */
    void mp_hal_stdout_tx_str(const char *str)
    {
    	mp_hal_stdout_tx_strn(str, strlen(str));
    }
    
    /* Used by MicroPython for debug output */
    int DEBUG_printf(const char *fmt, ...)
    {
    	va_list args;
    	va_start(args, fmt);
    	int ret = mp_vprintf(MP_PYTHON_PRINTER, fmt, args);
    	va_end(args);
    	return ret;
    }
    
    void __attribute__((noreturn)) sbrk_is_not_implemented___see_issue_44(void);
    intptr_t _sbrk(int incr)
    {
    	sbrk_is_not_implemented___see_issue_44();
    }
    
    void epic_isr_ctrl_c(void)
    {
    	mp_sched_keyboard_interrupt();
    }
    
    void mp_hal_set_interrupt_char(char c)
    {
    	if (c != '\xFF') {
    		mp_obj_exception_clear_traceback(
    			MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))
    		);
    	}
    
    	if (c == 0x03) {
    		epic_interrupt_enable(EPIC_INT_CTRL_C);
    	} else {
    		epic_interrupt_disable(EPIC_INT_CTRL_C);
    	}
    }
    
    /******************************************************************************
     * SysTick timer at 1000 Hz
     */
    
    static volatile int64_t systick_count = 0;
    
    void SysTick_Handler(void)
    {
    	systick_count += 1;
    }
    
    /*
     * Get an absolute "timestamp" in microseconds.
     */
    static int64_t systick_get_us()
    {
    	uint32_t val, count;
    	uint32_t irqsaved = __get_PRIMASK();
    
    	/* The asynchronous/slow clocking of the systick means that
    	 * its value can jump to 0 before the interrupt is triggered.
    	 * Simply wait until it is not 0 and then read the count. */
    	do {
    		__set_PRIMASK(0);
    		val   = SysTick->VAL;
    		count = systick_count;
    		__set_PRIMASK(irqsaved);
    	} while (val == 0);
    
    	int64_t us = count * SYSTICK_INTERVAL_US +
    		     (SysTick->LOAD - val) * 1000000LL / 32768;
    
    	return us;
    }
    
    static void systick_delay_precise(uint32_t us)
    {
    	/*
    	 * Calculate how long the busy-spin needs to be.  As the very first
    	 * instruction, read the current timer value to ensure as little skew as
    	 * possible.
    	 *
    	 * Accuracy is about 30 us (due to the 32 kHz systick)
    	 */
    	uint32_t count_to_overflow = SysTick->VAL;
    	uint32_t count_reload      = SysTick->LOAD;
    
    	uint32_t delay_count = us * 32768 / 1000000;
    
    	/*
    	 * Calculate the final count for both paths.  Marked as volatile so the
    	 * compiler can't move this into the branches and screw up the timing.
    	 */
    	volatile uint32_t count_final_direct = count_to_overflow - delay_count;
    	volatile uint32_t count_final_underflow =
    		count_reload - (delay_count - count_to_overflow);
    
    	if (delay_count > count_to_overflow) {
    		/*
    		 * Wait for the SysTick to underflow and then count down
    		 * to the final value.
    		 */
    		while (SysTick->VAL <= count_to_overflow ||
    		       SysTick->VAL > count_final_underflow) {
    			__NOP();
    		}
    	} else {
    		/*
    		 * Wait for the SysTick to count down to the final value.
    		 */
    		while (SysTick->VAL > count_final_direct) {
    			__NOP();
    		}
    	}
    }
    
    static void systick_delay(uint32_t us)
    {
    	if (us == 0)
    		return;
    
    	/*
    	 * For very short delays, use the systick_delay_precise() function which
    	 * delays with a microsecond accuracy. For anything > 10 ms, use
    	 * systick_delay_sleep() which puts the CPU to sleep when nothing is
    	 * happening and also checks for MicroPython interrupts every now and
    	 * then.
    	 */
    	if (us < 10000) {
    		systick_delay_precise(us);
    	} else {
    		int64_t now        = systick_get_us();
    		int64_t final_time = now + us;
    
    		while (final_time - systick_get_us() > 10000) {
    			uint32_t sleep_time =
    				(final_time - systick_get_us()) / 1000;
    
    			/* We need to wake up at least in SYSTICK_INTERVAL_US to make
    			 * sure we serve the systick interrupt. */
    			if (sleep_time > SYSTICK_INTERVAL_US / 1000) {
    				sleep_time = SYSTICK_INTERVAL_US / 1000;
    			}
    
    			/* Add some error margin to avoid issues with the clock accuracy
    			 * of epicardium. We will account for the actual time via our
    			 * (accurate) systick */
    			epic_sleep(sleep_time * 8 / 10);
    
    			/* epic_sleep() can return early if there was an interrupt
    			 * coming from epicardium side.
    			 * Give MP a chance to handle them. */
    			mp_handle_pending(true);
    		}
    
    		now = systick_get_us();
    		if (final_time > now) {
    			systick_delay_precise(final_time - now);
    		}
    	}
    }
    
    /******************************************************************************
     * Time & Delay
     */
    
    void mp_hal_delay_ms(mp_uint_t ms)
    {
    	systick_delay(ms * 1000);
    }
    
    void mp_hal_delay_us(mp_uint_t us)
    {
    	systick_delay(us);
    }
    
    mp_uint_t mp_hal_ticks_ms(void)
    {
    	return (mp_uint_t)(systick_get_us() / 1000);
    }
    
    mp_uint_t mp_hal_ticks_us(void)
    {
    	return (mp_uint_t)systick_get_us();
    }
    
    /******************************************************************************
     * Fatal Errors
     */
    
    void NORETURN nlr_jump_fail(void *val)
    {
    	char msg[] = " >>> nlr_jump_fail <<<\r\n";
    	epic_uart_write_str(msg, sizeof(msg));
    
    	epic_exit(253);
    }
    
    /******************************************************************************
     * CSPRNG
     */
    
    int mp_hal_csprng_read_int(void)
    {
    	int result;
    	epic_csprng_read((uint8_t *)&result, sizeof(result));
    	return result;
    }