An example project for the Heltec Turtle LoRa board (STM32L4 and SX1276 chips). The projects is only supported for the Nucleo-L432KC board platform in the mbed online and offline compiler environment. Visit www.radioshuttle.de (choose Turtle board) for instructions. Note that most source files and libraries are open source, however some files especially the RadioShuttle core protocol is copyrighted work. Check header for details.
Dependencies: mbed BufferedSerial SX1276GenericLib OLED_SSD1306 HELIOS_Si7021 NVProperty RadioShuttle-STM32L4 USBDeviceHT
utils.cpp
00001 /* 00002 * Copyright (c) 2019 Helmut Tschemernjak 00003 * 30826 Garbsen (Hannover) Germany 00004 */ 00005 #include "main.h" 00006 #include "GenericPingPong.h" 00007 #include "RadioTest.h" 00008 #ifdef TOOLCHAIN_GCC 00009 #include <malloc.h> 00010 #endif 00011 volatile uint32_t PendingInterrupts; // global interrupt mask of received interrupts 00012 00013 time_t cvt_date(char const *date, char const *time); 00014 00015 static float GetBrownOutVolt(void); 00016 #ifdef FEATURE_SI7021 00017 HELIOS_Si7021 *sensorSI7021; 00018 #endif 00019 BufferedSerial *ser; 00020 #ifdef FEATURE_USBSERIAL 00021 USBSerialBuffered *usb; 00022 #endif 00023 bool _useDprintf; 00024 00025 void InitSerial(int timeout, DigitalOut *led, InterruptIn *intr) 00026 { 00027 _useDprintf = true; 00028 bool uartActive = true; 00029 00030 #ifdef FEATURE_USBSERIAL 00031 DigitalOut rx(USBRX); // need to turn rx low to avoid floating signal 00032 rx = 0; 00033 DigitalIn uartRX(USBRX); 00034 uartActive = uartRX.read(); 00035 if (!uartActive) { 00036 usb = new USBSerialBuffered(); 00037 Timer t; 00038 t.start(); 00039 while(!usb->connected()) { 00040 if (led) 00041 *led = !*led; 00042 wait_ms(100); 00043 if (timeout) { 00044 if (t.read_ms() >= timeout || (intr && intr->read())) { 00045 delete usb; 00046 usb = NULL; 00047 DigitalOut rx(USBRX); 00048 rx = 0; // need to turn tx low to avoid floating signal 00049 break; 00050 } 00051 } 00052 } 00053 } 00054 #endif 00055 if (uartActive) { 00056 ser = new BufferedSerial(USBTX, USBRX); 00057 ser->baud(230400); 00058 ser->format(8); 00059 } 00060 00061 time_t t = cvt_date(__DATE__, __TIME__); 00062 if (t > time(NULL)) { 00063 set_time(t); 00064 } 00065 } 00066 00067 void RunStartup(void) 00068 { 00069 rprintf("\r\n"); 00070 int mbedversion = 9999; 00071 #ifdef MBED_LIBRARY_VERSION // not available in mbed head compiles 00072 mbedversion = MBED_LIBRARY_VERSION; 00073 #endif 00074 dprintf("Turtle: %d.%d (%s %s mbed: v%d)", MAJOR_VERSION, MINOR_VERSION, __DATE__, __TIME__, mbedversion); 00075 00076 dprintf("SysClock: %u Hz.", (unsigned int)SystemCoreClock); 00077 #ifdef __ARMCC_VERSION 00078 dprintf("ARM Compiler Version: 0x%x", __ARMCC_VERSION); 00079 #elif __GNUC__ 00080 dprintf("GCC Compiler Version: %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); 00081 #endif 00082 00083 const char *errstr; 00084 if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST) != RESET) 00085 errstr = "RESET OCCURRED"; 00086 else 00087 errstr = "initalized"; 00088 00089 dprintf("Brown Out Reset %s (%1.1f V)", errstr, GetBrownOutVolt()); 00090 dprintf("Voltage: %.2f (%s powered)", BatteryVoltage(), BatterySource()); 00091 dprintf("InitDefaults Done"); 00092 MemoryAvailable(true); 00093 __HAL_RCC_CLEAR_RESET_FLAGS(); 00094 #ifdef FEATURE_SI7021 00095 sensorSI7021 = new HELIOS_Si7021(SI7021_SDA, SI7021_SCL); 00096 if (sensorSI7021->hasSensor()) { 00097 dprintf("%s: Rev(%d) %.2f°C Humidity: %.2f%%", sensorSI7021->getModelName(), sensorSI7021->getRevision(), sensorSI7021->readTemperature(), sensorSI7021->readHumidity()); 00098 } 00099 #endif 00100 00101 } 00102 void printTimeStamp() 00103 { 00104 static LowPowerTimer *timer; 00105 if (!timer) { 00106 timer = new LowPowerTimer(); 00107 timer->start(); 00108 } 00109 time_t seconds = time(NULL); 00110 struct tm *tm = localtime(&seconds); 00111 int usecs = timer->read_us(); 00112 if (usecs < 0) { 00113 usecs = 0; 00114 timer->stop(); 00115 timer->reset(); 00116 timer->start(); 00117 } 00118 int msecs = usecs % 1000000; 00119 00120 rprintf("%02d:%02d:%02d.%06d ", tm->tm_hour, tm->tm_min, tm->tm_sec, msecs); 00121 } 00122 00123 void dprintf(const char *format, ...) 00124 { 00125 std::va_list arg; 00126 00127 va_start(arg, format); 00128 VAprintf(true, true, _useDprintf, format, arg); 00129 va_end(arg); 00130 } 00131 00132 void rprintf(const char *format, ...) 00133 { 00134 std::va_list arg; 00135 00136 va_start(arg, format); 00137 VAprintf(false, false, _useDprintf, format, arg); 00138 va_end(arg); 00139 } 00140 00141 void VAprintf(bool timstamp, bool newline, bool printEnabled, const char *format, va_list arg) 00142 { 00143 if (!printEnabled) 00144 return; 00145 00146 if (timstamp) 00147 printTimeStamp(); 00148 #ifdef FEATURE_USBSERIAL 00149 if (usb) { 00150 usb->vprintf_irqsafe(format, arg); 00151 if (newline) 00152 usb->printf_irqsafe("\r\n"); 00153 } 00154 #endif 00155 if (ser) { 00156 // serial jas 00157 int r = 0; 00158 r = vsnprintf(NULL, 0, format, arg); 00159 if (r < 82) { 00160 char buffer[82+1]; 00161 00162 vsnprintf(buffer, sizeof(buffer), format, arg); 00163 r = ser->write(buffer, r); 00164 } else { 00165 char *buffer = new char[r+1]; 00166 if (buffer) { 00167 vsnprintf(buffer, r+1, format, arg); 00168 r = ser->write(buffer, r); 00169 delete[] buffer; 00170 } else { 00171 error("%s %d cannot alloc memory (%d bytes)!\r\n", __FILE__, __LINE__, r+1); 00172 r = 0; 00173 } 00174 } 00175 if (newline) 00176 ser->write("\r\n", 2); 00177 } 00178 } 00179 00180 char *ConsoleReadline(char *buf, int buflen, bool echo, int timeout_ms) 00181 { 00182 int count = 0; 00183 memset(buf, 0, buflen); 00184 00185 #ifdef FEATURE_USBSERIAL 00186 if (usb == NULL && ser == NULL) 00187 return NULL; 00188 #else 00189 if (ser == NULL) 00190 return NULL; 00191 #endif 00192 00193 Timer t; 00194 int start = 0; 00195 if (timeout_ms) { 00196 t.start(); 00197 start = t.read_ms(); 00198 } 00199 00200 #ifdef FEATURE_USBSERIAL 00201 if (usb) { 00202 usb->flush(); 00203 while(usb->readable()) 00204 usb->getc(); // flush old chars 00205 } 00206 #endif 00207 if (ser) { 00208 while(ser->readable()) 00209 ser->getc(); // flush old chars 00210 } 00211 00212 while(true) { 00213 if (timeout_ms && t.read_ms() - start > timeout_ms) 00214 return NULL; 00215 int c = -2; 00216 #ifdef FEATURE_USBSERIAL 00217 if (usb && usb->readable()) 00218 c = usb->getc(); 00219 #endif 00220 if (ser && ser->readable()) 00221 c = ser->getc(); 00222 if (c == -2) 00223 continue; 00224 00225 if (c == 0 || c == -1 || c == '\r' || c == '\n' || c == 3 || c == 4) 00226 break; 00227 if (c == '\b' || c == 0x7f) { // backspace 00228 if (count < 1) 00229 continue; 00230 buf[--count] = 0; 00231 if (echo) 00232 rprintf("\b \b"); 00233 #ifdef FEATURE_USBSERIAL 00234 if (usb) 00235 usb->flush(); 00236 #endif 00237 continue; 00238 } 00239 if (echo) { 00240 rprintf("%c", c); 00241 #ifdef FEATURE_USBSERIAL 00242 if (usb) 00243 usb->flush(); 00244 #endif 00245 } 00246 00247 start = t.read_ms(); 00248 buf[count] = c; 00249 if (count++ >= buflen-2) 00250 break; 00251 // dprintf("Got char: '%c'(%d)", c, c); 00252 } 00253 00254 if (echo) 00255 rprintf("\r\n"); 00256 if (count) 00257 return buf; 00258 return NULL; 00259 } 00260 00261 00262 void dump(const char *title, void *data, int len) 00263 { 00264 dump(title, data, len, false); 00265 } 00266 00267 void dump(const char *title, const void *data, int len, bool dwords) 00268 { 00269 dprintf("dump(\"%s\", 0x%x, %d bytes)", title, (unsigned int)data, len); 00270 00271 int i, j, cnt; 00272 unsigned char *u; 00273 const int width = 16; 00274 const int seppos = 7; 00275 00276 cnt = 0; 00277 u = (unsigned char *)data; 00278 while (len > 0) { 00279 rprintf("%08x: ", (unsigned int)data + cnt); 00280 if (dwords) { 00281 unsigned int *ip = ( unsigned int *)u; 00282 rprintf(" 0x%08x\r\n", *ip); 00283 u+= 4; 00284 len -= 4; 00285 cnt += 4; 00286 continue; 00287 } 00288 cnt += width; 00289 j = len < width ? len : width; 00290 for (i = 0; i < j; i++) { 00291 rprintf("%2.2x ", *(u + i)); 00292 if (i == seppos) 00293 rprintf(" "); 00294 } 00295 rprintf(" "); 00296 if (j < width) { 00297 i = width - j; 00298 if (i > seppos + 1) 00299 rprintf(" "); 00300 while (i--) { 00301 rprintf("%s", " "); 00302 } 00303 } 00304 for (i = 0; i < j; i++) { 00305 int c = *(u + i); 00306 if (c >= ' ' && c <= '~') 00307 rprintf("%c", c); 00308 else 00309 rprintf("."); 00310 if (i == seppos) 00311 rprintf(" "); 00312 } 00313 len -= width; 00314 u += width; 00315 rprintf("\r\n"); 00316 if (ser) 00317 wait_ms(5); // give the serial some time. 00318 } 00319 rprintf("--\r\n"); 00320 } 00321 00322 /* 00323 * Convert compile time to system time 00324 */ 00325 time_t 00326 cvt_date(char const *date, char const *time) 00327 { 00328 char s_month[5]; 00329 int year; 00330 struct tm t; 00331 static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 00332 sscanf(date, "%s %d %d", s_month, &t.tm_mday, &year); 00333 sscanf(time, "%2d %*c %2d %*c %2d", &t.tm_hour, &t.tm_min, &t.tm_sec); 00334 // Find where is s_month in month_names. Deduce month value. 00335 t.tm_mon = (strstr(month_names, s_month) - month_names) / 3; 00336 t.tm_year = year - 1900; 00337 return (int)mktime(&t); 00338 } 00339 00340 00341 00342 void InterruptMSG(enum InterruptDevice irqid) { 00343 help_atomic_or_relaxed(&PendingInterrupts, irqid); 00344 } 00345 00346 00347 uint32_t readclrPendingInterrupts() { 00348 return help_atomic_readclr_relaxed(&PendingInterrupts); 00349 } 00350 00351 uint32_t readPendingInterrupts() { 00352 return help_atomic_load_relaxed(&PendingInterrupts); 00353 } 00354 00355 const char * 00356 BatterySource(void) 00357 { 00358 const char *pwrSource = "Battery"; 00359 #ifdef BATPOWER_EN 00360 { 00361 DigitalIn pwr(BATPOWER_EN); 00362 if (pwr == BATPOWER_EXT) 00363 pwrSource = "USB"; 00364 } 00365 #endif 00366 return pwrSource; 00367 } 00368 00369 00370 float 00371 GetBrownOutVolt(void) 00372 { 00373 unsigned int *FlashOptionRegister = (unsigned int *)0x1FFF7800; 00374 00375 int val = *FlashOptionRegister >> 8 & 0x7; // masking out the BOR bits 9-11 00376 switch(val) { 00377 case 0: 00378 return 1.7; 00379 case 1: 00380 return 2.0; 00381 case 2: 00382 return 2.2; 00383 case 3: 00384 return 2.5; 00385 case 4: 00386 return 2.8; 00387 default: 00388 return 999; 00389 } 00390 } 00391 00392 void MCUReset(void) 00393 { 00394 #define AIRCR_VECTKEY_MASK 0x05FA0000 00395 SCB->AIRCR = AIRCR_VECTKEY_MASK | 0x04; // NVIC_GenerateSystemReset(); 00396 } 00397 00398 00399 #define FREEMEM_CELL 100 00400 00401 struct elem { /* Definition of a structure that is FREEMEM_CELL bytes in size.) */ 00402 struct elem *next; 00403 char dummy[FREEMEM_CELL-2]; 00404 }; 00405 00406 size_t 00407 MemoryAvailable(bool print) 00408 { 00409 size_t counter; 00410 #ifdef TOOLCHAIN_GCC 00411 struct mallinfo mi = mallinfo(); 00412 extern char end[]; 00413 extern char _estack[]; 00414 counter = (_estack - end) - mi.uordblks; 00415 if (print) 00416 dprintf("MemoryAvailable: %d kB (%d bytes)", counter/1024, counter); 00417 return counter; 00418 #else 00419 struct elem *head, *current, *nextone; 00420 current = head = (struct elem*) malloc(sizeof(struct elem)); 00421 if (head == NULL) 00422 return 0; /*No memory available.*/ 00423 counter = 0; 00424 // __disable_irq(); 00425 do { 00426 counter++; 00427 current->next = (struct elem*) malloc(sizeof(struct elem)); 00428 current = current->next; 00429 } while (current != NULL); 00430 /* Now counter holds the number of type elem 00431 structures we were able to allocate. We 00432 must free them all before returning. */ 00433 current = head; 00434 do { 00435 nextone = current->next; 00436 free(current); 00437 current = nextone; 00438 } while (nextone != NULL); 00439 // __enable_irq(); 00440 00441 if (print) 00442 dprintf("MemoryAvailable: %d kB (%d bytes)", (counter*FREEMEM_CELL)/1024, counter*FREEMEM_CELL); 00443 return counter*FREEMEM_CELL; 00444 #endif 00445 } 00446 00447 00448 static const char *cmds = \ 00449 "\r\nThe following commands are available:\r\n\r\n" \ 00450 " p -- Property Editor\r\n" \ 00451 " t -- LoRa PingPong Test\r\n" \ 00452 " x -- LoRa TX Continuous Wave Test\r\n" \ 00453 " d -- Hexdump of memory address [offset count]\r\n" 00454 " r -- Reset\r\n" \ 00455 " c -- Continue with RadioShuttle RadioTest\r\n" \ 00456 "\r\n" \ 00457 "waiting 10 secs ...\r\n" \ 00458 "\r\n"; 00459 00460 void RunCommands(int timeout_ms) { 00461 bool cmdLoop = true; 00462 while(cmdLoop) { 00463 char buf[32]; 00464 00465 rprintf(cmds); 00466 rprintf("Turtle$ "); 00467 if (ConsoleReadline(buf, sizeof(buf), true, timeout_ms) == NULL) { 00468 cmdLoop = false; 00469 break; 00470 } 00471 switch(buf[0]) { 00472 case 'p': 00473 case 'P': 00474 #ifdef FEATURE_NVPROPERTYEDITOR 00475 NVPropertyEditor(); 00476 #endif 00477 break; 00478 case 't': 00479 case 'T': 00480 #ifdef FEATURE_LORA_PING_PONG 00481 SX1276PingPong(); // basic LoRa raw ping/pong without RadioShuttle 00482 #endif 00483 break; 00484 #ifdef FEATURE_RADIOTESTSAMPLE 00485 case 'x': 00486 case 'X': 00487 RadioContinuesTX(); 00488 #endif 00489 break; 00490 case 'r': 00491 case 'R': 00492 MCUReset(); 00493 break; 00494 case 'd': 00495 case 'D': 00496 { 00497 char *addr = strchr(buf, ' '); 00498 if (addr) { 00499 *addr++ = 0; 00500 char *length = strchr(addr, ' '); 00501 if (length) { 00502 *length++ = 0; 00503 } 00504 unsigned long address = strtoll(addr, NULL, 0); 00505 unsigned long cnt = 32; 00506 if (length) 00507 cnt = strtoll(length, NULL, 0); 00508 dump("Hexdump", (void *)address, cnt); 00509 } 00510 } 00511 break; 00512 case 'c': 00513 case 'C': 00514 cmdLoop = false; 00515 break; 00516 default: 00517 break; 00518 } 00519 } 00520 rprintf("\r\n"); 00521 00522 }
Generated on Wed Jul 13 2022 12:02:31 by 1.7.2