本帖最后由 Alden 于 2023-3-16 10:39 编辑
#技术资源#
FreeModbus简介 FreeMODBUS是一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植。Modbus是一个工业制造环境中应用的一个通用协议。Modbus通信协议栈包括两层:Modbus应用层协议,该层定义了数据模式和功能;另外一层是网络层。 FreeModbus协议对硬件的需求非常少——基本上任何具有串行接口,并且有一些能够容纳modbus数据帧的RAM的微控制器都足够了。 接下来基于其他的移植教程,将Freemodbus移植到APM32F030中,软件库基于极海SDK 《APM32F0xx_SDK_v1.7》 Freemodbus的源码可以直接在其官网:freemodbus官网 下载从机代码。 所需的文件只用modbus文件夹和demo里面BARE文件夹里的port文件。 实际使用中还需要对源文件做许多细节修改,可以直接使用修改好了的modbus进行接口移植。 参考来源:STM32 移植FreeModbus详细过程 将modbus的相关文件添加进APM32F030的例程中,即可进行下一步移植工作。 modbus通讯只需要使用一个串口和一个定时器,相关接口函数分别在portserial.c和porttimer.c文件中。 首先是portserial.c中串口的相关配置。 需要配置串口的初始化、中断处理、字节收发等。 串口初始化: void USART1_Config(uint16_t buad)
{
GPIO_Config_T gpioConfig;
USART_Config_T usartConfigStruct;
/* Enable GPIO clock */
RCM_EnableAHBPeriphClock(MINI_COM1_TX_GPIO_CLK | MINI_COM2_TX_GPIO_CLK);
/* Enable COM1 or COM2 clock */
RCM_EnableAPB2PeriphClock(MINI_COM1_CLK);
RCM_EnableAPB2PeriphClock(MINI_COM2_CLK);
/* Connect PXx to USARTx_Tx */
GPIO_ConfigPinAF(MINI_COM1_TX_GPIO_PORT, MINI_COM1_TX_SOURCE, MINI_COM1_TX_AF);
/* Connect PXx to USARRX_Rx */
GPIO_ConfigPinAF(MINI_COM1_RX_GPIO_PORT, MINI_COM1_RX_SOURCE, MINI_COM1_RX_AF);
/* Configure USART Tx as alternate function push-pull */
gpioConfig.mode = GPIO_MODE_AF;
gpioConfig.pin = MINI_COM1_TX_PIN;
gpioConfig.speed = GPIO_SPEED_50MHz;
gpioConfig.outtype = GPIO_OUT_TYPE_PP;
gpioConfig.pupd = GPIO_PUPD_PU;
GPIO_Config(MINI_COM1_TX_GPIO_PORT, &gpioConfig);
/* Configure USART Rx as input floating */
gpioConfig.pin = MINI_COM1_RX_PIN;
GPIO_Config(MINI_COM1_RX_GPIO_PORT, &gpioConfig);
/* MINI_USARTs configured as follow: */
/* BaudRate = 115200 baud */
usartConfigStruct.baudRate = buad;
/* Receive and transmit enabled */
usartConfigStruct.mode = USART_MODE_TX_RX;
/* Hardware flow control disabled (RTS and CTS signals) */
usartConfigStruct.hardwareFlowCtrl = USART_FLOW_CTRL_NONE;
/* No parity */
usartConfigStruct.parity = USART_PARITY_NONE;
/* One Stop Bit */
usartConfigStruct.stopBits = USART_STOP_BIT_1;
/* Word Length = 8 Bits */
usartConfigStruct.wordLength = USART_WORD_LEN_8B;
/* USART_Config */
USART_Config(MINI_COM1, &usartConfigStruct);
/* Enable USART_Interrupt_RXBNEIE */
// USART_EnableInterrupt(MINI_COM1, USART_INT_RXBNEIE);
NVIC_EnableIRQRequest(MINI_COM1_IRQn, 2);
/* Enable USART */
USART_Enable(MINI_COM1);
}
中断处理:void USART1_IRQHandler(void)
{
//发生接收中断
if(USART_ReadStatusFlag(MINI_COM1, USART_FLAG_RXBNE) == SET)
{
prvvUARTRxISR(); //串口接收中断调用函数
//清除中断标志位
USART_ClearStatusFlag(USART1, USART_FLAG_RXBNE);
}
if(USART_ReadStatusFlag(MINI_COM1, USART_FLAG_OVRE) == SET)
{
prvvUARTRxISR(); //串口发送中断调用函数
USART_ClearStatusFlag(USART1, USART_FLAG_OVRE);
}
//发生完成中断
if(USART_ReadStatusFlag(MINI_COM1, USART_FLAG_TXC) == SET)
{
prvvUARTTxReadyISR();
//清除中断标志
USART_ClearStatusFlag(USART1, USART_FLAG_TXC);
}
}
portserial.c文件的收发处理
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
if(xRxEnable == TRUE)
{
//UART中断使能
USART_EnableInterrupt(MINI_COM1, USART_INT_RXBNEIE);
}
else
{
//禁止接收和接收中断
USART_DisableInterrupt(MINI_COM1, USART_INT_RXBNEIE);
}
//STM32串口发送中断使能
if(xTxEnable == TRUE)
{
//使能发送中断
USART_EnableInterrupt(MINI_COM1, USART_INT_TXCIE);
}
else
{
//禁止发送中断
USART_DisableInterrupt(MINI_COM1, USART_INT_TXCIE);
}
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
USART1_Config((uint16_t)ulBaudRate);
return TRUE;
}
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
/* Put a byte in the UARTs transmit buffer. This function is called
* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
* called. */
USART_TxData(USART1, ucByte); //发送一个字节
return TRUE;
}
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
/* Return the byte in the UARTs receive buffer. This function is called
* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
*/
*pucByte = USART_RxData(USART1);
return TRUE;
}
porttimer.c文件中需要配置一个定时器,定时器时基为50us。
TMR14初始化:
void APM_MINI_TMR14_Init(uint16_t period)
{
TMR_TimeBase_T timeBaseConfig;
/* Enable Clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR14);
/* Set clockDivision = 1 */
timeBaseConfig.clockDivision = TMR_CKD_DIV1;
/* Up-counter */
timeBaseConfig.counterMode = TMR_COUNTER_MODE_UP;
/* Set divider = 2399.So TMR1 clock freq ~= 48/(2400) = 20kHZ */
timeBaseConfig.div = (2400-1) ;
/* Set counter = 0xffff */
timeBaseConfig.period = period;
/* Repetition counter = 0x0 */
// timeBaseConfig.repetitionCounter = 0;
TMR_ConfigTimeBase(TMR14, &timeBaseConfig);
/* Enable update interrupt*/
// TMR_EnableInterrupt(TMR14, TMR_INT_UPDATE);
NVIC_EnableIRQRequest(TMR14_IRQn, 2);
/* Enable TMR14 */
TMR_Enable(TMR14);
}
TMR14中断函数:
void TMR14_IRQHandler(void)
{
if (TMR_ReadIntFlag(TMR14, TMR_INT_FLAG_UPDATE) != RESET)
{
prvvTIMERExpiredISR();
TMR_ClearIntFlag(TMR14, TMR_INT_FLAG_UPDATE);
}
}
porttimer.c文件接口配置。
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
APM_MINI_TMR14_Init(usTim1Timerout50us);
return TRUE;
}
void vMBPortTimersEnable( )
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
TMR_ClearIntFlag(TMR14, TMR_INT_FLAG_UPDATE);
TMR_EnableInterrupt(TMR14, TMR_INT_UPDATE);
TMR_SetCounter(TMR14,0x0000);
TMR_Enable(TMR14);
}
void vMBPortTimersDisable( )
{
/* Disable any pending timers. */
TMR_ClearIntFlag(TMR14, TMR_INT_FLAG_UPDATE);
TMR_DisableInterrupt(TMR14, TMR_INT_UPDATE);
TMR_SetCounter(TMR14,0x0000);
TMR_Disable(TMR14);
}
然后再定义各个模拟寄存器的地址和大小,补全补全输入寄存器操作函数、保持寄存器操作函数、线圈操作函数、离散寄存器函数
详细见prot.c文件。
然后完善main函数即可。
int main(void)
{
eMBInit(MB_RTU, 0x01, 1, 9600, MB_PAR_NONE); //初始化FreeModbus
eMBEnable(); //启动FreeModbus
while(1)
{
(void)eMBPoll(); //查询数据帧
}
}
程序移植完成后即可使用modbus工具测试,配置ModbusRTU的校验方式。
然后就可以设置发送01 04 00 00 00 01,就会自动计算校验值发送出去,得到MCU回传的结果。
APM32F0xx_SDK_v1.7-Freemodbus.zip
(1.45 MB)
crc计算助手.zip
(320.1 KB)
freemodbus-master1.6.zip
(4.24 MB)
|