/*- * Copyright 2022 John-Mark Gurney. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 OR CONTRIBUTORS 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. * */ #include #include #include #include #include #include #include SYSINIT(hal_init, SI_SUB_HAL, SI_ORDER_FIRST, (void (*)(const void *))HAL_Init, NULL); static int dorx = 0; static uint8_t rxbuf[128]; static int gotrxdone = 0; static int gottxdone = 0; static int goterr = 0; static int rxbufsize = 0; static int rxbuftrunc = 0; static void c13led(const void *none) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct = (GPIO_InitTypeDef){ .Pin = GPIO_PIN_13, .Mode = GPIO_MODE_OUTPUT_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_LOW, }; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); } SYSINIT(c13led, SI_SUB_HAL, SI_ORDER_SECOND, c13led, NULL); #if 0 #undef usb_printf #define usb_printf debug_printf #endif /* * Referenced from: * Projects/STM32F103RB-Nucleo/Applications/USB_Device/HID_Standalone/Src/main.c */ static void oscconfig(const void *none) { RCC_ClkInitTypeDef clkinitstruct; RCC_OscInitTypeDef oscinitstruct; RCC_PeriphCLKInitTypeDef rccperiphclkinit; __HAL_RCC_PWR_CLK_ENABLE(); oscinitstruct = (RCC_OscInitTypeDef){ .OscillatorType = RCC_OSCILLATORTYPE_HSE, .HSEState = RCC_HSE_ON, .HSEPredivValue = RCC_HSE_PREDIV_DIV1, .PLL.PLLMUL = RCC_PLL_MUL9, .PLL.PLLState = RCC_PLL_ON, .PLL.PLLSource = RCC_PLLSOURCE_HSE, }; HAL_RCC_OscConfig(&oscinitstruct); /* USB clock selection */ rccperiphclkinit = (RCC_PeriphCLKInitTypeDef){ .PeriphClockSelection = RCC_PERIPHCLK_USB, .UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5, }; HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit); /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ clkinitstruct = (RCC_ClkInitTypeDef){ .ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2), .SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK, .AHBCLKDivider = RCC_SYSCLK_DIV1, .APB1CLKDivider = RCC_HCLK_DIV2, .APB2CLKDivider = RCC_HCLK_DIV1, }; HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2); } SYSINIT(oscconfig, SI_SUB_HAL, SI_ORDER_THIRD, oscconfig, NULL); char * findeol(char *pos, size_t len) { while (len) { if (*pos == '\r' || *pos == '\n') return pos; pos++; len--; } return NULL; } void hexdump(const uint8_t *ptr, size_t len) { int i; for (i = 0; i < len; i++) usb_printf("%02x", ptr[i]); } void txdone(void) { gottxdone = 1; } void errfunc(void) { goterr = 1; } void rxdone(const uint8_t *payload, size_t size) { if (size > sizeof rxbuf) { size = sizeof rxbuf; rxbuftrunc = 1; } memcpy(rxbuf, payload, size); rxbufsize = size; gotrxdone = 1; } void rxtimeout(void) { usb_printf("rxtimeout\r\n"); } void rxerr(void) { usb_printf("rxerr\r\n"); } static uint8_t hexchartonib(char s) { switch (s) { case '0'...'9': return s - '0'; case 'a'...'f': return s - 'a' + 10; case 'A'...'F': return s - 'A' + 10; default: return -1; } } static bool hexdecode(char *buf, size_t len, uint8_t *out) { uint8_t topchr, botchr; if (len % 2) return false; /* NB: only needed to silence a bad gcc warning */ topchr = -1; while (len) { if (len % 2) { /* bottom nibble */ botchr = hexchartonib(*buf); if (topchr == -1 || botchr == -1) return false; *out = topchr << 4 | botchr; out++; } else { /* top nibble */ topchr = hexchartonib(*buf); } len--; buf++; } return true; } static const char pktstart[] = "pkt:"; static const size_t pktstartlen = sizeof pktstart - 1; static uint8_t pktbuf[128]; static void process_line(char *start, char *end) { size_t len; /* trim off leading CR/NL */ while (start < end && (*start == '\r' || *start == '\n')) start++; len = end - start; if (len >= pktstartlen && memcmp(start, pktstart, sizeof pktstart - 1) == 0) { start += pktstartlen; len -= pktstartlen; if (len % 2) { usb_printf("invalid pkt len\r\n"); return; } if (!hexdecode(start, len, pktbuf)) { usb_printf("invalid pkt\r\n"); return; } rs485_starttx(pktbuf, len / 2); return; } usb_printf("line: %.*s", end - start, start); fflush(vcp_usb); } static void WaitForIRQ() { __disable_irq(); HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); __enable_irq( ); } int main(void) { sysinit_run(); debug_printf("starting...\n"); #if 1 int i; for (i = 0; i < 5; i++) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); HAL_Delay(250); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); HAL_Delay(250); } #endif setlinebuf(vcp_usb); #if 1 wait_for_vcp(); /* * This is required to use w/ FreeBSD. This is an issue w/ the * STM32 Core USB library: * https://github.com/STMicroelectronics/STM32CubeL1/issues/10 */ HAL_Delay(50); usb_printf("starting...\r\n"); #endif char inpbuf[1024]; char *lastcheck; char *endchr; int inpbufpos = 0; int cpylen; rs485_register(txdone, rxdone, errfunc); rs485_startrx(); unsigned lasttick = -1; uint32_t gt; loop: /* * This is disabled as when it's enabled, it causes a hang * when sending USB data. The USB CDC end point never becomes * unbusy to allow the next send to progress. */ #if 0 /* * A ms delay isn't a big deal, so no special locking is * used to make sure we don't got to sleep w/ data pending. */ WaitForIRQ(); #else (void)WaitForIRQ; #endif gt = HAL_GetTick(); if (lasttick != gt / 1000) { lasttick = gt / 1000; //usb_printf("tick: %u\r\n", lasttick); } if (goterr) { //usb_printf("error\r\n"); goterr = 0; dorx = 1; } if (gottxdone) { usb_printf("txdone\r\n"); dorx = 1; gottxdone = 0; } if (gotrxdone) { if (rxbuftrunc) { rxbuftrunc = 0; //usb_printf("rxdone: buffer overflow\r\n"); } else { usb_printf("rxdone: size: %u\r\ndata: ", rxbufsize); hexdump(rxbuf, rxbufsize); usb_printf("\r\n"); } dorx = 1; gotrxdone = 0; } if (dorx) { //usb_printf("dorx\r\n"); rs485_startrx(); //usb_printf("startrx res: %s\r\n", rs485err); dorx = 0; } /* while we have data */ while (CDC_RX_LEN) { /* store last position */ lastcheck = &inpbuf[inpbufpos]; /* calculate how much space left */ cpylen = MIN(sizeof inpbuf - inpbufpos, CDC_RX_LEN); /* copy into buffer */ memcpy(&inpbuf[inpbufpos], CDC_RX_BUFFER, cpylen); /* and point to end of buffer */ inpbufpos += cpylen; do { /* find first end of line characters */ endchr = findeol(lastcheck, cpylen); if (endchr != NULL) { /* if so, process it */ process_line(inpbuf, endchr); /* skip end of line char */ endchr++; /* move remaining buffer to the beginning */ memmove(inpbuf, endchr, inpbufpos - (endchr - inpbuf)); /* and store new length */ inpbufpos = inpbufpos - (endchr - inpbuf); /* mark begining of stream as last checked */ lastcheck = inpbuf; /* and try to process another line */ continue; } else if (inpbufpos == sizeof inpbuf) { /* we overflowed the buffer */ /* XXX - best way is to throw away this line */ inpbufpos = 0; } } while (0); /* if we copied all the data */ if (cpylen == CDC_RX_LEN) { /* declare that we are ready to receive more data */ CDC_RX_LEN = 0; USBD_CDC_ReceivePacket(&hUsbDeviceFS); } else { /* if not, move the remaining to the begining and try again */ memmove(CDC_RX_BUFFER, &CDC_RX_BUFFER[cpylen], CDC_RX_LEN - cpylen); CDC_RX_LEN -= cpylen; } } goto loop; }