xiashuang 发表于 2022-7-19 10:51

【先楫HPM6750测评】裸机移植agile_modbus

本帖最后由 xiashuang 于 2022-7-19 10:51 编辑

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size:18px;"><span style="font-family:楷体;">&nbsp;Modbus是常用的工业通讯协议,本次在HPM上移植Modbus RTU,主要是熟悉uart和定时器。本来想移植freemodbus,但是freemodbus只支持单从机,前段时间在RT-THREAD社区里看到agile_modbus,看了下例程发现和libmodbus一样比较简单,并且支持多主机多从机,正好想试一下HPM6750的uart和定时器,于是使用uart接收和发送中断以及定时器产生超时中断实现modbus rtu。</span></span></p>

<p><span style="font-size:18px;"><span style="font-family:楷体;">&nbsp; &nbsp; 移植步骤如下:1.从GitHub上下载agile_modbus文件,在工程中加入相关源文件</span></span></p>

<p></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; <span style="font-size:18px;"><span style="font-family:楷体;">2.增加头文件路径</span></span></p>

<p></p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="font-family:楷体;"><span style="font-size:18px;">3.协议栈初始化</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">/*init modbus stack*/<br />
&nbsp; &nbsp; uint8_t ctx_send_buf;<br />
&nbsp; &nbsp; uint8_t ctx_read_buf;</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; agile_modbus_rtu_t ctx_rtu;<br />
&nbsp; &nbsp; agile_modbus_t *ctx = &amp;ctx_rtu._ctx;<br />
&nbsp; &nbsp; agile_modbus_rtu_init(&amp;ctx_rtu, ctx_send_buf, sizeof(ctx_send_buf), ctx_read_buf, sizeof(ctx_read_buf));<br />
&nbsp; &nbsp; agile_modbus_set_slave(ctx, 1);</span></span></p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family:楷体;"><span style="font-size:18px;">4.串口使用uart13,中断代码如下</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">void uart_isr(void)<br />
{<br />
&nbsp; &nbsp; uint8_t c;<br />
&nbsp; &nbsp; uint8_t uart_irq_state = uart_get_irq_id(TEST_UART1);<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (uart_irq_state &amp; uart_intr_id_rx_data_avail)&nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (status_success != uart_receive_byte(TEST_UART1, &amp;c)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while (1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //定时器重置计数<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gptmr_channel_reset_count(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //printf(&quot;R&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //启动定时器<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(buff_index == 0)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gptmr_start_counter(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_rx_outtime = 0;//接收超时状态为0<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //printf(&quot;S&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data_buff = c;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buff_index++;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //接收数据过多,重新接收<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (buff_index &gt;= 260) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //uart_disable_irq(TEST_UART1, uart_intr_rx_data_avail_or_timeout);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //uart_enable_irq(TEST_UART1, uart_intr_tx_slot_avail);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buff_index = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; &nbsp; &nbsp; }</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; &nbsp; &nbsp; if (uart_irq_state &amp; uart_intr_id_tx_slot_avail)&nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (status_success != uart_send_byte(TEST_UART1, data_buff)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while (1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data_count ++;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(data_count &gt;= buff_index){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buff_index = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data_count = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_rx_outtime = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_flush(TEST_UART1);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_disable_irq(TEST_UART1, uart_intr_tx_slot_avail);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_enable_irq(TEST_UART1, uart_intr_rx_data_avail_or_timeout);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
}</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">SDK_DECLARE_EXT_ISR_M(TEST_UART_IRQ1, uart_isr)</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; &nbsp; &nbsp; 5.定时器配置</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">void modbus_timer_create(uint32_t ms, modbus_timer_cb cb)<br />
{<br />
&nbsp; &nbsp; uint32_t gptmr_freq;<br />
&nbsp; &nbsp; gptmr_channel_config_t config;<br />
&nbsp; &nbsp; gptmr_channel_get_default_config(MODBUS_CALLBACK_TIMER, &amp;config);<br />
&nbsp; &nbsp; clock_add_to_group(MODBUS_CALLBACK_TIMER_CLK_NAME, 0);<br />
&nbsp; &nbsp; gptmr_freq = clock_get_frequency(MODBUS_CALLBACK_TIMER_CLK_NAME);<br />
&nbsp; &nbsp; config.reload = gptmr_freq / 1000 * ms;<br />
&nbsp; &nbsp; gptmr_channel_config(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH, &amp;config, false);<br />
&nbsp; &nbsp; gptmr_enable_irq(MODBUS_CALLBACK_TIMER, GPTMR_CH_RLD_IRQ_MASK(MODBUS_CALLBACK_TIMER_CH));<br />
&nbsp; &nbsp; intc_m_enable_irq_with_priority(MODBUS_CALLBACK_TIMER_IRQ, 1);</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; //gptmr_start_counter(MODBUS_CALLBACK_TIMER, MODBUS_CALLBACK_TIMER_CH);<br />
}</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; &nbsp; &nbsp; 6.收到UART第一个接收中断启动定时器,以后每个中断定时器重新计数,超过T3.5后定时器超时更新标志,进行modbus协议栈处理,如果需要发送数据则发送数据</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">if(uart_rx_outtime == 1)<br />
&nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //printf(&quot;t&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_rx_outtime = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memcpy(ctx_read_buf,data_buff,buff_index);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int send_len = agile_modbus_slave_handle(ctx, buff_index, 0, slave_callback, NULL);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (send_len &gt; 0)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data_count = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buff_index = send_len;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memcpy(data_buff,ctx_send_buf,buff_index);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_flush(TEST_UART1);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_enable_irq(TEST_UART1, uart_intr_tx_slot_avail);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uart_disable_irq(TEST_UART1, uart_intr_rx_data_avail_or_timeout);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buff_index = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }</span></span></p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; &nbsp; &nbsp; 7.slave_callback不需要做修改,因为我使用逻辑,所以注释掉互斥访问</span></span></p>

<p>&nbsp;</p>

<p><span style="font-family:楷体;"><span style="font-size:18px;">&nbsp; &nbsp; &nbsp; &nbsp; 总结:HPM官方提供UART中断收发代码中中断状态读取在实际使用中不太合理,发送使用阻塞发送若果不使用阻塞发送将会导致程序卡死在接收中断处理里面的while(1),增加中间变量uint8_t uart_irq_state = uart_get_irq_id(TEST_UART1) 使用中间变量判断状态就OK了,怀疑每次读中断状态将把中断寄存器中断清除。agile_modbus比freemodbus移植和使用点单多了,如果有需要使用modbus的建议试试看看。</span></span></p>

freebsder 发表于 2022-7-20 19:13

<p>看了下这个agile_modbus,谢谢分享,有机会试试</p>

eew_bFe6FH 发表于 2022-7-21 14:31

谢谢非常详尽的测评分享哈!针对最后总结的问题,uart_isr这个是example里面的示例,应用如果有不同需求的时候,可以按照具体的需求编写,谢谢!!
页: [1]
查看完整版本: 【先楫HPM6750测评】裸机移植agile_modbus