[Xianji HPM6750 Review] Bare Metal Transplantation agile_modbus
[Copy link]
This post was last edited by xiashuang on 2022-7-19 10:51
Modbus is a commonly used industrial communication protocol. This time, I ported Modbus RTU on HPM, mainly to get familiar with UART and timer. I originally wanted to port freemodbus, but freemodbus only supports single slave. Some time ago, I saw agile_modbus in the RT-THREAD community. After reading the examples, I found that it is as simple as libmodbus and supports multiple hosts and multiple slaves. I just wanted to try the UART and timer of HPM6750, so I used UART receiving and sending interrupts and timer timeout interrupts to implement modbus rtu.
The migration steps are as follows: 1. Download the agile_modbus file from GitHub and add the relevant source files to the project
2. Add header file path
3. Protocol stack initialization
/*init modbus stack*/
uint8_t ctx_send_buf[AGILE_MODBUS_MAX_ADU_LENGTH];
uint8_t ctx_read_buf[AGILE_MODBUS_MAX_ADU_LENGTH];
agile_modbus_rtu_t ctx_rtu;
agile_modbus_t *ctx = &ctx_rtu._ctx;
agile_modbus_rtu_init(&ctx_rtu, ctx_send_buf, sizeof(ctx_send_buf), ctx_read_buf, sizeof(ctx_read_buf));
agile_modbus_set_slave(ctx, 1);
4. The serial port uses uart13, and the interrupt code is as follows
void uart_isr(void)
{
uint8_t c;
uint8_t uart_irq_state = uart_get_irq_id(TEST_UART1);
if (uart_irq_state & uart_intr_id_rx_data_avail)
{
if (status_success != uart_receive_byte(TEST_UART1, &c)) {
while (1) {
}
}
//Timer reset count
gptmr_channel_reset_count(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH);
//printf("R");
//Start timerif
(buff_index == 0)
{
gptmr_start_counter(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH);
uart_rx_outtime = 0;//Receive timeout status is 0
//printf("S");
}
data_buff[buff_index] = c;
buff_index++;
//Too much received data, receive againif
(buff_index >= 260) {
//uart_disable_irq(TEST_UART1, uart_intr_rx_data_avail_or_timeout);
//uart_enable_irq(TEST_UART1, uart_intr_tx_slot_avail);
buff_index = 0;
}
}
if (uart_irq_state & uart_intr_id_tx_slot_avail)
{
if (status_success != uart_send_byte(TEST_UART1, data_buff[data_count])) {
while (1) {
}
}
data_count ++;
if(data_count >= buff_index){
buff_index = 0;
data_count = 0;
uart_rx_outtime = 0;
uart_flush(TEST_UART1);
uart_disable_irq(TEST_UART1, uart_intr_tx_slot_avail);
uart_enable_irq(TEST_UART1, uart_intr_rx_data_avail_or_timeout);
}
}
}
SDK_DECLARE_EXT_ISR_M(TEST_UART_IRQ1, uart_isr)
5. Timer configuration
void modbus_timer_create(uint32_t ms, modbus_timer_cb cb)
{
uint32_t gptmr_freq;
gptmr_channel_config_t config;
gptmr_channel_get_default_config(MODBUS_CALLBACK_TIMER, &config);
clock_add_to_group(MODBUS_CALLBACK_TIMER_CLK_NAME, 0); freq
= clock_get_frequency(MODBUS_CALLBACK_TIMER_CLK_NAME);
config.reload = gptmr_freq / 1000 * ms;
gptmr_channel_config(MODBUS_CALLBACK_TIMER , MODBUS_CALLBACK_TIMER_CH, &config, false);
gptmr_enable_irq(MODBUS_CALLBACK_TIMER, GPTMR_CH_RLD_IRQ_MASK(MODBUS_CALLBACK_TIMER_CH));
intc_m_enable_irq_with_priority(MODBUS_CALLBACK_TIMER_IRQ, 1);
//gptmr_start_counter(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH);
}
6. The timer is started when the first receive interrupt of UART is received. Each subsequent interrupt timer is reset. After exceeding T3.5, the timer timeout flag is updated and the modbus protocol stack is processed. If data needs to be sent, send the data
if(uart_rx_outtime == 1)
{
//printf("t");
uart_rx_outtime = 0;
memcpy(ctx_read_buf,data_buff,buff_index);
int send_len = agile_modbus_slave_handle(ctx, buff_index, 0, slave_callback, NULL);
if (send_len > 0)
{
data_count = 0;
buff_index = send_len;
memcpy(data_buff,ctx_send_buf,buff_index);
uart_flush(TEST_UART1);
uart_enable_irq(TEST_UART1, uart_intr_tx_slot_avail);
uart_disable_irq(TEST_UART1, uart_intr_rx_data_avail_or_time out);
}
else{
buff_index = 0;
}
}
7.slave_callback does not need to be modified, because I use logic, so comment out the exclusive access
Summary: The interrupt status reading in the UART interrupt receiving and sending code provided by HPM is not reasonable in actual use. If blocking is not used, the program will be stuck in the while (1) in the receiving interrupt processing. Adding an intermediate variable uint8_t uart_irq_state = uart_get_irq_id(TEST_UART1) and using the intermediate variable to judge the status is OK. It is suspected that each reading of the interrupt status will clear the interrupt register interrupt. agile_modbus has more porting and usage orders than freemodbus. If you need to use modbus, try it.
|