|
Modbus在工业仪表的应用非常广,连接PLC、HMI、PC等设备十分方便,最初用开源的freemodbus,这个功能比较完善,就是效率有点低,后来用的时间长了,就按照freemodbus的分层结构自己写了一份主从一体程序,移植到不同的平台主要就是硬件接口层了,拿到207i评估板后照例移植了过来。
207I的串口支持DMA,接收由于要同时操作定时器,暂时还没想到办法使用DMA功能,不过发送用DMA优化太合适不过了,这样可以减少大量的中断次数,特别是作为从机的时候。
报文发送程序示例:
uint8_t mbWriteMultipleRegister( uint16_t DataAdd, uint8_t Len, uint16_t *Src )
{
uint16_t DataIndex;
WORD_VAL WToB;
if( ( Len > 123 ) || ( Len == 0 ) || ( ( 0xFFFF - DataAdd) < (Len - 1) ) )
return MB_ERR_OVERFLOW;
if( (mbSndSt != MB_TX_IDLE) || (mbRcvSt != MB_RX_IDLE))
return MB_ERR_BUSY;
mbAduBuff[0] = mbSlaveAddr;
mbAduBuff[1] = 0x10;
WToB.Val = DataAdd;
mbAduBuff[2] = WToB.byte.HB;
mbAduBuff[3] = WToB.byte.LB;
mbAduBuff[4] = 0;
mbAduBuff[5] = Len;
mbAduBuff[6] = Len * 2;
DataIndex = 7;
while(Len --)
{
WToB.Val = *(Src ++);
mbAduBuff[DataIndex ++] = WToB.byte.HB;
mbAduBuff[DataIndex ++] = WToB.byte.LB;
}
mbRcvSt = MB_RX_RCV;
mbSndCnt = DataIndex;
mbAduSend();
return MB_TX_IDLE;
}
//硬件接口程序如下
void mbAduSend()
{
//添加crc并开启发送
UINT16_VAL Data;
if(mbState.bits.RTUMode)
{
Data.Val = usMBCRC16( mbAduBuff, mbSndCnt );
mbAduBuff[mbSndCnt++] = Data.byte.LB;
mbAduBuff[mbSndCnt++] = Data.byte.HB;
pmbSndCur = mbAduBuff;
mbPortEnable(DISABLE,ENABLE);
}
mbSndSt = MB_TX_XMIT;
}
/*****************************************************************************//*!
*
* @brief Uart En or Dis.
*
* @param none
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void mbPortEnable( uint8_t xRxEnable, uint8_t xTxEnable )
{
volatile uint8_t u8Temp;
if(xRxEnable)
{
mbUART->CTLR1 &= ~((uint32_t)USART_CTLR1_TCIE);
mbUART->CTLR1 &= ~((uint32_t)USART_CTLR1_TBEIE);
U1RxEnable();
mbUART->CTLR1 &= ~((uint32_t)USART_CTLR1_TEN);
mbUART->CTLR1 |= (uint32_t)USART_CTLR1_REN;
u8Temp = mbUART->DR;
u8Temp = mbUART->STR;
while((mbUART->STR & (USART_STR_ORE | USART_STR_RBNE)) != 0)
{
u8Temp = mbUART->DR;
u8Temp = mbUART->STR;
}
mbUART->CTLR1 |= USART_CTLR1_RBNEIE;
}
else if(xTxEnable)
{
mbUART->CTLR1 &= ~((uint32_t)USART_CTLR1_RBNEIE);
U1TxEnable();
mbUART->CTLR1 &= ~((uint32_t) USART_CTLR1_REN );
mbUART->CTLR1 |= (uint32_t)USART_CTLR1_TEN;
//mbSndCnt --; //work without dma
//mbUART->DR = *(pmbSndCur ++); //work without dma
//mbUART->CTLR1 |= (uint32_t)(USART_CTLR1_TBEIE); //work without dma
Uart1TxDma(mbAduBuff, mbSndCnt);
}
else
{
}
}
//DMA 处理程序
/*****************************************************************************//*!
*
* @brief uart dma channel = 4.
*
* @param none
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void Uart1TxDma(uint8_t *pData, uint16_t Len)
{
DMA1_CHANNEL4->MBAR = (uint32_t)pData;
DMA1_CHANNEL4->RCNT = Len;
DMA1_CHANNEL4->CTLR |= DMA_CTLR_CHEN;
__disable_irq();
mbUART->CTLR3 |= ((uint32_t)USART_CTLR3_DENT);
mbUART->STR &= ~((uint32_t)(USART_STR_TC));
__enable_irq();
}
/*****************************************************************************//*!
*
* @brief uart dma channel4 isr.
*
* @param none
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void DMA1_Channel4_IRQHandler()
{
if(DMA1->IFR & DMA_IFR_TCIF4)
{
DMA1->ICR |= DMA_ICR_GIC4 | DMA_ICR_TCIC4 | DMA_ICR_HTIC4 | DMA_ICR_ERRIC4;
mbSndCnt = 0;
mbUART->CTLR3 &= ~((uint32_t)USART_CTLR3_DENT);
DMA1_CHANNEL4->CTLR &= ~DMA_CTLR_CHEN;
mbUART->CTLR1 |= (uint32_t)(USART_CTLR1_TCIE); //打开uart发送结束中断
}
else
{
//错误的中断
DMA1->ICR |= DMA_ICR_GIC4 | DMA_ICR_TCIC4 | DMA_ICR_HTIC4 | DMA_ICR_ERRIC4;
}
}
对于RS485接口,需要控制发送方向,这里在DMA结束后打开UART的 TC中断,在TC中断中切换总线方向 |
|