使用数据缓存的思路处理接收的数据,DMA采用循环接收的方式,利用接收索引与取数据索引不同判断是否有新数据导入需要处理,从原理上可以解决一些数据阻塞覆盖的问题,理论上来说,在单片机算力不足导致主循环处理数据不及时时会出现数据处理错误,此时需要扩大缓冲区或更换芯片,但整体来讲应用效果应该会比较好。
数据缓存处理方法:即取数据索引若与存数据索引不同则不断自增并取数据判断,直到追上储存数据的索引。
typedef
union{
struct {
u8 b0:1;
u8 b1:1;
u8 b2:1;
u8 b3:1;
u8 b4:1;
u8 b5:1;
u8 b6:1;
u8 b7:1;
u8 b8:1;
u8 b9:1;
u8 b10:1;
u8 b11:1;
u8 b12:1;
u8 b13:1;
u8 b14:1;
u8 b15:1;
} bit;
struct{
u8 low; /* low 8 bit */
u8 high; /* high 8 bit */
}byte;
u16 word;
}word_def;
#define TX_MODE (GPIOB->ODR |= GPIO_Pin_8)
#define RX_MODE (GPIOB->ODR &= ~GPIO_Pin_8)
word_def UartStateAdr;
#define FunctionReceiveFlag UartStateAdr.bit.b5//功能码接收成功标志
#define HeadReceiveFlag UartStateAdr.bit.b4//数据头帧接收标志
#define ReceiveVerifyFlag UartStateAdr.bit.b3//取出的数据有效需求校验标志
#define NeedReplyFlag UartStateAdr.bit.b2//接收处理完毕需求回复标志
#define ReceiveCompletedFlag UartStateAdr.bit.b1//接收完毕标志
#define UselessDataFlag UartStateAdr.bit.b0//前期普通校验无效数据标志
typedef struct //对应存放寄存器数据的数组坐标的结构体
{
u8 A;
u8 B;
} ModbusRegs_Coordinate;
ModbusRegs_Coordinate G_Coordinate;//数组坐标结构体变量(全局)
u8 RXmodeFlag;//大循环中持续置位GPIO,避免在其他实践中遇到在中断直接操作GOIO运行后却未置位的情况
u8 IDcode=0x02;//Modbus协议从机ID
u8 G_SendByteTable_8u[256];//发送区数组
u8 G_BufferReceive_8u[256];//接收缓冲区(DMA接收数据存储区)
u8 G_ReceiveByteTable[256];//取数据判断以及校验后实际接收数据的数组
u8 ReceiveDataTemp;//接收传递变量
volatile u8 SaveFetchByteIndex=0;//取出数据的储存指针
volatile u8 ReceiveBufferIndex=0;//接收的数据在缓冲数组位置的指针(指向下一个将被赋值的数组位置)
volatile u8 FetchBufferIndex=0;//取缓冲区数组的数据在缓冲数组的指针(目前取用的数据位置)(u8数据对应256个缓冲数据,自增即可循环,即255+1=0)
u16 G_ModbusRegs_16u[6][128]; //定义700个寄存器
u16 Read_RegAddress;//读取寄存器起始地址
u16 Read_ProcessRegAddress;//读取寄存器过程中地址
u16 Read_RegNum;//读取寄存器的个数
u16 Write_RegAddress;//写寄存器地址 或 写多个寄存器起始地址
u16 Write_ProcessRegAddress;//写寄存器地址 或 写多个寄存器末尾地址
u16 Write_RegNum;//写寄存器的个数
u16 ReceiveLength=8;//接收数据长度初始化为8
u16 SendPacketState;//发送数据位
u16 SendLength;//发送数据长度
u16 CRC_Check;//根据前面数据计算获得的校验值(与接收到的校验比较验证)
u16 SendCRC_Check;//发送数据CRC校验码
/*v将寄存器地址至数组行列坐标转化v*/
ModbusRegs_Coordinate RegCoordinateCalcuate(u16 Address)
{
ModbusRegs_Coordinate Coordinate;
Coordinate.A=(Address>>7);//第A+1行
Coordinate.B=(Address%128);//第B+1列
return (Coordinate);
}
/*^将寄存器地址至数组行列坐标转化^*/
/*vCRC校验计算v*/
u16 Crc_cal_value(u8 *data_value,u8 data_length)
{
u16 crc_value=0xFFFF;
u8 i,j;
for (j=0; j<data_length; j++)
{
crc_value = crc_value ^ *data_value;
data_value++;
for (i=0; i<8; i++)
{
if(crc_value & 0x0001)
{
crc_value = crc_value >> 1;
crc_value = crc_value ^ 0xa001;
}
else
{
crc_value = crc_value >> 1;
}
}
}
return(crc_value);
}
/*^CRC校验计算^*/
void Uart1_IO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8; //485使能端口
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13; //LED
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC,&GPIO_InitStruct);
//设置复用功能的类型
GPIO_PinAFConfig(GPIOB,GPIO_Pin_6,GPIO_AF_0);
GPIO_PinAFConfig(GPIOB,GPIO_Pin_7,GPIO_AF_0);
//复用开漏输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//复用输入上拉
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB,&GPIO_InitStruct);
RX_MODE;
}
void Uart1_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//启动外围时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//8倍过采样模式
USART1->CR1 = (1<<15);
//关闭溢出检测
USART1->CR3 = (1<<12)+(1<<6);//(1<<6):DMA接收使能(1<<7):DMA发送使能
//波特率的计算
// BORD = Fpclk/(8*(2 - OVER8)*DIV)
//目标波特率 9600 DIV = 625
//2400 DIV = 2500
USART1->BRR = (52<<4);
//使能串口,清空TC标志
USART1->CR1 |=(1<<0); // 使能串口
USART1->CR1 |=(1<<3)+(1<<2); // 允许接收和发送
USART1->ICR |=(1<<6); // 清除发送完成标志
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
}
void Uart1DMA_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/*vRX-DMA初始化v*/
DMA1_Channel3->CCR = 0;//复位DMA
DMA1_Channel3->CNDTR = 0;
DMA1_Channel3->CPAR = 0;
DMA1_Channel3->CMAR = 0;
DMA1->IFCR |= ((uint32_t)(DMA_ISR_GIF1 | DMA_ISR_TCIF1 | DMA_ISR_HTIF1 | DMA_ISR_TEIF1));
DMA1_Channel3->CCR =(1<<7)+(1<<1)+(1<<9)+(1<<5);//从外设读,循环操作,执行存储器地址增量操作,存储数据宽度8,优先级低,非存储器到存储器模式,允许传输完成中断
DMA1_Channel3->CPAR = (uint32_t)(&USART1->RDR);//指定DMA的外设目标地址
DMA1_Channel3->CMAR = (uint32_t)G_BufferReceive_8u;//指定DMA的内存目标地址
DMA_Cmd(DMA1_Channel3, DISABLE );//关闭USART1 TX DMA1 所指示的通道
DMA1_Channel3->CNDTR =256;
DMA_Cmd(DMA1_Channel3, ENABLE);//使能DMA通道
// NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_3_IRQn;
// NVIC_InitStructure.NVIC_IRQChannelPriority = 0x00;
// NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
// NVIC_Init(&NVIC_InitStructure);
/*^RX-DMA初始化^*/
}
void Uart1_Communicate(void)
{
u8 a,b;//a用于写寄存器的过程变量,b用于读寄存器的过程变量
u8 timecnt;//RX_Mode转TX_Mode的延时(必须有延时,否则当485芯片高阻态建立较慢时通讯数据会紊乱)
/*vDMA数据缓存区取数据初步校验接收v*/
if(FetchBufferIndex!=ReceiveBufferIndex)//若存储区指针与取数据区指针不重合(说明有数据待处理)且数据未基础取出检验完毕
{
if(G_BufferReceive_8u[FetchBufferIndex]==IDcode&&HeadReceiveFlag==0)
{
G_ReceiveByteTable[SaveFetchByteIndex]=IDcode;
HeadReceiveFlag=1;//头帧判断
FetchBufferIndex++;
SaveFetchByteIndex++;
}
else if(HeadReceiveFlag)
{
if(FunctionReceiveFlag==0)
{
switch(G_BufferReceive_8u[FetchBufferIndex])
{
case 0x03:
{
G_ReceiveByteTable[SaveFetchByteIndex]=0x03;
FunctionReceiveFlag=1;
FetchBufferIndex++;
SaveFetchByteIndex++;
break;
}
case 0x04:
{
G_ReceiveByteTable[SaveFetchByteIndex]=0x04;
FunctionReceiveFlag=1;
FetchBufferIndex++;
SaveFetchByteIndex++;
break;
}
case 0x06:
{
G_ReceiveByteTable[SaveFetchByteIndex]=0x06;
FunctionReceiveFlag=1;
FetchBufferIndex++;
SaveFetchByteIndex++;
break;
}
case 0x10:
{
G_ReceiveByteTable[SaveFetchByteIndex]=0x10;
FunctionReceiveFlag=1;
FetchBufferIndex++;
SaveFetchByteIndex++;
break;
}
default:
{
UselessDataFlag=1;
break;
}
}
}
else//功能码已经接收到
{
switch(G_ReceiveByteTable[1])
{
case 0x03:
{
ReceiveLength=8;
if(SaveFetchByteIndex<ReceiveLength)
{
G_ReceiveByteTable[SaveFetchByteIndex]=G_BufferReceive_8u[FetchBufferIndex];
FetchBufferIndex++;
SaveFetchByteIndex++;
}
if(SaveFetchByteIndex==ReceiveLength)
{
FetchBufferIndex--;
ReceiveVerifyFlag=1;
break;
}
break;
}
case 0x04:
{
ReceiveLength=8;
if(SaveFetchByteIndex<ReceiveLength)
{
G_ReceiveByteTable[SaveFetchByteIndex]=G_BufferReceive_8u[FetchBufferIndex];
FetchBufferIndex++;
SaveFetchByteIndex++;
}
if(SaveFetchByteIndex==ReceiveLength)
{
FetchBufferIndex--;
ReceiveVerifyFlag=1;
break;
}
break;
}
case 0x06:
{
ReceiveLength=8;
if(SaveFetchByteIndex<ReceiveLength)
{
G_ReceiveByteTable[SaveFetchByteIndex]=G_BufferReceive_8u[FetchBufferIndex];
FetchBufferIndex++;
SaveFetchByteIndex++;
}
if(SaveFetchByteIndex==ReceiveLength)
{
FetchBufferIndex--;
ReceiveVerifyFlag=1;
break;
}
break;
}
case 0x10:
{
if(SaveFetchByteIndex<7)
{
G_ReceiveByteTable[SaveFetchByteIndex]=G_BufferReceive_8u[FetchBufferIndex];
FetchBufferIndex++;
SaveFetchByteIndex++;
}
else
{
if(SaveFetchByteIndex==7)
{
G_ReceiveByteTable[SaveFetchByteIndex]=G_BufferReceive_8u[FetchBufferIndex];
Write_RegAddress=G_ReceiveByteTable[2]*256+G_ReceiveByteTable[3];
Write_RegNum=G_ReceiveByteTable[4]*256+G_ReceiveByteTable[5];
if(Write_RegNum*2==G_ReceiveByteTable[6])
{
ReceiveLength=Write_RegNum*2+9;
FetchBufferIndex++;
SaveFetchByteIndex++;
}
else
{
UselessDataFlag=1;
break;
}
}
else if(SaveFetchByteIndex<ReceiveLength)
{
G_ReceiveByteTable[SaveFetchByteIndex]=G_BufferReceive_8u[FetchBufferIndex];
FetchBufferIndex++;
SaveFetchByteIndex++;
}
if(SaveFetchByteIndex==ReceiveLength)
{
FetchBufferIndex--;
ReceiveVerifyFlag=1;
break;
}
}
break;
}
default: break;
}
}
}
else
{
FetchBufferIndex++;
UselessDataFlag=1;
}
if(UselessDataFlag)//无效数据清零重取
{
UselessDataFlag=0;
HeadReceiveFlag=0;
FunctionReceiveFlag=0;
SaveFetchByteIndex=0;
}
}
/*^DMA数据缓存区取数据初步校验接收^*/
/*v485状态再置位v*/
if(RXmodeFlag)RX_MODE;//避免在中断中操作GPIO后不置位的情况(STM32051芯片遇到)
/*v485状态再置位v*/
/*vCRC数据校验v*/
if(ReceiveVerifyFlag)
{
CRC_Check=Crc_cal_value(G_ReceiveByteTable,ReceiveLength-2);
if( CRC_Check == (G_ReceiveByteTable[ReceiveLength-2]+G_ReceiveByteTable[ReceiveLength-1]*256) )//校验正确
{
ReceiveCompletedFlag=1;
}
FetchBufferIndex++;
FunctionReceiveFlag=0;
HeadReceiveFlag=0;
SaveFetchByteIndex=0;
ReceiveVerifyFlag=0;
}
/*^CRC数据校验^*/
/*v接收数据处理v*/
if(ReceiveCompletedFlag)
{
switch(G_ReceiveByteTable[1])
{
case 0x03://读
{
Read_RegAddress=G_ReceiveByteTable[2]*256+G_ReceiveByteTable[3];
Read_RegNum=G_ReceiveByteTable[4]*256+G_ReceiveByteTable[5];
//读数据赋值发送缓存数组在发送处理中
break;
}
case 0x04://读
{
Read_RegAddress=G_ReceiveByteTable[2]*256+G_ReceiveByteTable[3];
Read_RegNum=G_ReceiveByteTable[4]*256+G_ReceiveByteTable[5];
//读数据赋值发送缓存数组在发送处理中
break;
}
case 0x06://写单个
{
Write_RegAddress=G_ReceiveByteTable[2]*256+G_ReceiveByteTable[3];
Write_RegNum=1;
G_Coordinate = RegCoordinateCalcuate(Write_RegAddress);
G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B]=G_ReceiveByteTable[4]*256+G_ReceiveByteTable[5] ;
break;
}
case 0x10://写n个(起始地址与寄存器数量在中断中已经计算)
{
Write_ProcessRegAddress=Write_RegAddress;
for(a=0;a<Write_RegNum;a++)
{
G_Coordinate = RegCoordinateCalcuate(Write_ProcessRegAddress);
G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B]=G_ReceiveByteTable[a*2+7]*256+G_ReceiveByteTable[a*2+8] ;
Write_ProcessRegAddress++;
}
break;
}
default :
{
break;
}
}
NeedReplyFlag=1;
ReceiveCompletedFlag=0;
}
/*^接收数据处理^*/
/*v发送处理v*/
if(NeedReplyFlag)
{
RXmodeFlag=0;
switch(G_ReceiveByteTable[1])
{
case 0x03://读的回复
{
SendLength=Read_RegNum*2+5;
SendPacketState=1;
Read_ProcessRegAddress = Read_RegAddress;
G_SendByteTable_8u[0]=IDcode;
G_SendByteTable_8u[1]=0x03;
G_SendByteTable_8u[2]=(u8)(Read_RegNum*2);
for(b=0;b<Read_RegNum;b++)
{
G_Coordinate = RegCoordinateCalcuate(Read_ProcessRegAddress);
G_SendByteTable_8u[b*2+3]=(u8)(G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B]>>8);
G_SendByteTable_8u[b*2+4]=(u8)G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B];
Read_ProcessRegAddress++;
}
SendCRC_Check = Crc_cal_value(G_SendByteTable_8u,SendLength-2);
G_SendByteTable_8u[SendLength-2]=(u8)(SendCRC_Check);//RCR校验计算出来的高字节与低字节的左右位置与实际数据相反,因此SendCRC_Check的低位放在协议RCR校验高字节位
G_SendByteTable_8u[SendLength-1]=(u8)(SendCRC_Check>>8);
/*-------------------------------------------------------------------------------*/
TX_MODE;
for(timecnt=0;timecnt<60;timecnt++);//模式转换延时
/*-------------------------------------------------------------------------------*/
USART1->TDR = (G_SendByteTable_8u[0] & (uint16_t)0x00FF);
break;
}
case 0x04://读的回复
{
SendLength=Read_RegNum*2+5;
SendPacketState=1;
Read_ProcessRegAddress = Read_RegAddress;
G_SendByteTable_8u[0]=IDcode;
G_SendByteTable_8u[1]=0x04;
G_SendByteTable_8u[2]=(u8)(Read_RegNum*2);
for(b=0;b<Read_RegNum;b++)
{
G_Coordinate = RegCoordinateCalcuate(Read_ProcessRegAddress);
G_SendByteTable_8u[b*2+3]=(u8)(G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B]>>8);
G_SendByteTable_8u[b*2+4]=(u8)G_ModbusRegs_16u[G_Coordinate.A][G_Coordinate.B];
Read_ProcessRegAddress++;
}
SendCRC_Check = Crc_cal_value(G_SendByteTable_8u,SendLength-2);
G_SendByteTable_8u[SendLength-2]=(u8)(SendCRC_Check);//RCR校验计算出来的高字节与低字节的左右位置与实际数据相反,因此SendCRC_Check的低位放在协议RCR校验高字节位
G_SendByteTable_8u[SendLength-1]=(u8)(SendCRC_Check>>8);
/*-------------------------------------------------------------------------------*/
TX_MODE;
for(timecnt=0;timecnt<60;timecnt++);//模式转换延时
/*-------------------------------------------------------------------------------*/
USART1->TDR = (G_SendByteTable_8u[0] & (uint16_t)0x00FF);
break;
}
case 0x06://写单个的回复
{
SendLength=8;
SendPacketState=1;
for(b=0;b<SendLength;b++)G_SendByteTable_8u[b] = G_ReceiveByteTable[b];//06的回复数据与接收数据相同
/*-------------------------------------------------------------------------------*/
TX_MODE;
for(timecnt=0;timecnt<60;timecnt++);//模式转换延时
/*-------------------------------------------------------------------------------*/
USART1->TDR = (G_SendByteTable_8u[0] & (uint16_t)0x00FF);
break;
}
case 0x10://写n个(起始地址与寄存器数量在中断中已经计算)的回复
{
SendLength=8;
SendPacketState=1;
for(b=0;b<6;b++)G_SendByteTable_8u[b] = G_ReceiveByteTable[b];
SendCRC_Check = Crc_cal_value(G_SendByteTable_8u,6);
G_SendByteTable_8u[6]=(u8)(SendCRC_Check);//RCR校验计算出来的高字节与低字节的左右位置与实际数据相反,因此SendCRC_Check的低位放在协议RCR校验高字节位
G_SendByteTable_8u[7]=(u8)(SendCRC_Check>>8);
/*-------------------------------------------------------------------------------*/
TX_MODE;
for(timecnt=0;timecnt<60;timecnt++);//模式转换延时
/*-------------------------------------------------------------------------------*/
USART1->TDR = (G_SendByteTable_8u[0] & (uint16_t)0x00FF);
break;
}
default :
{
break;
}
}
NeedReplyFlag=0;
}
/*^发送处理^*/
}
void USART1_IRQHandler(void)
{
/*v发送v*/
if((USART1->ISR & USART_ISR_TC) == USART_ISR_TC)
{
USART1->ICR |= USART_ISR_TC;
if(SendPacketState<SendLength)
{
USART1->TDR = (G_SendByteTable_8u[SendPacketState] & (uint16_t)0x00FF);
SendPacketState ++;
}
else
{
RX_MODE;
RXmodeFlag=1;
}
}
/*^发送^*/
/*v获取接收数据指针位置v*/
if((USART1->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)
{
USART1->ICR |= USART_ISR_IDLE;
ReceiveBufferIndex=256-DMA1_Channel3->CNDTR;
}
/*^获取接收数据指针位置^*/
}