From: Martin Mares Date: Thu, 18 Jul 2019 09:45:45 +0000 (+0200) Subject: ds-test -> test-ds X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=c3d236dbb636fd728bb09f9e09644f09ddec8e61;p=home-hw.git ds-test -> test-ds --- diff --git a/ds-test/Makefile b/ds-test/Makefile deleted file mode 100644 index 3e3438f..0000000 --- a/ds-test/Makefile +++ /dev/null @@ -1,81 +0,0 @@ -BINARY=test -OBJS=test.o util-debug.o ds18b20.o - -OPENCM3_DIR=/home/mj/stm/libopencm3 -DEVICE=stm32f103x8 - -all: $(BINARY).elf $(BINARY).bin - -flash: all - ../bin/st-flash write $(BINARY).bin 0x8000000 - -reset: all - ../bin/st-flash reset - -ifneq ($(V),1) -Q := @ -NULL := 2>/dev/null -endif - -include $(OPENCM3_DIR)/mk/genlink-config.mk - -PREFIX ?= arm-none-eabi - -CC := $(PREFIX)-gcc -CXX := $(PREFIX)-g++ -LD := $(PREFIX)-gcc -AR := $(PREFIX)-ar -AS := $(PREFIX)-as -OBJCOPY := $(PREFIX)-objcopy -OBJDUMP := $(PREFIX)-objdump -GDB := $(PREFIX)-gdb -OPT := -Os -DEBUG := -ggdb3 -CSTD ?= -std=c99 - -TGT_CFLAGS += $(OPT) $(CSTD) $(DEBUG) -TGT_CFLAGS += $(ARCH_FLAGS) -TGT_CFLAGS += -Wall -Wextra -Wshadow -Wimplicit-function-declaration -TGT_CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes -TGT_CFLAGS += -fno-common -ffunction-sections -fdata-sections - -TGT_CPPFLAGS += -MD - -TGT_LDFLAGS += --static -nostartfiles -TGT_LDFLAGS += -T$(LDSCRIPT) -TGT_LDFLAGS += $(ARCH_FLAGS) $(DEBUG) -TGT_LDFLAGS += -Wl,-Map=$(*).map -Wl,--cref -TGT_LDFLAGS += -Wl,--gc-sections -ifeq ($(V),99) -TGT_LDFLAGS += -Wl,--print-gc-sections -endif - -LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group - -include $(OPENCM3_DIR)/mk/genlink-rules.mk - -%.bin: %.elf - @printf " OBJCOPY $(*).bin\n" - $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin - -%.elf: $(OBJS) $(LDSCRIPT) - @printf " LD $(*).elf\n" - $(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $*.elf - -%.o: %.c - @printf " CC $(*).c\n" - $(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $*.o -c $*.c - -.PHONY: clean -clean: - @printf " CLEAN\n" - $(Q)rm -f *.elf *.bin *.o *.d *.map $(LDSCRIPT) - -%.stlink-flash: %.bin - @printf " FLASH $<\n" - $(STFLASH) write $(*).bin 0x8000000 - -.SECONDEXPANSION: -.SECONDARY: - --include $(OBJS:.o=.d) diff --git a/ds-test/ds18b20.c b/ds-test/ds18b20.c deleted file mode 100644 index 53df2a6..0000000 --- a/ds-test/ds18b20.c +++ /dev/null @@ -1,419 +0,0 @@ -// DS18B20 Temperature Sensors - -#include "util.h" -#include "ds18b20.h" - -#include -#include -#include -#include -#include -#include - -static volatile u32 ds_dma_buffer; - -#define DS_TIMER TIM3 -#define DS_GPIO GPIOA -#define DS_PIN GPIO7 -#define DS_DMA DMA1 -#define DS_DMA_CH 6 - -#define DS_DEBUG -#undef DS_DEBUG2 - -#ifdef DS_DEBUG -#define DEBUG debug_printf -#else -#define DEBUG(xxx, ...) do { } while (0) -#endif - -#ifdef DS_DEBUG2 -#define DEBUG2 debug_printf -#else -#define DEBUG2(xxx, ...) do { } while (0) -#endif - -// Current temperature -int ds_current_temp = DS_TEMP_UNKNOWN; - -// Auxiliary functions which are missing from libopencm3 -static inline bool timer_is_counter_enabled(u32 timer) -{ - return TIM_CR1(timer) & TIM_CR1_CEN; -} - -static inline void timer_enable_dma_cc1(u32 timer) -{ - TIM_DIER(timer) |= TIM_DIER_CC1DE; -} - -static inline void timer_disable_dma_cc1(u32 timer) -{ - TIM_DIER(timer) &= ~TIM_DIER_CC1DE; -} - -static bool ds_reset(void) -{ - DEBUG2("DS18B20: Reset\n"); - timer_disable_counter(DS_TIMER); - timer_one_shot_mode(DS_TIMER); - - // DMA for reading pin state - ds_dma_buffer = 0xdeadbeef; - dma_set_memory_address(DS_DMA, DS_DMA_CH, (u32) &ds_dma_buffer); - dma_set_peripheral_address(DS_DMA, DS_DMA_CH, (u32) &GPIO_IDR(DS_GPIO)); - dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1); - dma_enable_channel(DS_DMA, DS_DMA_CH); - - // CC1 is used to drive the DMA (read line state at specified time) - timer_disable_oc_output(DS_TIMER, TIM_OC1); - timer_set_oc_mode(DS_TIMER, TIM_OC1, TIM_OCM_FROZEN); - timer_set_oc_value(DS_TIMER, TIM_OC1, 560); - timer_set_dma_on_compare_event(DS_TIMER); - timer_enable_dma_cc1(DS_TIMER); - - // CC2 is used to generate pulses (return line to idle state at specified time) - timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH); - timer_enable_oc_output(DS_TIMER, TIM_OC2); - timer_set_oc_value(DS_TIMER, TIM_OC2, 480); - timer_set_oc_polarity_low(DS_TIMER, TIM_OC2); - - // Set timer period to the length of the whole transaction (1 ms) - timer_set_period(DS_TIMER, 999); - - // Pull line down and start timer - cm_disable_interrupts(); - timer_enable_counter(DS_TIMER); - timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE); - cm_enable_interrupts(); - - // Wait until the timer expires - while (timer_is_counter_enabled(DS_TIMER)) - ; - // Counter is automatically disabled at the end of cycle - - // Disable DMA - timer_disable_dma_cc1(DS_TIMER); - dma_disable_channel(DS_DMA, DS_DMA_CH); - - DEBUG2("Init DMA: %08x [%u] (%u remains)\n", - ds_dma_buffer, - !!(ds_dma_buffer & GPIO7), - dma_get_number_of_data(DS_DMA, DS_DMA_CH)); - - // Did the device respond? - if (ds_dma_buffer & GPIO7) { - DEBUG("DS18B20: Initialization failed\n"); - return 0; - } else - return 1; -} - -static void ds_send_bit(bool bit) -{ - timer_set_period(DS_TIMER, 99); // Each write slot takes 100 μs - timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH); - timer_set_oc_value(DS_TIMER, TIM_OC2, (bit ? 3 : 89)); // 1: 3μs pulse, 0: 89μs pulse - cm_disable_interrupts(); - // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter, - // otherwise OC triggers immediately. Reasons? - timer_enable_counter(DS_TIMER); - timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE); - cm_enable_interrupts(); - while (timer_is_counter_enabled(DS_TIMER)) - ; -} - -static void ds_send_byte(byte b) -{ - DEBUG2("DS write: %02x\n", b); - for (uint m = 1; m < 0x100; m <<= 1) - ds_send_bit(b & m); -} - -static bool ds_recv_bit(void) -{ - timer_set_period(DS_TIMER, 79); // Each read slot takes 80μs - timer_set_oc_value(DS_TIMER, TIM_OC2, 2); // Generate 2μs pulse to start read slot - timer_set_oc_value(DS_TIMER, TIM_OC1, 8); // Sample data 8μs after start of slot - timer_enable_dma_cc1(DS_TIMER); - - ds_dma_buffer = 0xdeadbeef; - dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1); - dma_enable_channel(DS_DMA, DS_DMA_CH); - timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH); - cm_disable_interrupts(); - timer_enable_counter(DS_TIMER); - timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE); - cm_enable_interrupts(); - while (timer_is_counter_enabled(DS_TIMER)) - ; - // DEBUG2("XXX %08x\n", ds_dma_buffer); - bool out = ds_dma_buffer & GPIO7; - dma_disable_channel(DS_DMA, DS_DMA_CH); - - timer_disable_dma_cc1(DS_TIMER); - - return out; -} - -static byte ds_recv_byte(void) -{ - uint out = 0; - for (uint m = 1; m < 0x100; m <<= 1) { - if (ds_recv_bit()) - out |= m; - } - - DEBUG2("DS read: %02x\n", out); - return out; -} - -static byte ds_buf[10]; - -static byte ds_crc_block(uint n) -{ - /// XXX: This might be worth optimizing - uint crc = 0; - - for (uint i = 0; i < n; i++) { - byte b = ds_buf[i]; - for (uint j = 0; j < 8; j++) { - uint k = (b & 1) ^ (crc >> 7); - crc = (crc << 1) & 0xff; - if (k) - crc ^= 0x31; - b >>= 1; - } - } - - return crc; -} - -static bool ds_recv_block(uint n) -{ - for (uint i = 0; i < n; i++) - ds_buf[i] = ds_recv_byte(); - - byte crc = ds_crc_block(n); - if (crc) { - DEBUG("DS18B20: Invalid CRC %02x\n", crc); - return 0; - } - return 1; -} - -struct ds_sensor ds_sensors[DS_NUM_SENSORS]; - -#if DS_NUM_SENSORS == 1 - -static void ds_enumerate(void) -{ - if (!ds_reset()) - return; - - ds_send_byte(0x33); // READ_ROM - if (!ds_recv_block(8)) - return; - - DEBUG("DS18B20: Found sensor "); - for (uint i = 0; i < 8; i++) { - DEBUG("%02x", ds_buf[i]); - ds_sensors[0].address[i] = ds_buf[i]; - } - DEBUG("\n"); -} - -#else - -static void ds_enumerate(void) -{ - /* - * The enumeration algorithm roughly follows the one described in the - * Book of iButton Standards (Maxim Integrated Application Note 937). - * - * It simulates depth-first search on the trie of all device IDs. - * In each pass, it walks the trie from the root and recognizes branching nodes. - * - * The old_choice variable remembers the deepest left branch taken in the - * previous pass, new_choice is the same for the current pass. - */ - - DEBUG("DS18B20: Enumerate\n"); - - uint num_sensors = 0; - byte *addr = ds_buf; - byte old_choice = 0; - - for (;;) { - if (!ds_reset()) { - DEBUG("DS18B20: Enumeration found no sensor\n"); - return; - } - - ds_send_byte(0xf0); // SEARCH_ROM - byte new_choice = 0; - for (byte i=0; i<64; i++) { - bool have_one = ds_recv_bit(); - bool have_zero = ds_recv_bit(); - bool old_bit = addr[i/8] & (1U << (i%8)); - bool new_bit; - switch (2*have_one + have_zero) { - case 3: - // This should not happen - DEBUG("DS18B20: Enumeration failed\n"); - return; - case 1: - // Only 0 - new_bit = 0; - break; - case 2: - // Only 1 - new_bit = 1; - break; - default: - // Both - if (i == old_choice) - new_bit = 1; - else if (i > old_choice) { - new_bit = 0; - new_choice = i; - } else { - new_bit = old_bit; - if (!new_bit) - new_choice = i; - } - } - if (new_bit) - addr[i/8] |= 1U << (i%8); - else - addr[i/8] &= ~(1U << (i%8)); - ds_send_bit(new_bit); - } - - if (num_sensors >= DS_NUM_SENSORS) { - DEBUG("DS18B20: Too many sensors\n"); - return; - } - - DEBUG("DS18B20: Found sensor #%u: ", num_sensors); - for (byte i=0; i<8; i++) - DEBUG("%02x", addr[i]); - if (ds_crc_block(8)) { - DEBUG(" - invalid CRC!\n"); - } else if (ds_buf[0] == 0x28) { - DEBUG("\n"); - memcpy(ds_sensors[num_sensors].address, ds_buf, 8); - num_sensors++; - } else { - DEBUG(" - wrong type\n"); - } - - old_choice = new_choice; - if (!old_choice) - break; - } -} - -#endif - -void ds_init(void) -{ - DEBUG("DS18B20: Init\n"); - - for (uint i = 0; i < DS_NUM_SENSORS; i++) { - memset(ds_sensors[i].address, 0, 8); - ds_sensors[i].current_temp = DS_TEMP_UNKNOWN; - } - - dma_set_read_from_peripheral(DS_DMA, DS_DMA_CH); - dma_set_priority(DS_DMA, DS_DMA_CH, DMA_CCR_PL_VERY_HIGH); - dma_disable_peripheral_increment_mode(DS_DMA, DS_DMA_CH); - dma_enable_memory_increment_mode(DS_DMA, DS_DMA_CH); - dma_set_peripheral_size(DS_DMA, DS_DMA_CH, DMA_CCR_PSIZE_16BIT); - dma_set_memory_size(DS_DMA, DS_DMA_CH, DMA_CCR_MSIZE_16BIT); - - timer_set_prescaler(DS_TIMER, 71); // 1 tick = 1 μs - timer_set_mode(DS_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); - timer_disable_preload(DS_TIMER); - - gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO7); - - ds_enumerate(); - - // FIXME: Configure precision? -} - -#if DS_NUM_SENSORS == 1 -#define ds_current_id 0 -#else - static byte ds_current_id; -#endif - -static bool ds_activate(void) -{ - if (!ds_reset()) { - DEBUG("DS18B20: Reset failed\n"); - return false; - } -#if DS_NUM_SENSORS == 1 - ds_send_byte(0xcc); // SKIP_ROM -#else - ds_send_byte(0x55); // MATCH_ROM - for (uint i = 0; i < 8; i++) - ds_send_byte(ds_sensors[ds_current_id].address[i]); -#endif - return true; -} - -void ds_step(void) -{ - static byte ds_running; - static byte ds_timeout; - - if (!ds_running) { - // Start measurement -#if DS_NUM_SENSORS != 1 - uint maxn = DS_NUM_SENSORS; - do { - if (!maxn--) - return; - ds_current_id++; - if (ds_current_id >= DS_NUM_SENSORS) - ds_current_id = 0; - } while (!ds_sensors[ds_current_id].address[0]); -#endif - if (!ds_activate()) { - ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN; - return; - } - ds_send_byte(0x44); // CONVERT_T - ds_running = 1; - ds_timeout = 255; - } else { - // Still running? - if (!ds_recv_bit()) { - if (!ds_timeout--) { - DEBUG("DS18B20 #%u: Timeout\n", ds_current_id); - ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN; - ds_running = 0; - } - return; - } - ds_running = 0; - - // Read scratch pad - if (!ds_activate()) - return; - ds_send_byte(0xbe); // READ_SCRATCHPAD - if (!ds_recv_block(9)) { - ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN; - return; - } - int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8)); - t = t * 1000 / 16; - - DEBUG("DS18B20 #%u: %d.%03d degC\n", ds_current_id, t / 1000, t % 1000); - ds_sensors[ds_current_id].current_temp = t; - } -} diff --git a/ds-test/ds18b20.h b/ds-test/ds18b20.h deleted file mode 100644 index 6ca5d75..0000000 --- a/ds-test/ds18b20.h +++ /dev/null @@ -1,22 +0,0 @@ -// DS18B20 Temperature Sensors - -#ifndef _DS18B20_H -#define _DS18B20_H - -// Maximum number of supported sensors -#define DS_NUM_SENSORS 8 - -struct ds_sensor { - byte address[8]; // All zeroes if sensor does not exist. - // Otherwise, address[0] is guaranteed to be non-zero. - int current_temp; // Temperature in m°C or DS_TEMP_UNKNOWN -}; - -extern struct ds_sensor ds_sensors[DS_NUM_SENSORS]; - -#define DS_TEMP_UNKNOWN 0x7fffffff - -void ds_init(void); -void ds_step(void); - -#endif diff --git a/ds-test/test.c b/ds-test/test.c deleted file mode 100644 index 309fc75..0000000 --- a/ds-test/test.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "util.h" -#include "ds18b20.h" - -#include -#include -#include -#include -#include - -static void clock_setup(void) -{ - rcc_clock_setup_in_hse_8mhz_out_72mhz(); - - rcc_periph_clock_enable(RCC_GPIOA); - rcc_periph_clock_enable(RCC_GPIOC); - rcc_periph_clock_enable(RCC_USART1); - rcc_periph_clock_enable(RCC_TIM3); - rcc_periph_clock_enable(RCC_DMA1); -} - -static void gpio_setup(void) -{ - gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); -} - -static volatile u32 ms_ticks; - -void sys_tick_handler(void) -{ - ms_ticks++; -} - -static void tick_setup(void) -{ - systick_set_frequency(1000, 72000000); - systick_counter_enable(); - systick_interrupt_enable(); -} - -static void delay_ms(uint ms) -{ - u32 start_ticks = ms_ticks; - while (ms_ticks - start_ticks < ms) - ; -} - -static void usart_setup(void) -{ - gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); - - usart_set_baudrate(USART1, 115200); - usart_set_databits(USART1, 8); - usart_set_stopbits(USART1, USART_STOPBITS_1); - usart_set_mode(USART1, USART_MODE_TX); - usart_set_parity(USART1, USART_PARITY_NONE); - usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); - - usart_enable(USART1); -} - -int main(void) -{ - clock_setup(); - gpio_setup(); - tick_setup(); - usart_setup(); - - ds_init(); - for (;;) { - gpio_toggle(GPIOC, GPIO13); - delay_ms(100); - ds_step(); - } - - return 0; -} diff --git a/ds-test/util-debug.c b/ds-test/util-debug.c deleted file mode 100644 index a1698cf..0000000 --- a/ds-test/util-debug.c +++ /dev/null @@ -1,166 +0,0 @@ -#include "util.h" - -#include - -#include -#include - -#ifdef DEBUG_SEMIHOSTING - -void semi_put_char(char c) -{ - // This is tricky, we need to work around GCC bugs - volatile char cc = c; - asm volatile ( - "mov r0, #0x03\n" /* SYS_WRITEC */ - "mov r1, %[msg]\n" - "bkpt #0xAB\n" - : - : [msg] "r" (&cc) - : "r0", "r1" - ); -} - -void semi_write_string(char *c) -{ - asm volatile ( - "mov r0, #0x04\n" /* SYS_WRITE0 */ - "mov r1, %[msg]\n" - "bkpt #0xAB\n" - : - : [msg] "r" (c) - : "r0", "r1" - ); -} - -#endif - -void debug_putc(int c) -{ -#ifdef DEBUG_SEMIHOSTING - static char debug_buf[128]; - static int debug_i; - debug_buf[debug_i++] = c; - if (c == '\n' || debug_i >= sizeof(debug_buf) - 1) { - debug_buf[debug_i] = 0; - semi_write_string(debug_buf); - debug_i = 0; - } -#endif -#ifdef DEBUG_USART - if (c == '\n') - usart_send_blocking(USART1, '\r'); - usart_send_blocking(USART1, c); -#endif -} - -void debug_puts(const char *s) -{ - while (*s) - debug_putc(*s++); -} - -enum printf_flags { - PF_ZERO_PAD = 1, - PF_SIGNED = 2, - PF_NEGATIVE = 4, - PF_UPPERCASE = 8, - PF_LEFT = 16, -}; - -static void printf_string(const char *s, uint width, uint flags) -{ - uint len = strlen(s); - uint pad = (len < width) ? width - len : 0; - char pad_char = (flags & PF_ZERO_PAD) ? '0' : ' '; - - if (flags & PF_LEFT) - debug_puts(s); - while (pad--) - debug_putc(pad_char); - if (!(flags & PF_LEFT)) - debug_puts(s); -} - -static void printf_number(uint i, uint width, uint flags, uint base) -{ - char buf[16]; - char *w = buf + sizeof(buf); - - if (flags & PF_SIGNED) { - if ((int) i < 0) { - i = - (int) i; - flags |= PF_NEGATIVE; - } - } - - *--w = 0; - do { - uint digit = i % base; - if (digit < 10) - *--w = '0' + digit; - else - *--w = ((flags & PF_UPPERCASE) ? 'A' : 'a') + digit - 10; - i /= base; - } - while (i); - - if (flags & PF_NEGATIVE) - *--w = '-'; - - printf_string(w, width, flags); -} - -void debug_printf(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - while (*fmt) { - int c = *fmt++; - if (c != '%') { - debug_putc(c); - continue; - } - - uint width = 0; - uint flags = 0; - - if (*fmt == '-') { - fmt++; - flags |= PF_LEFT; - } - - if (*fmt == '0') { - fmt++; - flags |= PF_ZERO_PAD; - } - - while (*fmt >= '0' && *fmt <= '9') - width = 10*width + *fmt++ - '0'; - - c = *fmt++; - switch (c) { - case 'd': - printf_number(va_arg(args, int), width, flags | PF_SIGNED, 10); - break; - case 'u': - printf_number(va_arg(args, int), width, flags, 10); - break; - case 'X': - flags |= PF_UPPERCASE; - // fall-thru - case 'x': - printf_number(va_arg(args, int), width, flags, 16); - break; - case 's': - printf_string(va_arg(args, char *), width, flags); - break; - default: - debug_putc(c); - continue; - } - } - - va_end(args); -} diff --git a/ds-test/util.h b/ds-test/util.h deleted file mode 100644 index 6d8c9cc..0000000 --- a/ds-test/util.h +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include - -typedef unsigned int uint; -typedef uint8_t byte; -typedef uint16_t u16; -typedef int16_t s16; -typedef uint32_t u32; -typedef int32_t s32; - -#define MIN(x,y) ((x) < (y) ? (x) : (y)) -#define MAX(x,y) ((x) > (y) ? (x) : (y)) - -static inline uint get_u16_le(byte *p) -{ - return (p[1] << 8) | p[0]; -} - -static inline uint get_u16_be(byte *p) -{ - return (p[0] << 8) | p[1]; -} - -static inline uint get_u32_le(byte *p) -{ - return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; -} - -static inline uint get_u32_be(byte *p) -{ - return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; -} - -static inline void put_u16_le(byte *p, u16 x) -{ - p[0] = x; - p[1] = x >> 8; -} - -static inline void put_u16_be(byte *p, u16 x) -{ - p[0] = x >> 8; - p[1] = x; -} - -static inline void put_u32_be(byte *p, u32 x) -{ - p[0] = x >> 24; - p[1] = (x >> 16) & 0xff; - p[2] = (x >> 8) & 0xff; - p[3] = x & 0xff; -} - -static inline void put_u32_le(byte *p, u32 x) -{ - p[3] = x >> 24; - p[2] = (x >> 16) & 0xff; - p[1] = (x >> 8) & 0xff; - p[0] = x & 0xff; -} - -// debug.c - -// #define DEBUG_SEMIHOSTING -#define DEBUG_USART USART1 - -void debug_printf(const char *fmt, ...); -void debug_puts(const char *s); -void debug_putc(int c); diff --git a/test-ds/Makefile b/test-ds/Makefile new file mode 100644 index 0000000..3e3438f --- /dev/null +++ b/test-ds/Makefile @@ -0,0 +1,81 @@ +BINARY=test +OBJS=test.o util-debug.o ds18b20.o + +OPENCM3_DIR=/home/mj/stm/libopencm3 +DEVICE=stm32f103x8 + +all: $(BINARY).elf $(BINARY).bin + +flash: all + ../bin/st-flash write $(BINARY).bin 0x8000000 + +reset: all + ../bin/st-flash reset + +ifneq ($(V),1) +Q := @ +NULL := 2>/dev/null +endif + +include $(OPENCM3_DIR)/mk/genlink-config.mk + +PREFIX ?= arm-none-eabi + +CC := $(PREFIX)-gcc +CXX := $(PREFIX)-g++ +LD := $(PREFIX)-gcc +AR := $(PREFIX)-ar +AS := $(PREFIX)-as +OBJCOPY := $(PREFIX)-objcopy +OBJDUMP := $(PREFIX)-objdump +GDB := $(PREFIX)-gdb +OPT := -Os +DEBUG := -ggdb3 +CSTD ?= -std=c99 + +TGT_CFLAGS += $(OPT) $(CSTD) $(DEBUG) +TGT_CFLAGS += $(ARCH_FLAGS) +TGT_CFLAGS += -Wall -Wextra -Wshadow -Wimplicit-function-declaration +TGT_CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes +TGT_CFLAGS += -fno-common -ffunction-sections -fdata-sections + +TGT_CPPFLAGS += -MD + +TGT_LDFLAGS += --static -nostartfiles +TGT_LDFLAGS += -T$(LDSCRIPT) +TGT_LDFLAGS += $(ARCH_FLAGS) $(DEBUG) +TGT_LDFLAGS += -Wl,-Map=$(*).map -Wl,--cref +TGT_LDFLAGS += -Wl,--gc-sections +ifeq ($(V),99) +TGT_LDFLAGS += -Wl,--print-gc-sections +endif + +LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +include $(OPENCM3_DIR)/mk/genlink-rules.mk + +%.bin: %.elf + @printf " OBJCOPY $(*).bin\n" + $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + +%.elf: $(OBJS) $(LDSCRIPT) + @printf " LD $(*).elf\n" + $(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $*.elf + +%.o: %.c + @printf " CC $(*).c\n" + $(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $*.o -c $*.c + +.PHONY: clean +clean: + @printf " CLEAN\n" + $(Q)rm -f *.elf *.bin *.o *.d *.map $(LDSCRIPT) + +%.stlink-flash: %.bin + @printf " FLASH $<\n" + $(STFLASH) write $(*).bin 0x8000000 + +.SECONDEXPANSION: +.SECONDARY: + +-include $(OBJS:.o=.d) diff --git a/test-ds/ds18b20.c b/test-ds/ds18b20.c new file mode 100644 index 0000000..53df2a6 --- /dev/null +++ b/test-ds/ds18b20.c @@ -0,0 +1,419 @@ +// DS18B20 Temperature Sensors + +#include "util.h" +#include "ds18b20.h" + +#include +#include +#include +#include +#include +#include + +static volatile u32 ds_dma_buffer; + +#define DS_TIMER TIM3 +#define DS_GPIO GPIOA +#define DS_PIN GPIO7 +#define DS_DMA DMA1 +#define DS_DMA_CH 6 + +#define DS_DEBUG +#undef DS_DEBUG2 + +#ifdef DS_DEBUG +#define DEBUG debug_printf +#else +#define DEBUG(xxx, ...) do { } while (0) +#endif + +#ifdef DS_DEBUG2 +#define DEBUG2 debug_printf +#else +#define DEBUG2(xxx, ...) do { } while (0) +#endif + +// Current temperature +int ds_current_temp = DS_TEMP_UNKNOWN; + +// Auxiliary functions which are missing from libopencm3 +static inline bool timer_is_counter_enabled(u32 timer) +{ + return TIM_CR1(timer) & TIM_CR1_CEN; +} + +static inline void timer_enable_dma_cc1(u32 timer) +{ + TIM_DIER(timer) |= TIM_DIER_CC1DE; +} + +static inline void timer_disable_dma_cc1(u32 timer) +{ + TIM_DIER(timer) &= ~TIM_DIER_CC1DE; +} + +static bool ds_reset(void) +{ + DEBUG2("DS18B20: Reset\n"); + timer_disable_counter(DS_TIMER); + timer_one_shot_mode(DS_TIMER); + + // DMA for reading pin state + ds_dma_buffer = 0xdeadbeef; + dma_set_memory_address(DS_DMA, DS_DMA_CH, (u32) &ds_dma_buffer); + dma_set_peripheral_address(DS_DMA, DS_DMA_CH, (u32) &GPIO_IDR(DS_GPIO)); + dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1); + dma_enable_channel(DS_DMA, DS_DMA_CH); + + // CC1 is used to drive the DMA (read line state at specified time) + timer_disable_oc_output(DS_TIMER, TIM_OC1); + timer_set_oc_mode(DS_TIMER, TIM_OC1, TIM_OCM_FROZEN); + timer_set_oc_value(DS_TIMER, TIM_OC1, 560); + timer_set_dma_on_compare_event(DS_TIMER); + timer_enable_dma_cc1(DS_TIMER); + + // CC2 is used to generate pulses (return line to idle state at specified time) + timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH); + timer_enable_oc_output(DS_TIMER, TIM_OC2); + timer_set_oc_value(DS_TIMER, TIM_OC2, 480); + timer_set_oc_polarity_low(DS_TIMER, TIM_OC2); + + // Set timer period to the length of the whole transaction (1 ms) + timer_set_period(DS_TIMER, 999); + + // Pull line down and start timer + cm_disable_interrupts(); + timer_enable_counter(DS_TIMER); + timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE); + cm_enable_interrupts(); + + // Wait until the timer expires + while (timer_is_counter_enabled(DS_TIMER)) + ; + // Counter is automatically disabled at the end of cycle + + // Disable DMA + timer_disable_dma_cc1(DS_TIMER); + dma_disable_channel(DS_DMA, DS_DMA_CH); + + DEBUG2("Init DMA: %08x [%u] (%u remains)\n", + ds_dma_buffer, + !!(ds_dma_buffer & GPIO7), + dma_get_number_of_data(DS_DMA, DS_DMA_CH)); + + // Did the device respond? + if (ds_dma_buffer & GPIO7) { + DEBUG("DS18B20: Initialization failed\n"); + return 0; + } else + return 1; +} + +static void ds_send_bit(bool bit) +{ + timer_set_period(DS_TIMER, 99); // Each write slot takes 100 μs + timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH); + timer_set_oc_value(DS_TIMER, TIM_OC2, (bit ? 3 : 89)); // 1: 3μs pulse, 0: 89μs pulse + cm_disable_interrupts(); + // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter, + // otherwise OC triggers immediately. Reasons? + timer_enable_counter(DS_TIMER); + timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE); + cm_enable_interrupts(); + while (timer_is_counter_enabled(DS_TIMER)) + ; +} + +static void ds_send_byte(byte b) +{ + DEBUG2("DS write: %02x\n", b); + for (uint m = 1; m < 0x100; m <<= 1) + ds_send_bit(b & m); +} + +static bool ds_recv_bit(void) +{ + timer_set_period(DS_TIMER, 79); // Each read slot takes 80μs + timer_set_oc_value(DS_TIMER, TIM_OC2, 2); // Generate 2μs pulse to start read slot + timer_set_oc_value(DS_TIMER, TIM_OC1, 8); // Sample data 8μs after start of slot + timer_enable_dma_cc1(DS_TIMER); + + ds_dma_buffer = 0xdeadbeef; + dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1); + dma_enable_channel(DS_DMA, DS_DMA_CH); + timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH); + cm_disable_interrupts(); + timer_enable_counter(DS_TIMER); + timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE); + cm_enable_interrupts(); + while (timer_is_counter_enabled(DS_TIMER)) + ; + // DEBUG2("XXX %08x\n", ds_dma_buffer); + bool out = ds_dma_buffer & GPIO7; + dma_disable_channel(DS_DMA, DS_DMA_CH); + + timer_disable_dma_cc1(DS_TIMER); + + return out; +} + +static byte ds_recv_byte(void) +{ + uint out = 0; + for (uint m = 1; m < 0x100; m <<= 1) { + if (ds_recv_bit()) + out |= m; + } + + DEBUG2("DS read: %02x\n", out); + return out; +} + +static byte ds_buf[10]; + +static byte ds_crc_block(uint n) +{ + /// XXX: This might be worth optimizing + uint crc = 0; + + for (uint i = 0; i < n; i++) { + byte b = ds_buf[i]; + for (uint j = 0; j < 8; j++) { + uint k = (b & 1) ^ (crc >> 7); + crc = (crc << 1) & 0xff; + if (k) + crc ^= 0x31; + b >>= 1; + } + } + + return crc; +} + +static bool ds_recv_block(uint n) +{ + for (uint i = 0; i < n; i++) + ds_buf[i] = ds_recv_byte(); + + byte crc = ds_crc_block(n); + if (crc) { + DEBUG("DS18B20: Invalid CRC %02x\n", crc); + return 0; + } + return 1; +} + +struct ds_sensor ds_sensors[DS_NUM_SENSORS]; + +#if DS_NUM_SENSORS == 1 + +static void ds_enumerate(void) +{ + if (!ds_reset()) + return; + + ds_send_byte(0x33); // READ_ROM + if (!ds_recv_block(8)) + return; + + DEBUG("DS18B20: Found sensor "); + for (uint i = 0; i < 8; i++) { + DEBUG("%02x", ds_buf[i]); + ds_sensors[0].address[i] = ds_buf[i]; + } + DEBUG("\n"); +} + +#else + +static void ds_enumerate(void) +{ + /* + * The enumeration algorithm roughly follows the one described in the + * Book of iButton Standards (Maxim Integrated Application Note 937). + * + * It simulates depth-first search on the trie of all device IDs. + * In each pass, it walks the trie from the root and recognizes branching nodes. + * + * The old_choice variable remembers the deepest left branch taken in the + * previous pass, new_choice is the same for the current pass. + */ + + DEBUG("DS18B20: Enumerate\n"); + + uint num_sensors = 0; + byte *addr = ds_buf; + byte old_choice = 0; + + for (;;) { + if (!ds_reset()) { + DEBUG("DS18B20: Enumeration found no sensor\n"); + return; + } + + ds_send_byte(0xf0); // SEARCH_ROM + byte new_choice = 0; + for (byte i=0; i<64; i++) { + bool have_one = ds_recv_bit(); + bool have_zero = ds_recv_bit(); + bool old_bit = addr[i/8] & (1U << (i%8)); + bool new_bit; + switch (2*have_one + have_zero) { + case 3: + // This should not happen + DEBUG("DS18B20: Enumeration failed\n"); + return; + case 1: + // Only 0 + new_bit = 0; + break; + case 2: + // Only 1 + new_bit = 1; + break; + default: + // Both + if (i == old_choice) + new_bit = 1; + else if (i > old_choice) { + new_bit = 0; + new_choice = i; + } else { + new_bit = old_bit; + if (!new_bit) + new_choice = i; + } + } + if (new_bit) + addr[i/8] |= 1U << (i%8); + else + addr[i/8] &= ~(1U << (i%8)); + ds_send_bit(new_bit); + } + + if (num_sensors >= DS_NUM_SENSORS) { + DEBUG("DS18B20: Too many sensors\n"); + return; + } + + DEBUG("DS18B20: Found sensor #%u: ", num_sensors); + for (byte i=0; i<8; i++) + DEBUG("%02x", addr[i]); + if (ds_crc_block(8)) { + DEBUG(" - invalid CRC!\n"); + } else if (ds_buf[0] == 0x28) { + DEBUG("\n"); + memcpy(ds_sensors[num_sensors].address, ds_buf, 8); + num_sensors++; + } else { + DEBUG(" - wrong type\n"); + } + + old_choice = new_choice; + if (!old_choice) + break; + } +} + +#endif + +void ds_init(void) +{ + DEBUG("DS18B20: Init\n"); + + for (uint i = 0; i < DS_NUM_SENSORS; i++) { + memset(ds_sensors[i].address, 0, 8); + ds_sensors[i].current_temp = DS_TEMP_UNKNOWN; + } + + dma_set_read_from_peripheral(DS_DMA, DS_DMA_CH); + dma_set_priority(DS_DMA, DS_DMA_CH, DMA_CCR_PL_VERY_HIGH); + dma_disable_peripheral_increment_mode(DS_DMA, DS_DMA_CH); + dma_enable_memory_increment_mode(DS_DMA, DS_DMA_CH); + dma_set_peripheral_size(DS_DMA, DS_DMA_CH, DMA_CCR_PSIZE_16BIT); + dma_set_memory_size(DS_DMA, DS_DMA_CH, DMA_CCR_MSIZE_16BIT); + + timer_set_prescaler(DS_TIMER, 71); // 1 tick = 1 μs + timer_set_mode(DS_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); + timer_disable_preload(DS_TIMER); + + gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO7); + + ds_enumerate(); + + // FIXME: Configure precision? +} + +#if DS_NUM_SENSORS == 1 +#define ds_current_id 0 +#else + static byte ds_current_id; +#endif + +static bool ds_activate(void) +{ + if (!ds_reset()) { + DEBUG("DS18B20: Reset failed\n"); + return false; + } +#if DS_NUM_SENSORS == 1 + ds_send_byte(0xcc); // SKIP_ROM +#else + ds_send_byte(0x55); // MATCH_ROM + for (uint i = 0; i < 8; i++) + ds_send_byte(ds_sensors[ds_current_id].address[i]); +#endif + return true; +} + +void ds_step(void) +{ + static byte ds_running; + static byte ds_timeout; + + if (!ds_running) { + // Start measurement +#if DS_NUM_SENSORS != 1 + uint maxn = DS_NUM_SENSORS; + do { + if (!maxn--) + return; + ds_current_id++; + if (ds_current_id >= DS_NUM_SENSORS) + ds_current_id = 0; + } while (!ds_sensors[ds_current_id].address[0]); +#endif + if (!ds_activate()) { + ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN; + return; + } + ds_send_byte(0x44); // CONVERT_T + ds_running = 1; + ds_timeout = 255; + } else { + // Still running? + if (!ds_recv_bit()) { + if (!ds_timeout--) { + DEBUG("DS18B20 #%u: Timeout\n", ds_current_id); + ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN; + ds_running = 0; + } + return; + } + ds_running = 0; + + // Read scratch pad + if (!ds_activate()) + return; + ds_send_byte(0xbe); // READ_SCRATCHPAD + if (!ds_recv_block(9)) { + ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN; + return; + } + int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8)); + t = t * 1000 / 16; + + DEBUG("DS18B20 #%u: %d.%03d degC\n", ds_current_id, t / 1000, t % 1000); + ds_sensors[ds_current_id].current_temp = t; + } +} diff --git a/test-ds/ds18b20.h b/test-ds/ds18b20.h new file mode 100644 index 0000000..6ca5d75 --- /dev/null +++ b/test-ds/ds18b20.h @@ -0,0 +1,22 @@ +// DS18B20 Temperature Sensors + +#ifndef _DS18B20_H +#define _DS18B20_H + +// Maximum number of supported sensors +#define DS_NUM_SENSORS 8 + +struct ds_sensor { + byte address[8]; // All zeroes if sensor does not exist. + // Otherwise, address[0] is guaranteed to be non-zero. + int current_temp; // Temperature in m°C or DS_TEMP_UNKNOWN +}; + +extern struct ds_sensor ds_sensors[DS_NUM_SENSORS]; + +#define DS_TEMP_UNKNOWN 0x7fffffff + +void ds_init(void); +void ds_step(void); + +#endif diff --git a/test-ds/test.c b/test-ds/test.c new file mode 100644 index 0000000..309fc75 --- /dev/null +++ b/test-ds/test.c @@ -0,0 +1,76 @@ +#include "util.h" +#include "ds18b20.h" + +#include +#include +#include +#include +#include + +static void clock_setup(void) +{ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_USART1); + rcc_periph_clock_enable(RCC_TIM3); + rcc_periph_clock_enable(RCC_DMA1); +} + +static void gpio_setup(void) +{ + gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); +} + +static volatile u32 ms_ticks; + +void sys_tick_handler(void) +{ + ms_ticks++; +} + +static void tick_setup(void) +{ + systick_set_frequency(1000, 72000000); + systick_counter_enable(); + systick_interrupt_enable(); +} + +static void delay_ms(uint ms) +{ + u32 start_ticks = ms_ticks; + while (ms_ticks - start_ticks < ms) + ; +} + +static void usart_setup(void) +{ + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); + + usart_set_baudrate(USART1, 115200); + usart_set_databits(USART1, 8); + usart_set_stopbits(USART1, USART_STOPBITS_1); + usart_set_mode(USART1, USART_MODE_TX); + usart_set_parity(USART1, USART_PARITY_NONE); + usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); + + usart_enable(USART1); +} + +int main(void) +{ + clock_setup(); + gpio_setup(); + tick_setup(); + usart_setup(); + + ds_init(); + for (;;) { + gpio_toggle(GPIOC, GPIO13); + delay_ms(100); + ds_step(); + } + + return 0; +} diff --git a/test-ds/util-debug.c b/test-ds/util-debug.c new file mode 100644 index 0000000..a1698cf --- /dev/null +++ b/test-ds/util-debug.c @@ -0,0 +1,166 @@ +#include "util.h" + +#include + +#include +#include + +#ifdef DEBUG_SEMIHOSTING + +void semi_put_char(char c) +{ + // This is tricky, we need to work around GCC bugs + volatile char cc = c; + asm volatile ( + "mov r0, #0x03\n" /* SYS_WRITEC */ + "mov r1, %[msg]\n" + "bkpt #0xAB\n" + : + : [msg] "r" (&cc) + : "r0", "r1" + ); +} + +void semi_write_string(char *c) +{ + asm volatile ( + "mov r0, #0x04\n" /* SYS_WRITE0 */ + "mov r1, %[msg]\n" + "bkpt #0xAB\n" + : + : [msg] "r" (c) + : "r0", "r1" + ); +} + +#endif + +void debug_putc(int c) +{ +#ifdef DEBUG_SEMIHOSTING + static char debug_buf[128]; + static int debug_i; + debug_buf[debug_i++] = c; + if (c == '\n' || debug_i >= sizeof(debug_buf) - 1) { + debug_buf[debug_i] = 0; + semi_write_string(debug_buf); + debug_i = 0; + } +#endif +#ifdef DEBUG_USART + if (c == '\n') + usart_send_blocking(USART1, '\r'); + usart_send_blocking(USART1, c); +#endif +} + +void debug_puts(const char *s) +{ + while (*s) + debug_putc(*s++); +} + +enum printf_flags { + PF_ZERO_PAD = 1, + PF_SIGNED = 2, + PF_NEGATIVE = 4, + PF_UPPERCASE = 8, + PF_LEFT = 16, +}; + +static void printf_string(const char *s, uint width, uint flags) +{ + uint len = strlen(s); + uint pad = (len < width) ? width - len : 0; + char pad_char = (flags & PF_ZERO_PAD) ? '0' : ' '; + + if (flags & PF_LEFT) + debug_puts(s); + while (pad--) + debug_putc(pad_char); + if (!(flags & PF_LEFT)) + debug_puts(s); +} + +static void printf_number(uint i, uint width, uint flags, uint base) +{ + char buf[16]; + char *w = buf + sizeof(buf); + + if (flags & PF_SIGNED) { + if ((int) i < 0) { + i = - (int) i; + flags |= PF_NEGATIVE; + } + } + + *--w = 0; + do { + uint digit = i % base; + if (digit < 10) + *--w = '0' + digit; + else + *--w = ((flags & PF_UPPERCASE) ? 'A' : 'a') + digit - 10; + i /= base; + } + while (i); + + if (flags & PF_NEGATIVE) + *--w = '-'; + + printf_string(w, width, flags); +} + +void debug_printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + while (*fmt) { + int c = *fmt++; + if (c != '%') { + debug_putc(c); + continue; + } + + uint width = 0; + uint flags = 0; + + if (*fmt == '-') { + fmt++; + flags |= PF_LEFT; + } + + if (*fmt == '0') { + fmt++; + flags |= PF_ZERO_PAD; + } + + while (*fmt >= '0' && *fmt <= '9') + width = 10*width + *fmt++ - '0'; + + c = *fmt++; + switch (c) { + case 'd': + printf_number(va_arg(args, int), width, flags | PF_SIGNED, 10); + break; + case 'u': + printf_number(va_arg(args, int), width, flags, 10); + break; + case 'X': + flags |= PF_UPPERCASE; + // fall-thru + case 'x': + printf_number(va_arg(args, int), width, flags, 16); + break; + case 's': + printf_string(va_arg(args, char *), width, flags); + break; + default: + debug_putc(c); + continue; + } + } + + va_end(args); +} diff --git a/test-ds/util.h b/test-ds/util.h new file mode 100644 index 0000000..6d8c9cc --- /dev/null +++ b/test-ds/util.h @@ -0,0 +1,69 @@ +#include +#include + +typedef unsigned int uint; +typedef uint8_t byte; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; + +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +static inline uint get_u16_le(byte *p) +{ + return (p[1] << 8) | p[0]; +} + +static inline uint get_u16_be(byte *p) +{ + return (p[0] << 8) | p[1]; +} + +static inline uint get_u32_le(byte *p) +{ + return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; +} + +static inline uint get_u32_be(byte *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +static inline void put_u16_le(byte *p, u16 x) +{ + p[0] = x; + p[1] = x >> 8; +} + +static inline void put_u16_be(byte *p, u16 x) +{ + p[0] = x >> 8; + p[1] = x; +} + +static inline void put_u32_be(byte *p, u32 x) +{ + p[0] = x >> 24; + p[1] = (x >> 16) & 0xff; + p[2] = (x >> 8) & 0xff; + p[3] = x & 0xff; +} + +static inline void put_u32_le(byte *p, u32 x) +{ + p[3] = x >> 24; + p[2] = (x >> 16) & 0xff; + p[1] = (x >> 8) & 0xff; + p[0] = x & 0xff; +} + +// debug.c + +// #define DEBUG_SEMIHOSTING +#define DEBUG_USART USART1 + +void debug_printf(const char *fmt, ...); +void debug_puts(const char *s); +void debug_putc(int c);