正点原子学习小组
直播中

张兴照

11年用户 93经验值
擅长:可编程逻辑 电源/新能源 模拟技术 测量仪表 嵌入式技术 存储技术 接口/总线/驱动 控制/MCU
私信 关注
[经验]

【正点原子STM32战舰V4开发板体验】MODBUS测试

MODBUS是比较常见的工业设备通信协议,战舰板引出了多路串口,今天测试一下多路MODBUS应用。
测试程序使用调试串口和RS485两路,和单片机串口的对应关系如下:
UART1-调试串口
UART2-RS485
移植MODBUS协议,主要是串口的移植,定时器使用系统1ms心跳计时,调用modbus API就可以了。
硬件上RS485串口默认没有连接,需要手动换一下跳线帽位置。

串口移植的关键是初始化和中断处理:
测试程序采用RTU模式,初始化如下:

static void qc_port_Init(uu8 qcMode, uint32_t ulBaudRate, mb_parity_type eParity)
{
	LL_USART_InitTypeDef cfg;

	qc_port_hal_cfg();

	qc_cb_set();
	qc_obj.mode = qcMode;
	qc_obj.dat_send = qc_port_send;

	qc_byte_send = qc_rtu_byte_send;

	cfg.BaudRate = ulBaudRate;
	cfg.TransferDirection = LL_USART_DIRECTION_TX_RX;
	cfg.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
	cfg.OverSampling = LL_USART_OVERSAMPLING_16;

	// rtu mode
	if (MB_PAR_NONE == eParity)
	{
		cfg.Parity = LL_USART_PARITY_NONE;
		cfg.StopBits = LL_USART_STOPBITS_2;
		cfg.DataWidth = LL_USART_DATAWIDTH_8B;
	}
	else if (MB_PAR_NONE_1S == eParity)
	{
		cfg.Parity = LL_USART_PARITY_NONE;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_8B;
	}
	else if (MB_PAR_ODD == eParity)
	{
		cfg.Parity = LL_USART_PARITY_ODD;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_9B;
	}
	else
	{
		cfg.Parity = LL_USART_PARITY_EVEN;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_9B;
	}

	// rtu timer
	if (ulBaudRate > 19200)
	{
		qc_obj.tim_sv = 3;
	}
	else
	{
		qc_obj.tim_sv = (com_timer_cal(ulBaudRate, (35 * 11 + 9) / 10, 300, 12000) + 99) / 100;
	}

	LL_USART_Init(qc_port, &cfg);
	LL_USART_Enable(qc_port);

	nvic_irq_set(qc_uart_irqn, 0x06, 1);

	qc_tx1_rx0_enable(0);

	UART_TX_EN();
}

// 中断处理

void qc_isr()
{
	volatile uint32_t IntSt;
	volatile uint8_t Data;

	IntSt = qc_port->SR;

	if (IntSt & (USART_SR_RXNE | USART_SR_ORE))
	{
		Data = UART_RCV_DAT();

		if ((IntSt & UART_RX_ERR_FLAG) != 0)
		{
			if (qc_obj.rx_cnt >= 1)
			{
				qc_obj.err_hal = 1;
			}
			UART_RX_ERR_CLR();
		}

		qc_data_rcv(&qc_obj, Data);
	}
	else if ((qc_obj.tx_size <= qc_obj.tx_cnt) && (IntSt & USART_SR_TC))
	{
		qc_send_end(&qc_obj);
		qc_tx1_rx0_enable(0);
		UART_TX_TC_CLR();
	}
	else if (IntSt & USART_SR_TXE)
	{
		if (qc_data_send(&qc_obj, qc_rtu_byte_send, 1))
		{
			UART_TX_TC_EN();
		}
	}
	else
	{
	}
}

应用层创建独立任务处理modbus事件。
mbapp.c为任务处理文件,文件中的如下定义用来配置开启哪一路modbus,两路可以同时开启。
#define MB1_EN 1
#define MB2_EN 1

void qc_task(const void *argv)
{
	qc_cmd_type mcmd;
	uu8 bdone;

// slave config
#if MB1_EN > 0
	qc01_Init(QC_MODE_SLAVE, 9600, MB_PAR_NONE);
	mb.qc01.os_event_send = mb_os_send;
#endif

#if MB2_EN > 0
	qc02_Init(QC_MODE_SLAVE, 9600, MB_PAR_NONE);
	mb.qc02.os_event_send = mb_os_send;
#endif

	// cmd config
	mcmd.id = 1;
	mcmd.wdat = mb_tst.pv_w;
	mcmd.rdat = mb_tst.pv_r;
	mcmd.wa = word_make(0xF, 5);
	mcmd.wn = 4;
	mcmd.ra = word_make(0x00, 0x10);
	mcmd.rn = 6;
	mcmd.callback = qc_callback;
	mcmd.attr = QC_MB_ATTR_HOLD_RW;

#if MB1_EN > 0
	mqc_stc_cmd_req(&mb.qc01, 0, &mcmd);
#endif

#if MB1_EN > 0
	mqc_stc_cmd_req(&mb.qc02, 0, &mcmd);
#endif

	loop(32) mb_tst.reg_w[index] = index;
	mb_tst.reg_w[0] = 0x5588;

	// task handle
	for (;;)
	{
		osEvent event;

		// wait modbus event
		event = osSignalWait(0, 1000);

		(void)event;
		
#if MB1_EN > 0
		qc_mb_poll(&mb.qc01);
#endif

#if MB2_EN > 0
		qc_mb_poll(&mb.qc02);
#endif
	}
}

程序下载测试如下:
mbs1.png

代码托管在GITEE,有兴趣的小伙可以一起测试研究:
https://gitee.com/aple_sun/opendev-f1-v4.git

更多回帖

发帖
×
20
完善资料,
赚取积分