【先楫HPM6750测评】裸机移植agile_modbus
[复制链接]
本帖最后由 xiashuang 于 2022-7-19 10:51 编辑
Modbus是常用的工业通讯协议,本次在HPM上移植Modbus RTU,主要是熟悉uart和定时器。本来想移植freemodbus,但是freemodbus只支持单从机,前段时间在RT-THREAD社区里看到agile_modbus,看了下例程发现和libmodbus一样比较简单,并且支持多主机多从机,正好想试一下HPM6750的uart和定时器,于是使用uart接收和发送中断以及定时器产生超时中断实现modbus rtu。
移植步骤如下:1.从GitHub上下载agile_modbus文件,在工程中加入相关源文件
2.增加头文件路径
3.协议栈初始化
/*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.串口使用uart13,中断代码如下
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) {
}
}
//定时器重置计数
gptmr_channel_reset_count(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH);
//printf("R");
//启动定时器
if(buff_index == 0)
{
gptmr_start_counter(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH);
uart_rx_outtime = 0;//接收超时状态为0
//printf("S");
}
data_buff[buff_index] = c;
buff_index++;
//接收数据过多,重新接收
if (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.定时器配置
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);
gptmr_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.收到UART第一个接收中断启动定时器,以后每个中断定时器重新计数,超过T3.5后定时器超时更新标志,进行modbus协议栈处理,如果需要发送数据则发送数据
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_timeout);
}
else{
buff_index = 0;
}
}
7.slave_callback不需要做修改,因为我使用逻辑,所以注释掉互斥访问
总结:HPM官方提供UART中断收发代码中中断状态读取在实际使用中不太合理,发送使用阻塞发送若果不使用阻塞发送将会导致程序卡死在接收中断处理里面的while(1),增加中间变量uint8_t uart_irq_state = uart_get_irq_id(TEST_UART1) 使用中间变量判断状态就OK了,怀疑每次读中断状态将把中断寄存器中断清除。agile_modbus比freemodbus移植和使用点单多了,如果有需要使用modbus的建议试试看看。
|