A-A+

[GD32F207开发板]Modbus with DMA

2016年11月23日 电子世界 暂无评论

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中断中切换总线方向

标签:

Copyright © E网新时代 保留所有权利.   Theme  Ality站点地图
查询次数: 23
粤ICP备14073293号-1

用户登录

分享到: