AN0211 应用笔记 |
Modbus TCP throught DM9051 |
前言
Modbus是一种串行通讯协议,在工业领域为事实上的业界标准,是工业电子装置之间常用的连接方式;本应用笔记将介绍Modbus TCP, 透过TCP/IP来实现Modbus协议。
支持型号列表:
支持型号 | AT32F423xx |
目录
表目录
图目录
图1. Modbus network topology. 5
图2. Modbus TCP packet format 6
图3. AT-START-F423开发板及DM9051, 电机和温度传感器... 7
图4. Modbus Poll Connection Page. 13
- Modbus概述
关于Modbus详细的协议内容,请参阅Modbus组织的参考指南,这篇应用笔记只会提及关键的网络拓朴与封包格式。
Modbus协议是一个master/slave架构的协议,有一个节点是master节点,其他使用Modbus协议参与通讯的节点是slave节点。本应用笔记提供的sample code是作为slave参与Modbus协议通讯。
图1. Modbus network topology
在传输过程中,client与server的讯息至少会有Function Code与Data两部份:
- Function Code: 代表要执行的动作代码,例如读取或写入。
- Data: 代表动作执行代码的相关参数,例如读取某个地址上的数据,或是回传某个地址上的数据作为结果。
Function Code和Data是整个Modbus沟通最基本的单元,也称作Protocol Data Unit(PDU)。
除此之外,根据传输方式不同可能还会在头尾加上一些附加信息,附加后的整个讯息称为Application Data Unit(ADU)。
图2. Modbus TCP packet format
Function Code的详细定义,可以参考Modbus的参考指南。
透过DM9051参与Modbus TCP协议,提供以下功能:
- LED开关
- 读取温度传感器数据
- 控制步进电机的正反转
- 硬件环境:
对应产品型号的AT-START BOARD
图3. AT-START-F423开发板及DM9051, 电机和温度传感器
- 软件环境
Source Code\utilities\demo\mdk_v5
- 上位机软件: Modbus Poll
- 配置流程
- 初始化DM9051,DM9051对应的脚位请参照表1
- 初始化LwIP
- 启动Modbus Task
- 配置ADC读取温度传感器数值
- 配置GPIO控制电机
表1. DM9051脚位对照
- 代码介绍
- main函数代码描述
int main(void) { error_status status; system_clock_config(); at32_board_init(); uart_print_init(115200); status = env_dm9051f_system_init(); while(status == ERROR); tcpip_stack_init(); modbus_task(); #ifdef en_temperature adc_pin_config(); adc_config(); adc_ordinary_software_trigger_enable(ADC1, TRUE); #endif #ifdef en_motor motor_pin_init(); motor_init(); #endif while(1) { /* lwip receive handle */ lwip_rx_loop_handler(); /*timeout handle*/ lwip_periodic_handle(local_time); eMBPoll(); #ifdef en_motor motor_run(Motor_Postion,motor_en); #endif } } |
- DM9051初始化函数代码描述
error_status env_dm9051f_system_init(void) { printf("dm9051_pins_configuration\r\n"); dm9051_pins_configuration(); dm9051_HW_reset(); printf("dm9051_(%x %x %x %x)\r\n",DM9051_Read_Reg(0x28),DM9051_Read_Reg(0x29),DM9051_Read_Reg(0x2A),DM9051_Read_Reg(0x2B)); dm9051_tmr3_init(); printf("dm9051_tmr3_init end\r\n"); return SUCCESS; } |
- Modbus Task初始化函数代码描述
void modbus_task(void) { eMBErrorCode eStatus;
/* ucPort: select port_uart. * this parameter can be one of the following values: * 0: USART2: tx--PA2, rx--PA3, de--PA1; * 1: USART3: tx--PB10, rx--PB11, de--PB14; * other: invalid. */ eStatus = eMBInit(MB_TCP, MB_SLAVE_ADDRESS, 0, MB_BAUDRATE, MB_PAR_NONE); if(MB_ENOERR == eStatus) { printf("modbus init ok\r\n"); eStatus = eMBEnable(); if(MB_ENOERR == eStatus) { printf("modbus enable ok\r\n"); } else { printf("modbus enable fail, error code: %u\r\n", eStatus); } } else { printf("modbus init fail, error code: %u\r\n", eStatus); }
if(MB_ENOERR != eStatus) { printf("exit modbus task.\r\n"); return; } } |
- ADC初始化函数代码描述
void adc_config(void) { adc_common_config_type adc_common_struct; adc_base_config_type adc_base_struct; crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE); nvic_irq_enable(ADC1_IRQn, 0, 0); crm_adc_clock_select(CRM_ADC_CLOCK_SOURCE_HCLK); adc_common_default_para_init(&adc_common_struct); /* config division,adcclk is division by hclk */ adc_common_struct.div = ADC_HCLK_DIV_4; /* config inner temperature sensor and vintrv */ adc_common_struct.tempervintrv_state = FALSE; adc_common_config(&adc_common_struct); adc_base_default_para_init(&adc_base_struct); adc_base_struct.sequence_mode = TRUE; //adc_base_struct.repeat_mode = FALSE; adc_base_struct.repeat_mode = TRUE; adc_base_struct.data_align = ADC_RIGHT_ALIGNMENT; adc_base_struct.ordinary_channel_length = 1; adc_base_config(ADC1, &adc_base_struct); adc_resolution_set(ADC1, ADC_RESOLUTION_12B); /* config ordinary channel */ adc_ordinary_channel_set(ADC1, ADC_CHANNEL_1, 1, ADC_SAMPLETIME_92_5); /* config ordinary trigger source and trigger edge */ adc_ordinary_conversion_trigger_set(ADC1, ADC_ORDINARY_TRIG_TMR1CH1, ADC_ORDINARY_TRIG_EDGE_NONE); /* config dma mode,it's not useful when common dma mode is use */ adc_dma_mode_enable(ADC1, TRUE); /* config dma request repeat,it's not useful when common dma mode is use */ adc_dma_request_repeat_enable(ADC1, FALSE); /* enable adc overflow interrupt */ adc_interrupt_enable(ADC1, ADC_OCCO_INT, TRUE); /* adc enable */ adc_enable(ADC1, TRUE); while(adc_flag_get(ADC1, ADC_RDY_FLAG) == RESET); /* adc calibration */ adc_calibration_init(ADC1); while(adc_calibration_init_status_get(ADC1)); adc_calibration_start(ADC1); while(adc_calibration_status_get(ADC1)); } |
- 电机初始化函数代码描述
void motor_init(void) { printf("motor_init\r\n"); gpio_bits_write(IO0,FALSE); gpio_bits_write(IO1,FALSE); gpio_bits_write(IO2,FALSE); gpio_bits_write(IO3,FALSE); motor_en=0; } |
- 开启Modbus Poll当作Modbus Master并联机,请参照图3
- 控制LED的方法为透过Modbus Poll上的Write Single Coil(0x05)设定开关, 请参照图4
- 读取温度传感器的数值方法为读取holding register, 请参照图5
- 控制Motor的方式为:
- 先设定holding register上Address 5为正转(1)或反转(2)
- 再设定holding register上Address 6决定要走几步(4096步为一圈)
图4. Modbus Poll Connection Page
图5. LED Control
图6. 读取温度传感器
图7. 制步进电机