文章目录
- 前言
- 一、移植前需要的工作
- 二、修改点讲解
- 1.串口中断
- 2.定时器
- 3.保持寄存器
- 4.测试
- 总结
前言
最近需要做一个modbus485的传感器,主要是用来做从机。之前做过主机的是stm标准库,那这次做一个HAL的从机协议栈,方便大家直接获取数据。
移植成功的代码仓库如下:Freemodbus从机仓库代码
一、移植前需要的工作
1.了解modbus485协议(协议帧内容含义等等)
2.选一块stm32的开发板(随便选,能用stm32cubemx就行)
3.移植这个协议,主要关注3点,串口、定时器以及数据存储的寄存器缓冲区
二、修改点讲解
1.串口中断
portserial.c :
extern UART_HandleTypeDef huart1;
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
eMBParity eParity)
{
//这个是用stm32cubemx生成的函数名称,我增加了参数
MX_USART1_UART_Init(ulBaudRate);
return TRUE;
}
void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
ENTER_CRITICAL_SECTION();
if (xRxEnable)
{
//使能串口接收中断
__HAL_UART_ENABLE_IT(&huart1, USART_IT_RXNE);
}
else
{
//关闭串口接收中断
__HAL_UART_DISABLE_IT(&huart1, USART_IT_RXNE);
}
if (xTxEnable)
{
//使能串口发送中断
__HAL_UART_ENABLE_IT(&huart1, USART_IT_TXE);
}
else
{
//关闭串口发送中断
__HAL_UART_DISABLE_IT(&huart1, USART_IT_TXE);
}
EXIT_CRITICAL_SECTION();
}
void vMBPortClose(void)
{
//不做函数处理
}
BOOL xMBPortSerialPutByte(CHAR ucByte)
{
// 发送串口数据 一次发送一个数据
HAL_UART_Transmit(&huart1, &ucByte,1,5);
return TRUE;
}
BOOL xMBPortSerialGetByte(CHAR *pucByte)
{
// 获取串口数据 一次拿一个数据
HAL_UART_Receive(&huart1,pucByte,1, 0);
return TRUE;
}
void prvvUARTTxReadyISR(void)
{
pxMBFrameCBTransmitterEmpty();
}
void prvvUARTRxISR(void)
{
pxMBFrameCBByteReceived();
}
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
{ //获取接收RXNE标志位是否被置位
prvvUARTRxISR();
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
}
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE) == SET)
{
prvvUARTTxReadyISR();
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);
}
}
2.定时器
代码如下:
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
{
//这个是用stm32cubemx生成的函数名称,我增加了参数
MX_TIM2_Init(usTim1Timerout50us);
return TRUE;
}
void vMBPortTimersEnable()
{
// 开启定时器,计算是否超时
__HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); // 清除中断标志位
__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE); //开启定时器自动更新中断
__HAL_TIM_SET_COUNTER(&htim2, 0x0000); // 设置计数器的值为0
__HAL_TIM_ENABLE(&htim2); // 开启定时器中断
}
void vMBPortTimersDisable()
{
__HAL_TIM_DISABLE(&htim2); // 关闭定时器中断
__HAL_TIM_SET_COUNTER(&htim2, 0x0000); // 设置计数器的值为0
__HAL_TIM_DISABLE_IT(&htim2, TIM_IT_UPDATE); //关闭定时器自动更新中断
__HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); // 清除中断标志位
}
void prvvTIMERExpiredISR(void)
{
(void)pxMBPortCBTimerExpired();
}
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim2);
if(__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET)
{
prvvTIMERExpiredISR();
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); // 清除中断标志位
}
}
3.保持寄存器
在寄存器中,我们需要划分我们自己的寄存器的含义,从而达到数据传输的含义
#define S_REG_HOLDING_START 0
#这是一个倾角传感器的保持寄存器数据划分
/*----------------------------------------
* 划分保持寄存器的类型区域
*========================================
* 0 |从机地址
*-----------------------------------------
* 1 |X轴角度
*-----------------------------------------
* 2 |Y轴角度
*-----------------------------------------
* 3 |Z轴角度
*-----------------------------------------
*/
#define S_SLAVEID_REG_START S_REG_HOLDING_START
#define S_SLAVEID_REG_NUM 1
#define S_SLAVEID_REG_END S_SLAVEID_REG_START+S_SLAVEID_REG_NUM
#define S_XANGLE_REG_START S_SLAVEID_REG_END
#define S_XANGLE_REG_NUM 1
#define S_XANGLE_REG_END S_XANGLE_REG_START+S_XANGLE_REG_NUM
#define S_YANGLE_REG_START S_XANGLE_REG_END
#define S_YANGLE_REG_NUM 1
#define S_YANGLE_REG_END S_YANGLE_REG_START+S_YANGLE_REG_NUM
#define S_ZANGLE_REG_START S_YANGLE_REG_END
#define S_ZANGLE_REG_NUM 1
#define S_ZANGLE_REG_END S_ZANGLE_REG_START+S_ZANGLE_REG_NUM
//当你发送指令过来时,最终的解析在这里。这是一个关键的函数,要想理解这个函数,你要多参考前面的接口。
//usAddress 要查询的寄存器地址 usNRegs 寄存器数量 eMode 模式 pucRegBuffer 缓冲区
//这个函数就是根据usAddress ,从你的保持寄存器usSRegHoldBuf中读出想要的数据返回给主机。
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT *pusRegHoldingBuf;
USHORT reg_holding_start;
USHORT reg_holding_nregs;
USHORT usRegHoldStart;
pusRegHoldingBuf = usSRegHoldBuf;
reg_holding_start = S_REG_HOLDING_START;
reg_holding_nregs = S_REG_HOLDING_NREGS;
usRegHoldStart = usSRegHoldStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= reg_holding_start)
&& (usAddress + usNRegs <= reg_holding_start + reg_holding_nregs))
{
iRegIndex = usAddress - usRegHoldStart;
switch (eMode)
{
/* read current register values from the protocol stack. */
case MB_REG_READ:
while (usNRegs > 0)
{
*pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] >> 8);
*pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
break;
/* write current register values with new values from the protocol stack. */
case MB_REG_WRITE:
while (usNRegs > 0)
{
pusRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
pusRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
4.测试
注意烧写的时候要擦除整片芯片
总结
多多指教