初级会员

- 积分
- 82
- 金钱
- 82
- 注册时间
- 2019-11-23
- 在线时间
- 12 小时
|
10金钱
每次按一下复位按钮才会获取新数据,不然就是一直循环打印第一次获取的数据- main.c
- =====================================================================
- #include "stm32f4xx.h"
- #include "usart.h"
- #include "delay.h"
- #include "MODBUS_Master.h"
- extern u16 Master_ReadReg[1000]; //主机接收缓冲区
- extern u8 RS485_TX_BUFF[2048];
- extern u8 RS485_RX_BUFF[2048];
- volatile u16 wx;
- int main(void)
- {
- u8 i;
- SystemInit();
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
- delay_init(168); //延时函数初始化
- uart_init(57600); //串口初始化为115200
-
- Modbus_RegMap();
- Master_USART2_Init();
- Timer4_enable(5000); //500ms 通讯节拍
- while(1)
- {
- /* RS485_RX_BUFF[4] 是风向数据 ,但无法实时读取*/
- for(i=0;i<8;i++)
- {
- printf("%x \r",RS485_TX_BUFF[i]);
- }
- printf("\n");
- for(i=0;i<8;i++)
- {
- printf("%x \r",RS485_RX_BUFF[i]);
- }
- printf("\n");
- printf("%x\r",RS485_RX_BUFF[4]);
- printf("%x\r",wx);
- printf("\n");
- delay_ms(1000);
- }
-
-
- }
复制代码- modbus.c
- =====================================================================
- #include "MODBUS_Master.h"
- #include "crc16.h"
- u32 Master_Baudrate=4800; //通讯波特率
- u8 Master_Parity=0; //0无校验;1奇校验;2偶校验
- u16 RS485_Frame_Distance=4; //数据帧最小间隔(ms),超过此时间则认为是下一帧
- u8 RS485_RX_BUFF[2048]; //接收缓冲区2048字节
- u16 RS485_RX_CNT=0;//接收计数器
- u8 RS485_RxFlag=0;//接收一帧结束标记
- u8 RS485_TX_BUFF[2048];//发送缓冲区
- u16 RS485_TX_CNT=0;//发送计数器
- u8 RS485_TxFlag=0;//发送一帧结束标记
- extern volatile u16 wx;
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //主机命令区
- u8 SlaverAddr = 0x01; //从机地址
- u8 Fuction = 0x03; //功能码
- u16 StartAddr = 0x0000; //起始地址
- u16 ValueOrLenth = 0x0002; //数据or长度
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////
- u8 TX_RX_SET=0; //发送,接受命令切换。 0 发送模式 1接受模式
- u8 ComErr=8; //0代表通讯正常
- //1代表CRC错误
- //2代表功能码错误
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //Master寄存器和单片机寄存器的映射关系
- u16 Master_InputIO[100]; //输入开关量寄存器(这里使用的是位带操作) 注意: 这里储存从机返回的数据。 开关量的数据只能是0,1 例如 Master_InputIO[5]=0;Master_InputIO[8]=1;
- u16 Master_OutputIO[100]; //输出(给从机)开关量寄存器(这里使用的是位带操作) 功能码 05 15
- u16 Master_ReadReg[1000]; //只读寄存器----存储从机返回的数据 功能码 03
- u16 Master_WriteReg[1000];//写(写从机)寄存器-------将寄存器中的数据送给从机 功能码 06 16
- //u32 testData1=1201,testData2=1002,testData3=2303,testData4=8204;
- void Modbus_RegMap(void)
- {
- Master_WriteReg[0]=1;
- Master_WriteReg[1]=8;
- Master_WriteReg[2]=9;
- Master_WriteReg[3]=235;
- Master_WriteReg[4]=8690;
- Master_WriteReg[5]=23578;
- Master_WriteReg[6]=125;
-
- Master_OutputIO[20]=1;
- Master_OutputIO[21]=0;
- Master_OutputIO[22]=1;
- Master_OutputIO[23]=1;
- Master_OutputIO[24]=0;
- Master_OutputIO[25]=0;
- Master_OutputIO[26]=1;
- Master_OutputIO[27]=1;
-
- Master_OutputIO[28]=1;
- Master_OutputIO[29]=0;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- void Master_USART2_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- USART_InitTypeDef USART_InitStructure;
-
- /*使能使用的外设时钟*/
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC, ENABLE); //GPIOA、GPIOG时钟使能
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //串口2时钟使能
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2);
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2);
- /*GPIOA_Pin_2----USART2_Tx发送端*/
- /*GPIOA_Pin_3----USART2_Rx接收端*/
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
-
- //PC23端口配置,控制485是处于发送状态还是接收状态
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //普通输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOC, &GPIO_InitStructure);
-
- USART_DeInit(USART2); //复位串口2
-
- //USART2通信配置
- USART_InitStructure.USART_BaudRate = Master_Baudrate;//波特率
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
- switch (Master_Parity) //0无校验;1奇校验;2偶校验
- {
- case 0:
- USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验
- break;
- case 1:
- USART_InitStructure.USART_Parity = USART_Parity_Odd; //奇模式
- break;
- case 2:
- USART_InitStructure.USART_Parity = USART_Parity_Even; //偶模式
- break;
- default:
- break;
- }
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送与接收模式
- USART_Init(USART2, &USART_InitStructure);//初始化串口2
-
- USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除串口2的中断标志位 RXNE:读数据寄存器非空(后续将数据读入RX_BUFF)
- USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); //使能串口2接收中断
-
- //Usart2的NVIC配置
- NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //响应优先级2
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
-
- USART_Cmd(USART2, ENABLE); //使能串口2
- RS485_TX_EN = 0; //默认为接收状态
- RS485_RX_EN = 0;
- Timer7_Init(); //定时器7初始化,用于监视空闲时间
- }
- //定时器4初始化
- //定时1s进行通讯,分2步:前500ms进行发送功能,后500ms处理从机返回的数据
- void Timer4_enable(u16 arr) //TIM4使能
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
-
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
- TIM_TimeBaseStructure.TIM_Prescaler = 7199; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
- TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
-
- TIM_ITConfig( //使能或者失能指定的TIM中断
- TIM4, //TIM2
- TIM_IT_Update | //TIM 中断源
- TIM_IT_Trigger, //TIM 触发中断源
- ENABLE //使能
- );
- NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0级
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级3级
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
- NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
- TIM_Cmd(TIM4, ENABLE); //使能TIM4外设
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////
- //定时器7初始化---功能:判断从机返回的数据是否接受完成
- void Timer7_Init(void)
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); //TIM7时钟使能
- //TIM7初始化设置
- TIM_TimeBaseStructure.TIM_Period = RS485_Frame_Distance*10; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 =4; //数据帧最小间隔(40=4ms),超过此时间则认为是下一帧
- TIM_TimeBaseStructure.TIM_Prescaler =7199; //设置用来作为TIMx时钟频率除数的预分频值 设置计数频率为10kHz
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
- TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
- TIM_ITConfig( TIM7, TIM_IT_Update, ENABLE );//TIM7 允许更新中断
- //TIM7中断分组配置
- NVIC_InitStructure.NVIC_IRQChannel =TIM7_IRQn; //TIM7中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2级
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级3级
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
- NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
- }
- /////////////////////////////////////////////////////////////////////////////////////
- void USART2_IRQHandler(void)//串口2中断服务程序
- {
- u8 res;
- u8 err;
- if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
- {
- //清除中断标志位
- //USART_ClearITPendingBit(USART2, USART_IT_RXNE);
- USART2->SR &= ~(0x1<<5);
- if(USART_GetFlagStatus(USART2,USART_FLAG_NE|USART_FLAG_FE|USART_FLAG_PE))
- err=1;//检测到噪音、帧错误或校验错误
- else
- err=0;
-
- res=USART_ReceiveData(USART2); //读接收到的字节,同时相关标志自动清除
- if((RS485_RX_CNT<2047)&&(err==0))
- {
- RS485_RX_BUFF[RS485_RX_CNT]=res;
- RS485_RX_CNT++;
-
- TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除定时器溢出中断
- TIM_SetCounter(TIM7,0);//当接收到一个新的字节,将定时器7复位为0,重新计时(相当于喂狗)
- TIM_Cmd(TIM7,ENABLE);//开始计时
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////
- void TIM4_IRQHandler(void) //TIM4中断
- {
- if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET) //检查指定的TIM中断发生与否:TIM 中断源
- {
- TIM_ClearITPendingBit(TIM4, TIM_IT_Update); //清除TIMx的中断待处理位:TIM 中断源 ;
- modbus_rtu();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////
- //用定时器7判断接收空闲时间,当空闲时间大于指定时间,认为一帧结束
- //定时器7中断服务程序
- void TIM7_IRQHandler(void)
- {
- if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)
- {
- TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除中断标志
- TIM_Cmd(TIM7,DISABLE);//停止定时器
-
- RS485_TX_EN = 1;//停止接收,切换为发送状态
- RS485_RX_EN = 1;
- RS485_RxFlag=1;//置位帧结束标记
- }
- }
- //////////////////////////////////////////////////////////////////////////////
- //发送n个字节数据
- //buff:发送区首地址
- //len:发送的字节数
- void RS485_SendData(u8 *buff,u8 len)
- {
- RS485_TX_EN = 1;//切换为发送状态
- RS485_RX_EN = 1;
- while(len--)
- {
- while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);//等待发送区为空
- USART_SendData(USART2,*(buff++));
- }
- while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);//等待发送完成
- TX_RX_SET=1; //发送命令完成,定时器T4处理接收到的数据
- RS485_TX_EN = 0;//接收状态
- RS485_RX_EN = 0;
- }
- /////////////////////////////////////////////////////////////////////////////////////
- void modbus_rtu(void)
- {
- static u8 i=0;
- static u8 j=0;
- switch(i)
- {
- case 0:
- RS485_TX_Service();
- if(TX_RX_SET) i=1;
- break;
- case 1:
- RS485_RX_Service();
- if(TX_RX_SET == 0) i=0;
- if(ComErr==0) //数据正确
- {
- i=0;//完成命令更换功能码!
- }
- else
- {
- i=1;//
- j++;//一个命令发送3次没有应答切换下一个命令
- if(j>=2)
- {
- j=0;
- i=0;
- ComErr=7; //通讯超时
- }
-
- }
- break;
- /* case 2: //从机地址++
- //SlaverAddr++;
- break;
- case 3://功能码
- break;
- */
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////主机发送指令(起点)////////////////////////////////////////////////////////
- //Modbus功能码03处理程序///////////////////////////////////////////////////////////////////////////////////////
- //读保持寄存器
- void Master_03_Slove( u8 board_adr,u16 start_address,u16 lenth )
- {
- u16 calCRC;
- RS485_TX_BUFF[0] = board_adr; //0x01
- RS485_TX_BUFF[1] = READ_HLD_REG; //modbus 指令码0x03
- RS485_TX_BUFF[2] = HI(start_address); //0x00
- RS485_TX_BUFF[3] = LOW(start_address); //0x00
- RS485_TX_BUFF[4] = HI(lenth); //0x00
- RS485_TX_BUFF[5] = LOW(lenth); //0x02
- calCRC=CRC_Compute(RS485_TX_BUFF,6);
- RS485_TX_BUFF[6]=(calCRC>>8)&0xFF;
- RS485_TX_BUFF[7]=(calCRC)&0xFF;
- RS485_SendData(RS485_TX_BUFF,8);
- }
- ///////////////////////////////////////////主机发送指令////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- void RS485_TX_Service(void)
- {
- Master_03_Slove(SlaverAddr,StartAddr,ValueOrLenth);
- }
- /////////////////////////////////////////////////////////////////////////////////////
- //RS485服务程序,用于处理接收到的数据(请在主函数中循环调用)
- void RS485_RX_Service(void)
- {
- u16 calCRC;
- u16 recCRC;
- if(RS485_RxFlag==1)
- {
- if(RS485_RX_BUFF[0]==SlaverAddr)//地址正确
- {
- if(RS485_RX_BUFF[1] == 0x03)//功能码正确
- {
- calCRC=CRC_Compute(RS485_RX_BUFF,RS485_RX_CNT-2);//计算所接收数据的CRC
- recCRC=RS485_RX_BUFF[RS485_RX_CNT-1]|(((u16)RS485_RX_BUFF[RS485_RX_CNT-2])<<8);//接收到的CRC(低字节在前,高字节在后)
- if(calCRC==recCRC)//CRC校验正确
- {
- /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- if(RS485_RX_BUFF[1] == 0x03)//0x03功能码进行处理
- {
- Modbus_03_Solve();
- }
- }
- else//CRC校验错误
- {
- ComErr=14;
-
- return ;
- }
- }
- else//功能码错误
- {
- /* if((RS485_RX_BUFF[1]==0x81)||(RS485_RX_BUFF[1]==0x82)||(RS485_RX_BUFF[1]==0x83)||(RS485_RX_BUFF[1]==0x85)||(RS485_RX_BUFF[1]==0x86)||(RS485_RX_BUFF[1]==0x8F)||(RS485_RX_BUFF[1]==0x90))
- {
- switch(RS485_RX_BUFF[2])
- {
- case 0x01:
- ComErr=11;break;
- case 0x02:
- ComErr=12;break;
- case 0x03:
- ComErr=13;break;
- case 0x04:
- ComErr=14;break;
- }
- TX_RX_SET=0; //命令完成
- }
- */
- TX_RX_SET=0; //命令完成
- return ;
- }
- }
- RS485_RxFlag=0;//复位帧结束标志
- RS485_RX_CNT=0;//接收计数器清零
- RS485_TX_EN = 1;//开启发送模式
- RS485_RX_EN = 1;
- TX_RX_SET=0; //命令完成
- }
- }
- //Modbus功能码03处理程序///////////////////////////////////////////////////////////////////////////////////////已验证程序OK
- //读保持寄存器
- void Modbus_03_Solve(void)
- {
- u8 i;
- u8 RegNum;
- RegNum= RS485_RX_BUFF[2]/2;//获取字节数 4/2=2
- if((StartAddr+RegNum)<1000)//寄存器地址+数量在范围内
- {
- for(i=0;i<RegNum;i++)
- {
- Master_ReadReg[StartAddr+i]= RS485_RX_BUFF[3+i*2]; /////////高8位
- Master_ReadReg[StartAddr+i]= RS485_RX_BUFF[4+i*2]+(Master_ReadReg[StartAddr+i]<<8);// 低8位+高8位 0x0? +0x00= 0x0?
-
- }
- wx = *Master_ReadReg;
- ComErr=0;
- //LED1 = 0;//////验证程序,验证主机是否接收到从机响应报文
- }
- else
- {
- ComErr=3;
- }
- TX_RX_SET=0; //命令完成
- }
复制代码
|
|