From e79f3d4047aaf9beb8a59f1d30d93c78efae0cc5 Mon Sep 17 00:00:00 2001 From: jaseg Date: Mon, 15 Apr 2019 13:07:11 +0900 Subject: driver/fw: Add tiny printf --- driver/driver.sch | 2 + driver_fw/Makefile | 2 +- driver_fw/ina226.h | 2 + driver_fw/lcd1602.h | 3 + driver_fw/main.c | 36 +++------ driver_fw/mini-printf.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++ driver_fw/mini-printf.h | 50 ++++++++++++ 7 files changed, 277 insertions(+), 26 deletions(-) create mode 100644 driver_fw/mini-printf.c create mode 100644 driver_fw/mini-printf.h diff --git a/driver/driver.sch b/driver/driver.sch index 4fa34e9..c0eff77 100644 --- a/driver/driver.sch +++ b/driver/driver.sch @@ -2747,4 +2747,6 @@ $EndComp Text Notes 7600 10050 0 50 ~ 0 TODO:\n* RS485 drv fp is wide, should be narrow SOIC-8\n* Heatsink holes are plated-through, shouln't be\n* Add thermal reliefs in upper logic ground plane\n* GND/VCC input labels are swapped\n* White label field for MAC\n* Remove optoisolators\n* Add series resistor to RS485 GND\n* Add decoupling cap next to mosfet temp sensor\n* Add INA226 filter network (see datasheet p.14) NoConn ~ 14600 2800 +Text Notes 14100 9650 0 50 ~ 0 +I2C addr=010'0111 $EndSCHEMATC diff --git a/driver_fw/Makefile b/driver_fw/Makefile index 482e265..ef0375d 100644 --- a/driver_fw/Makefile +++ b/driver_fw/Makefile @@ -31,7 +31,7 @@ CFLAGS += -I$(CMSIS_DEV_PATH)/Include -I$(CMSIS_PATH)/Include -I$(HAL_PATH)/Inc #LDFLAGS += -L$(CMSIS_PATH)/Lib/GCC -larm_cortexM0l_math SOURCES = main.c startup_stm32f030x6.s system_stm32f0xx.c base.c $(HAL_PATH)/Src/stm32f0xx_ll_utils.c cmsis_exports.c \ - ../common/8b10b.c serial.c mac.c i2c.c lcd1602.c mcp9801.c ina226.c + ../common/8b10b.c serial.c mac.c i2c.c lcd1602.c mcp9801.c ina226.c mini-printf.c ################################################### diff --git a/driver_fw/ina226.h b/driver_fw/ina226.h index 405be48..5314454 100644 --- a/driver_fw/ina226.h +++ b/driver_fw/ina226.h @@ -7,6 +7,8 @@ #define INA226_I2C_ADDR 0x80 #define INA226_I_LSB_uA 2000 +#define INA226_VB_LSB_uV 1250 +#define INA226_VS_LSB_nV 2500 #define INA226_RSHUNT_uOhm 2000 /* FIXME validate this */ #define INA226_CAL (5120000 / INA226_I_LSB_uA * 1000 / INA226_RSHUNT_uOhm) diff --git a/driver_fw/lcd1602.h b/driver_fw/lcd1602.h index 70b324e..4cb2758 100644 --- a/driver_fw/lcd1602.h +++ b/driver_fw/lcd1602.h @@ -32,6 +32,9 @@ #define LCD_I2C_PERIPH I2C1 #define LCD_I2C_ADDR 0x4e +/* 16 spaces you can concatenate to printf formats to make sure the entire LCD line is always cleared */ +#define LCD_FILL " "" "" "" " + void lcd1602_init(); // Инициализация дисплея void lcd_write_str(uint8_t in_u8X, uint8_t in_u8Y, char* in_cChar); // Отправить строку на экран с указанием позиции void lcd_send_char(char in_cChar); // Отправить символ на экран diff --git a/driver_fw/main.c b/driver_fw/main.c index 1cad970..b528e6e 100644 --- a/driver_fw/main.c +++ b/driver_fw/main.c @@ -23,6 +23,8 @@ #include "mcp9801.h" #include "ina226.h" +#include "mini-printf.h" + #include <8b10b.h> /* Part number: STM32F030F4C6 */ @@ -256,11 +258,6 @@ void SVC_Handler(void) { void PendSV_Handler(void) { } -char hexdigit(uint8_t nibble) { - nibble &= 0xf; - return (nibble < 10) ? ('0' + nibble) : ('A' + nibble - 10); -} - void SysTick_Handler(void) { sys_time_tick++; sys_time_ms += TICK_MS; @@ -268,28 +265,17 @@ void SysTick_Handler(void) { sys_time_ms = 0; sys_time_s++; - int32_t temp = mcp9801_read_mdegC(); - temp /= 100; - char buf[17] = { 0 }; - strcpy(buf, "Temp: +XXX.X\xdf""C"" "); - buf[6] = temp >= 0 ? '+' : '-'; - buf[7] = temp/1000 + '0'; - buf[8] = (temp%1000)/100 + '0'; - buf[9] = (temp%100)/10 + '0'; - buf[11] = temp%10 + '0'; + char buf[17]; + + int temp = mcp9801_read_mdegC(); + int deg = temp/1000; + int frac = (temp%1000)/100; + mini_snprintf(buf, sizeof(buf), "Temp: %d.%01d\xdf""C" LCD_FILL, deg, frac); lcd_write_str(0, 0, buf); - strcpy(buf, "INA:XXXX""/XXX""X "); - uint16_t rx = ina226_read_i(); - buf[4] = hexdigit(rx>>12); - buf[5] = hexdigit(rx>>8); - buf[6] = hexdigit(rx>>4); - buf[7] = hexdigit(rx>>0); - rx = ina226_read_v(); - buf[9] = hexdigit(rx>>12); - buf[10] = hexdigit(rx>>8); - buf[11] = hexdigit(rx>>4); - buf[12] = hexdigit(rx>>0); + + mini_snprintf(buf, sizeof(buf), "I=%dmA U=%dmV" LCD_FILL, ina226_read_i()*INA226_I_LSB_uA/1000, ina226_read_v()*INA226_VB_LSB_uV/1000); lcd_write_str(0, 1, buf); + mcp9801_init(); } diff --git a/driver_fw/mini-printf.c b/driver_fw/mini-printf.c new file mode 100644 index 0000000..53cfe99 --- /dev/null +++ b/driver_fw/mini-printf.c @@ -0,0 +1,208 @@ +/* + * The Minimal snprintf() implementation + * + * Copyright (c) 2013,2014 Michal Ludvig + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the auhor nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ---- + * + * This is a minimal snprintf() implementation optimised + * for embedded systems with a very limited program memory. + * mini_snprintf() doesn't support _all_ the formatting + * the glibc does but on the other hand is a lot smaller. + * Here are some numbers from my STM32 project (.bin file size): + * no snprintf(): 10768 bytes + * mini snprintf(): 11420 bytes (+ 652 bytes) + * glibc snprintf(): 34860 bytes (+24092 bytes) + * Wasting nearly 24kB of memory just for snprintf() on + * a chip with 32kB flash is crazy. Use mini_snprintf() instead. + * + */ + +#include "mini-printf.h" + +static unsigned int +mini_strlen(const char *s) +{ + unsigned int len = 0; + while (s[len] != '\0') len++; + return len; +} + +static unsigned int +mini_itoa(int value, unsigned int radix, unsigned int uppercase, unsigned int unsig, + char *buffer, unsigned int zero_pad) +{ + char *pbuffer = buffer; + int negative = 0; + unsigned int i, len; + + /* No support for unusual radixes. */ + if (radix > 16) + return 0; + + if (value < 0 && !unsig) { + negative = 1; + value = -value; + } + + /* This builds the string back to front ... */ + do { + int digit = value % radix; + *(pbuffer++) = (digit < 10 ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10); + value /= radix; + } while (value > 0); + + for (i = (pbuffer - buffer); i < zero_pad; i++) + *(pbuffer++) = '0'; + + if (negative) + *(pbuffer++) = '-'; + + *(pbuffer) = '\0'; + + /* ... now we reverse it (could do it recursively but will + * conserve the stack space) */ + len = (pbuffer - buffer); + for (i = 0; i < len / 2; i++) { + char j = buffer[i]; + buffer[i] = buffer[len-i-1]; + buffer[len-i-1] = j; + } + + return len; +} + +struct mini_buff { + char *buffer, *pbuffer; + unsigned int buffer_len; +}; + +static int +_putc(int ch, struct mini_buff *b) +{ + if ((unsigned int)((b->pbuffer - b->buffer) + 1) >= b->buffer_len) + return 0; + *(b->pbuffer++) = ch; + *(b->pbuffer) = '\0'; + return 1; +} + +static int +_puts(char *s, unsigned int len, struct mini_buff *b) +{ + unsigned int i; + + if (b->buffer_len - (b->pbuffer - b->buffer) - 1 < len) + len = b->buffer_len - (b->pbuffer - b->buffer) - 1; + + /* Copy to buffer */ + for (i = 0; i < len; i++) + *(b->pbuffer++) = s[i]; + *(b->pbuffer) = '\0'; + + return len; +} + +int +mini_vsnprintf(char *buffer, unsigned int buffer_len, const char *fmt, va_list va) +{ + struct mini_buff b; + char bf[24]; + char ch; + + b.buffer = buffer; + b.pbuffer = buffer; + b.buffer_len = buffer_len; + + while ((ch=*(fmt++))) { + if ((unsigned int)((b.pbuffer - b.buffer) + 1) >= b.buffer_len) + break; + if (ch!='%') + _putc(ch, &b); + else { + char zero_pad = 0; + char *ptr; + unsigned int len; + + ch=*(fmt++); + + /* Zero padding requested */ + if (ch=='0') { + ch=*(fmt++); + if (ch == '\0') + goto end; + if (ch >= '0' && ch <= '9') + zero_pad = ch - '0'; + ch=*(fmt++); + } + + switch (ch) { + case 0: + goto end; + + case 'u': + case 'd': + len = mini_itoa(va_arg(va, unsigned int), 10, 0, (ch=='u'), bf, zero_pad); + _puts(bf, len, &b); + break; + + case 'x': + case 'X': + len = mini_itoa(va_arg(va, unsigned int), 16, (ch=='X'), 1, bf, zero_pad); + _puts(bf, len, &b); + break; + + case 'c' : + _putc((char)(va_arg(va, int)), &b); + break; + + case 's' : + ptr = va_arg(va, char*); + _puts(ptr, mini_strlen(ptr), &b); + break; + + default: + _putc(ch, &b); + break; + } + } + } +end: + return b.pbuffer - b.buffer; +} + + +int +mini_snprintf(char* buffer, unsigned int buffer_len, const char *fmt, ...) +{ + int ret; + va_list va; + va_start(va, fmt); + ret = mini_vsnprintf(buffer, buffer_len, fmt, va); + va_end(va); + + return ret; +} diff --git a/driver_fw/mini-printf.h b/driver_fw/mini-printf.h new file mode 100644 index 0000000..99a9519 --- /dev/null +++ b/driver_fw/mini-printf.h @@ -0,0 +1,50 @@ +/* + * The Minimal snprintf() implementation + * + * Copyright (c) 2013 Michal Ludvig + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the auhor nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __MINI_PRINTF__ +#define __MINI_PRINTF__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +int mini_vsnprintf(char* buffer, unsigned int buffer_len, const char *fmt, va_list va); +int mini_snprintf(char* buffer, unsigned int buffer_len, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#define vsnprintf mini_vsnprintf +#define snprintf mini_snprintf + +#endif -- cgit