STM32 - MODBUS RTU MASTER 8N1 115200 / DWIN;
Пример рабочий и спокойненько коннектится с DWIN, но тут есть одна подъебка, которая мешает принимать ответы и про которую я помню, но никому не скажу. Наверно, опытный программист и сам поймет, а неопытный пусть пострадает, потому что в таких муках зарождается ум.
#include "main.h" #include <string.h> #include <stdint.h> /* ===================== Конфигурация ===================== */ #define SYSCLK_HZ 8000000UL #define BAUDRATE 115200UL #define TIMEOUT_MS 100U #define IWDG_TIMEOUT_MS 2000U #define FLASH_STATE_ADDR 0x08007C00UL #define FLASH_MAGIC_VALUE 0xDEADBEEFUL /* RS485 direction pin */ #define RS485_CONTROL_PIN GPIO_PIN_1 #define RS485_CONTROL_PORT GPIOF /* DWIN / Modbus */ #define MODBUS_SLAVE_ID 1U #define DWIN_VP_READ 0x1001U #define DWIN_VP_WRITE 0x1500U #define MODBUS_POLL_MS 10U #define READ_QTY 10U /* Светодиоды */ #define LED_COUNT 6 static const uint16_t led_pins[LED_COUNT] = { GPIO_PIN_15, GPIO_PIN_12, GPIO_PIN_11, GPIO_PIN_10, GPIO_PIN_9, GPIO_PIN_8 }; /* ===================== Глобальные переменные ===================== */ volatile uint32_t timer_overflows = 0; volatile uint32_t WatchdogTIMER_1 = 0; volatile uint32_t WatchdogTIMER_2 = 1000; typedef struct { uint8_t led_states[LED_COUNT]; uint32_t reset_count; uint32_t magic; uint32_t checksum; } SystemState; SystemState system_state = {0}; /* ===================== Прототипы ===================== */ void SystemClock_Config(void); void MX_GPIO_Init(void); void UART_Init(void); void RS485_Init(void); void RS485_SetTransmitMode(void); void RS485_SetReceiveMode(void); void Timer_Init(void); void WatchdogTimer_Init(void); void IWDG_Init(void); void Delay_us(uint32_t us); uint32_t millis(void); void CheckResetCause(uint8_t *is_iwdg_reset); void LoadSystemState(void); void SaveSystemState(void); uint32_t CalculateChecksum(SystemState *state); void ApplyLedStates(void); void UART_ClearRx(void); void UART_SendBytes(const uint8_t *data, uint16_t len); uint8_t UART_ReceiveExact(uint8_t *buf, uint16_t len, uint32_t timeout_ms); uint16_t Modbus_CRC16(const uint8_t *data, uint16_t len); uint8_t Modbus_ReadHoldingRegisters(uint8_t slave_id, uint16_t start_addr, uint16_t quantity, uint16_t *dest); uint8_t Modbus_WriteSingleRegister(uint8_t slave_id, uint16_t reg_addr, uint16_t value); void Error_Handler(void); /* ===================== Задержка ===================== */ void Delay_us(uint32_t us) { uint32_t cycles = (SYSCLK_HZ / 1000000UL) * us; while (cycles--) { __NOP(); } } /* ===================== millis ===================== */ uint32_t millis(void) { uint32_t overflows; uint32_t cnt; __disable_irq(); overflows = timer_overflows; cnt = TIM3->CNT; __enable_irq(); return (overflows * 1000UL) + cnt; } /* ===================== RS485 ===================== */ void RS485_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOF_CLK_ENABLE(); GPIO_InitStruct.Pin = RS485_CONTROL_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(RS485_CONTROL_PORT, &GPIO_InitStruct); RS485_SetReceiveMode(); } void RS485_SetTransmitMode(void) { HAL_GPIO_WritePin(RS485_CONTROL_PORT, RS485_CONTROL_PIN, GPIO_PIN_SET); Delay_us(50); } void RS485_SetReceiveMode(void) { HAL_GPIO_WritePin(RS485_CONTROL_PORT, RS485_CONTROL_PIN, GPIO_PIN_RESET); Delay_us(50); } /* ===================== UART ===================== */ void UART_Init(void) { __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /* PA2 = TX, PA3 = RX, AF1 */ GPIOA->MODER &= ~(GPIO_MODER_MODER2_Msk | GPIO_MODER_MODER3_Msk); GPIOA->MODER |= (GPIO_MODER_MODER2_1 | GPIO_MODER_MODER3_1); GPIOA->AFR[0] &= ~((0xFUL << GPIO_AFRL_AFSEL2_Pos) | (0xFUL << GPIO_AFRL_AFSEL3_Pos)); GPIOA->AFR[0] |= ((0x1UL << GPIO_AFRL_AFSEL2_Pos) | (0x1UL << GPIO_AFRL_AFSEL3_Pos)); USART1->CR1 = 0; USART1->CR2 = 0; USART1->CR3 = 0; /* 115200, 8N1 */ USART1->BRR = (SYSCLK_HZ + (BAUDRATE / 2U)) / BAUDRATE; USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; } void UART_ClearRx(void) { while (USART1->ISR & USART_ISR_RXNE) { volatile uint8_t dummy = (uint8_t)USART1->RDR; (void)dummy; } if (USART1->ISR & (USART_ISR_ORE | USART_ISR_PE | USART_ISR_FE | USART_ISR_NE)) { USART1->ICR = USART_ICR_ORECF | USART_ICR_PECF | USART_ICR_FECF | USART_ICR_NCF; } } void UART_SendBytes(const uint8_t *data, uint16_t len) { RS485_SetTransmitMode(); for (uint16_t i = 0; i < len; i++) { while ((USART1->ISR & USART_ISR_TXE) == 0U) { } USART1->TDR = data[i]; } while ((USART1->ISR & USART_ISR_TC) == 0U) { } Delay_us(50); RS485_SetReceiveMode(); } uint8_t UART_ReceiveExact(uint8_t *buf, uint16_t len, uint32_t timeout_ms) { uint32_t start = millis(); uint16_t index = 0; while (index < len) { if (USART1->ISR & USART_ISR_RXNE) { buf[index++] = (uint8_t)USART1->RDR; start = millis(); } if (USART1->ISR & (USART_ISR_ORE | USART_ISR_PE | USART_ISR_FE | USART_ISR_NE)) { USART1->ICR = USART_ICR_ORECF | USART_ICR_PECF | USART_ICR_FECF | USART_ICR_NCF; return 0; } if ((millis() - start) >= timeout_ms) { return 0; } } return 1; } /* ===================== Timer TIM3 для millis ===================== */ void Timer_Init(void) { __HAL_RCC_TIM3_CLK_ENABLE(); TIM3->PSC = (SYSCLK_HZ / 1000UL) - 1UL; TIM3->ARR = 1000UL - 1UL; TIM3->CNT = 0; TIM3->DIER |= TIM_DIER_UIE; NVIC_SetPriority(TIM3_IRQn, 1); NVIC_EnableIRQ(TIM3_IRQn); TIM3->CR1 |= TIM_CR1_CEN; } /* ===================== Timer TIM14 для watchdog ===================== */ void WatchdogTimer_Init(void) { __HAL_RCC_TIM14_CLK_ENABLE(); TIM14->PSC = (SYSCLK_HZ / 1000UL) - 1UL; TIM14->ARR = 1UL; TIM14->CNT = 0; TIM14->DIER |= TIM_DIER_UIE; NVIC_SetPriority(TIM14_IRQn, 2); NVIC_EnableIRQ(TIM14_IRQn); TIM14->CR1 |= TIM_CR1_CEN; } void TIM14_IRQHandler(void) { if (TIM14->SR & TIM_SR_UIF) { TIM14->SR &= ~TIM_SR_UIF; WatchdogTIMER_1++; } } void TIM3_IRQHandler(void) { if (TIM3->SR & TIM_SR_UIF) { TIM3->SR &= ~TIM_SR_UIF; timer_overflows++; } } /* ===================== IWDG ===================== */ void IWDG_Init(void) { __HAL_RCC_LSI_ENABLE(); while ((RCC->CSR & RCC_CSR_LSIRDY) == 0U) { } IWDG->KR = 0x5555; IWDG->PR = 0x04; IWDG->RLR = 2500U; IWDG->KR = 0xAAAA; IWDG->KR = 0xCCCC; } /* ===================== Flash state ===================== */ uint32_t CalculateChecksum(SystemState *state) { uint32_t sum = 0; for (int i = 0; i < LED_COUNT; i++) { sum += state->led_states[i]; } sum += state->reset_count; return sum; } void LoadSystemState(void) { SystemState *flash_state = (SystemState *)FLASH_STATE_ADDR; if ((flash_state->magic == FLASH_MAGIC_VALUE) && (flash_state->checksum == CalculateChecksum(flash_state))) { memcpy(&system_state, flash_state, sizeof(SystemState)); } else { memset(system_state.led_states, 0, sizeof(system_state.led_states)); system_state.reset_count = 0; system_state.magic = FLASH_MAGIC_VALUE; system_state.checksum = CalculateChecksum(&system_state); SaveSystemState(); } } void SaveSystemState(void) { HAL_StatusTypeDef status; FLASH_EraseInitTypeDef erase_init = {0}; uint32_t page_error = 0; uint32_t *state_ptr = (uint32_t *)&system_state; system_state.checksum = CalculateChecksum(&system_state); system_state.magic = FLASH_MAGIC_VALUE; HAL_FLASH_Unlock(); erase_init.TypeErase = FLASH_TYPEERASE_PAGES; erase_init.PageAddress = FLASH_STATE_ADDR; erase_init.NbPages = 1; status = HAL_FLASHEx_Erase(&erase_init, &page_error); if (status != HAL_OK) { HAL_FLASH_Lock(); return; } for (uint32_t i = 0; i < (sizeof(SystemState) / 4U); i++) { status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_STATE_ADDR + (i * 4U), state_ptr[i]); if (status != HAL_OK) { break; } } HAL_FLASH_Lock(); } /* ===================== LED ===================== */ void ApplyLedStates(void) { for (int i = 0; i < LED_COUNT; i++) { HAL_GPIO_WritePin(GPIOA, led_pins[i], system_state.led_states[i] ? GPIO_PIN_SET : GPIO_PIN_RESET); } } /* ===================== Reset cause ===================== */ void CheckResetCause(uint8_t *is_iwdg_reset) { uint32_t reset_flags = RCC->CSR; *is_iwdg_reset = (reset_flags & RCC_CSR_IWDGRSTF) ? 1U : 0U; RCC->CSR |= RCC_CSR_RMVF; } /* ===================== GPIO ===================== */ void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_15, GPIO_PIN_RESET); } /* ===================== Clock ===================== */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /* ===================== Modbus CRC ===================== */ uint16_t Modbus_CRC16(const uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFFU; for (uint16_t pos = 0; pos < len; pos++) { crc ^= (uint16_t)data[pos]; for (uint8_t i = 0; i < 8; i++) { if (crc & 0x0001U) { crc >>= 1; crc ^= 0xA001U; } else { crc >>= 1; } } } return crc; } /* ===================== Modbus RTU Master ===================== */ uint8_t Modbus_ReadHoldingRegisters(uint8_t slave_id, uint16_t start_addr, uint16_t quantity, uint16_t *dest) { uint8_t tx[8]; uint8_t rx[64]; uint16_t crc; uint16_t expected_len; if ((quantity == 0U) || (quantity > 29U) || (dest == NULL)) { return 0; } tx[0] = slave_id; tx[1] = 0x03; tx[2] = (uint8_t)(start_addr >> 8); tx[3] = (uint8_t)(start_addr & 0xFF); tx[4] = (uint8_t)(quantity >> 8); tx[5] = (uint8_t)(quantity & 0xFF); crc = Modbus_CRC16(tx, 6); tx[6] = (uint8_t)(crc & 0xFF); tx[7] = (uint8_t)((crc >> 8) & 0xFF); UART_ClearRx(); UART_SendBytes(tx, 8); expected_len = (uint16_t)(5U + (2U * quantity)); if (!UART_ReceiveExact(rx, expected_len, TIMEOUT_MS)) { return 0; } if (rx[0] != slave_id) return 0; if (rx[1] & 0x80U) return 0; if (rx[1] != 0x03U) return 0; if (rx[2] != (uint8_t)(quantity * 2U)) return 0; crc = Modbus_CRC16(rx, expected_len - 2U); if ((rx[expected_len - 2U] != (uint8_t)(crc & 0xFF)) || (rx[expected_len - 1U] != (uint8_t)((crc >> 8) & 0xFF))) { return 0; } for (uint16_t i = 0; i < quantity; i++) { dest[i] = ((uint16_t)rx[3U + (2U * i)] << 8) | (uint16_t)rx[4U + (2U * i)]; } return 1; } uint8_t Modbus_WriteSingleRegister(uint8_t slave_id, uint16_t reg_addr, uint16_t value) { uint8_t tx[8]; uint8_t rx[8]; uint16_t crc; tx[0] = slave_id; tx[1] = 0x06; tx[2] = (uint8_t)(reg_addr >> 8); tx[3] = (uint8_t)(reg_addr & 0xFF); tx[4] = (uint8_t)(value >> 8); tx[5] = (uint8_t)(value & 0xFF); crc = Modbus_CRC16(tx, 6); tx[6] = (uint8_t)(crc & 0xFF); tx[7] = (uint8_t)((crc >> 8) & 0xFF); UART_ClearRx(); UART_SendBytes(tx, 8); if (!UART_ReceiveExact(rx, 8, TIMEOUT_MS)) { return 0; } if (memcmp(tx, rx, 8) != 0) { return 0; } return 1; } /* ===================== main ===================== */ int main(void) { uint8_t is_iwdg_reset = 0; uint32_t last_poll_time = 0; uint16_t src_value = 0; uint8_t heartbeat = 0; HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); RS485_Init(); UART_Init(); Timer_Init(); WatchdogTimer_Init(); IWDG_Init(); CheckResetCause(&is_iwdg_reset); LoadSystemState(); system_state.reset_count++; if (!is_iwdg_reset) { memset(system_state.led_states, 0, sizeof(system_state.led_states)); } ApplyLedStates(); SaveSystemState(); while (1) { if (WatchdogTIMER_1 >= WatchdogTIMER_2) { IWDG->KR = 0xAAAA; WatchdogTIMER_1 = 0; } if ((millis() - last_poll_time) >= MODBUS_POLL_MS) { last_poll_time = millis(); if (Modbus_ReadHoldingRegisters(MODBUS_SLAVE_ID, DWIN_VP_READ, 1, &src_value)) { if (Modbus_WriteSingleRegister(MODBUS_SLAVE_ID, DWIN_VP_WRITE, src_value)) { heartbeat ^= 1U; system_state.led_states[0] = heartbeat; system_state.led_states[1] = 1; system_state.led_states[2] = 0; } else { system_state.led_states[1] = 0; system_state.led_states[2] = 1; } } else { system_state.led_states[1] = 0; system_state.led_states[2] = 1; } ApplyLedStates(); } } } /* ===================== Error ===================== */ void Error_Handler(void) { __disable_irq(); while (1) { } } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { (void)file; (void)line; } #endif
