chore: rewrite to modbus rtu
This commit is contained in:
182
ch32ext.c
182
ch32ext.c
@@ -1,157 +1,81 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ch32v003fun.h"
|
||||
#include "modbus.h"
|
||||
#include "rs485.h"
|
||||
#include "shift_reg.h"
|
||||
#include "simple_eeprom.h"
|
||||
#include "state_manager.h"
|
||||
#include "systick.h"
|
||||
|
||||
#define RS485_DIR (1 << 0) // RS485 direction control
|
||||
#define SRCLR (1 << 1) // Shift register clear (active low)
|
||||
#define SRCLK (1 << 2) // Shift register clock
|
||||
#define RCLK (1 << 3) // Storage register clock
|
||||
#define SER (1 << 4) // Serial data input
|
||||
#define SLAVE_ADDRESS 0x01
|
||||
#define MAX_MODBUS_FRAME 16
|
||||
#define NUM_REGISTERS 1
|
||||
#define FRAME_TIMEOUT_MS 4 // 3.5 char times at 9600 baud (~1.042ms per char)
|
||||
|
||||
// "protocol" defines
|
||||
#define BOARD_ADDRESS 0x01
|
||||
#define CMD_SET_OUTPUTS 0x01
|
||||
#define BROADCAST_ADDR 0xFF
|
||||
static uint16_t holding_registers[NUM_REGISTERS];
|
||||
|
||||
// "protocol" states
|
||||
enum rx_states {
|
||||
STATE_ADDR,
|
||||
STATE_IGNORE,
|
||||
STATE_CMD,
|
||||
STATE_DATA_HIGH,
|
||||
STATE_DATA_LOW
|
||||
};
|
||||
static void handle_modbus_frame(uint8_t* rx_buffer, uint16_t rx_count) {
|
||||
uint8_t response[MAX_MODBUS_FRAME];
|
||||
uint16_t response_len;
|
||||
uint8_t result;
|
||||
|
||||
void setup_uart(int uartBRR) {
|
||||
RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC |
|
||||
RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO;
|
||||
result = modbus_process_message(rx_buffer, rx_count, SLAVE_ADDRESS,
|
||||
holding_registers, NUM_REGISTERS);
|
||||
|
||||
// RS485_DIR as output, set recv mode
|
||||
GPIOC->CFGLR &= ~(0xf << (4 * 0));
|
||||
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 0);
|
||||
GPIOC->BCR = RS485_DIR;
|
||||
if (result == MODBUS_ERROR_NONE) {
|
||||
shift_reg_write(holding_registers[0]);
|
||||
save_state(holding_registers[0]);
|
||||
|
||||
// UART pins (PD5=TX, PD6=RX)
|
||||
GPIOD->CFGLR &= ~(0xf << (4 * 5) | 0xf << (4 * 6));
|
||||
GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 5);
|
||||
GPIOD->CFGLR |= GPIO_CNF_IN_FLOATING << (4 * 6);
|
||||
|
||||
// 115200 8N1
|
||||
USART1->CTLR1 =
|
||||
USART_WordLength_8b | USART_Parity_No | USART_Mode_Tx | USART_Mode_Rx;
|
||||
USART1->CTLR2 = USART_StopBits_1;
|
||||
USART1->CTLR3 = USART_HardwareFlowControl_None;
|
||||
USART1->BRR = uartBRR;
|
||||
USART1->CTLR1 |= CTLR1_UE_Set;
|
||||
}
|
||||
|
||||
void setup_gpio(void) {
|
||||
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;
|
||||
|
||||
// PC1-PC4 as out
|
||||
for (int pin = 1; pin <= 4; pin++) {
|
||||
GPIOC->CFGLR &= ~(0xf << (4 * pin));
|
||||
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * pin);
|
||||
response_len = modbus_create_response(response, SLAVE_ADDRESS,
|
||||
MODBUS_FC_WRITE_SINGLE_REGISTER, 0,
|
||||
holding_registers[0]);
|
||||
} else {
|
||||
response_len = modbus_create_error_response(
|
||||
response, SLAVE_ADDRESS, MODBUS_FC_WRITE_SINGLE_REGISTER, result);
|
||||
}
|
||||
|
||||
GPIOC->BCR = SRCLR | SRCLK | RCLK | SER;
|
||||
}
|
||||
|
||||
void shift_out(uint16_t data) {
|
||||
GPIOC->BCR = RCLK;
|
||||
|
||||
// reorder
|
||||
uint16_t ordered_data = ((data & 0xFF00) >> 8) | // 1st reg (Q0-Q7)
|
||||
((data & 0x00FF) << 8); // 2nd reg (Q8-Q15)
|
||||
|
||||
// shift out 16 bits, MSB 1st
|
||||
for (int8_t i = 15; i >= 0; i--) {
|
||||
GPIOC->BCR = SRCLK;
|
||||
if (ordered_data & (1 << i)) {
|
||||
GPIOC->BSHR = SER;
|
||||
} else {
|
||||
GPIOC->BCR = SER;
|
||||
}
|
||||
GPIOC->BSHR = SRCLK;
|
||||
}
|
||||
|
||||
// latch
|
||||
GPIOC->BSHR = RCLK;
|
||||
GPIOC->BCR = RCLK;
|
||||
}
|
||||
|
||||
uint8_t uart_receive_byte(void) {
|
||||
while (!(USART1->STATR & USART_FLAG_RXNE));
|
||||
return USART1->DATAR & 0xFF;
|
||||
rs485_send(response, response_len);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
uint8_t rx_buffer[MAX_MODBUS_FRAME];
|
||||
uint16_t rx_count = 0;
|
||||
uint32_t last_byte_time = 0;
|
||||
|
||||
SystemInit();
|
||||
setup_gpio();
|
||||
setup_uart(UART_BRR);
|
||||
systick_init();
|
||||
shift_reg_init();
|
||||
rs485_init(UART_BRR);
|
||||
|
||||
// reset shift reg
|
||||
GPIOC->BSHR = SRCLR;
|
||||
GPIOC->BSHR = RCLK; // rising edge on RCLK
|
||||
GPIOC->BCR = RCLK;
|
||||
|
||||
// load last state
|
||||
uint16_t current_state = load_state();
|
||||
printf("Loaded state: %04x\n", current_state);
|
||||
shift_out(current_state);
|
||||
|
||||
uint8_t rx_state = STATE_ADDR;
|
||||
uint8_t addr, cmd;
|
||||
uint16_t data = 0;
|
||||
uint8_t bytes_to_ignore = 0;
|
||||
|
||||
printf("SystemClk:%d\r\n", FUNCONF_SYSTEM_CORE_CLOCK);
|
||||
printf("ChipID:%08lx\r\n", *(uint32_t *)0x1FFFF7C4);
|
||||
// restore last saved state
|
||||
holding_registers[0] = load_state();
|
||||
shift_reg_write(holding_registers[0]);
|
||||
|
||||
while (1) {
|
||||
uint8_t byte = uart_receive_byte();
|
||||
if (!rs485_available()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (rx_state) {
|
||||
case STATE_ADDR:
|
||||
addr = byte;
|
||||
if (addr == BOARD_ADDRESS || addr == BROADCAST_ADDR) {
|
||||
rx_state = STATE_CMD;
|
||||
} else {
|
||||
// crc??))
|
||||
bytes_to_ignore = 3; // skip cmd + data_high + data_low
|
||||
rx_state = STATE_IGNORE;
|
||||
}
|
||||
break;
|
||||
uint32_t current_time = millis();
|
||||
|
||||
case STATE_IGNORE:
|
||||
bytes_to_ignore--;
|
||||
if (bytes_to_ignore == 0) {
|
||||
rx_state = STATE_ADDR;
|
||||
}
|
||||
break;
|
||||
// check for frame timeout
|
||||
if (rx_count > 0 && (current_time - last_byte_time) > FRAME_TIMEOUT_MS) {
|
||||
rx_count = 0;
|
||||
}
|
||||
|
||||
case STATE_CMD:
|
||||
cmd = byte;
|
||||
rx_state = STATE_DATA_HIGH;
|
||||
break;
|
||||
rx_buffer[rx_count++] = rs485_read();
|
||||
last_byte_time = current_time;
|
||||
|
||||
case STATE_DATA_HIGH:
|
||||
data = (uint16_t)byte << 8;
|
||||
rx_state = STATE_DATA_LOW;
|
||||
break;
|
||||
// process complete frame
|
||||
if (rx_count >= 8) {
|
||||
handle_modbus_frame(rx_buffer, rx_count);
|
||||
rx_count = 0;
|
||||
}
|
||||
|
||||
case STATE_DATA_LOW:
|
||||
data |= byte;
|
||||
if (cmd == CMD_SET_OUTPUTS) {
|
||||
printf("Set outputs: 0x%04X\n", data);
|
||||
shift_out(data);
|
||||
save_state(data);
|
||||
dump_eeprom();
|
||||
}
|
||||
rx_state = STATE_ADDR;
|
||||
break;
|
||||
// buffer overflow protection
|
||||
if (rx_count >= MAX_MODBUS_FRAME) {
|
||||
rx_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user