/* * Code to TX/RX RS-485 frames. * */ #include #include #include #include #include #include #include #include #include #include const char *rs485err = NULL; static uint8_t rxbuf[128]; static txdone_fn_t txdone; static rxdone_fn_t rxdone; static txdone_fn_t errcb; static int doingrx = 0; static int doingtx = 0; static UART_HandleTypeDef rs485uart; /* * Notes: HAL_UARTEx_RxEventCallback rx evnts * HAL_UART_TxCpltCallback tx_it completed * HAL_UART_RxCpltCallback rx_it completed * HAL_UART_ErrorCallback for errors */ void USART3_IRQHandler(void) { HAL_UART_IRQHandler(&rs485uart); } void rs485_register(txdone_fn_t txf, rxdone_fn_t rxf, txdone_fn_t errf) { txdone = txf; rxdone = rxf; errcb = errf; } static void settx(bool tx) { GPIO_PinState s; if (tx) s = GPIO_PIN_SET; else s = GPIO_PIN_RESET; HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, s); /* XXX - led */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, s); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, s); } void rs485_starttx(uint8_t *pkt, size_t len) { /* XXX - make sure not actively rx'ing */ /* XXX - make sure line is idle */ if (rs485uart.RxState == HAL_UART_STATE_BUSY_RX) { while (rs485uart.RxXferCount != sizeof rxbuf) { } } #if 0 /* XXX - as it's full duplex, this shouldn't be needed */ /* This appears to corrupt state and break things */ if (doingrx) { HAL_UART_AbortReceive_IT(&rs485uart); } #endif settx(true); HAL_UART_Transmit_IT(&rs485uart, pkt, len); } void HAL_UART_ErrorCallback(UART_HandleTypeDef *phuart) { settx(false); doingtx = 0; doingrx = 0; errcb(); } void HAL_UART_TxCpltCallback(UART_HandleTypeDef *phuart) { if (phuart != &rs485uart || txdone == NULL) return; for (int x = 0; x < 1000000; x++); settx(false); doingtx = 0; txdone(); } void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart != &rs485uart || rxdone == NULL) return; doingrx = 0; rxdone(rxbuf, Size); } /* * Called when the buffer has been completed, i.e. it likely * overflowed. */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart != &rs485uart || rxdone == NULL) return; doingrx = 0; rxdone(NULL, 0); } /* USE_HAL_UART_REGISTER_CALLBACKS defaults to 0, so use this */ void HAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_InitStruct; /* UM1850 § 38.1.2 Page 550 */ /* 2.a */ __HAL_RCC_USART3_FORCE_RESET(); __HAL_RCC_USART3_RELEASE_RESET(); __HAL_RCC_USART3_CLK_ENABLE(); /* 2.b */ __HAL_RCC_GPIOB_CLK_ENABLE(); /* TX pin */ GPIO_InitStruct = (GPIO_InitTypeDef){ .Pin = GPIO_PIN_10, .Mode = GPIO_MODE_AF_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_LOW, }; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* RX pin */ GPIO_InitStruct = (GPIO_InitTypeDef){ .Pin = GPIO_PIN_11, .Mode = GPIO_MODE_AF_INPUT, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_LOW, }; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* 2.c */ HAL_NVIC_SetPriority(USART3_IRQn, 1, 0); HAL_NVIC_EnableIRQ(USART3_IRQn); /* 2.d */ /* DMA unused */ } const char * statustostr(HAL_StatusTypeDef r) { switch (r) { case HAL_OK: return "ok"; case HAL_ERROR: return "error"; case HAL_BUSY: return "busy"; default: return "unknown"; } } int rs485_startrx() { HAL_StatusTypeDef r; if (doingtx) { rs485err = "doingtx"; return 0; } doingrx = 1; r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); if (r == HAL_BUSY) { rs485err = "already"; return 1; #if 0 /* This appears to corrupt state and break things */ HAL_UART_AbortReceive_IT(&rs485uart); r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); #endif } rs485err = statustostr(r); return r == HAL_OK; } static void rs485frame_init(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOB_CLK_ENABLE(); /* setup DE/RE */ GPIO_InitStruct = (GPIO_InitTypeDef){ .Pin = GPIO_PIN_0|GPIO_PIN_1, .Mode = GPIO_MODE_OUTPUT_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_LOW, /* 2 MHz */ }; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* switch to RX */ settx(false); /* UM1850 § 38.1.2 Page 550 */ /* 3 */ rs485uart = (UART_HandleTypeDef){ .Init.BaudRate = 230400, .Init.WordLength = UART_WORDLENGTH_8B, .Init.StopBits = UART_STOPBITS_1, .Init.Parity = UART_PARITY_NONE, .Init.HwFlowCtl = UART_HWCONTROL_NONE, .Init.Mode = UART_MODE_TX_RX, .Instance = USART3, }; /* 4 */ HAL_UART_Init(&rs485uart); } SYSINIT_VF(rs485frame_init, SI_SUB_STANDARD, SI_ORDER_FIRST, rs485frame_init);