485_modbus通信,stm32f4.

本文通过实例介绍了如何使用STM32F407核心板配合USB转485模块,实现Modbus协议的通信,涉及CRC校验和数据的读写操作。通过发送和接收寄存器值,验证了0x06和0x03功能码的工作,0x05位状态暂未测试。
摘要由CSDN通过智能技术生成

实验目的

熟悉485通信;
熟悉modbus协议;
熟悉crc校验;

实验设备

stm32F407核心板
USB转485六合一;
485转TTL转接板;

实验资源:

单片机的485通信,用的是串口3和定时器4做监控;
串口1发送数据;
LED可以测试;

实验内容

在modbus调试助手中,发送写寄存器内容,给单片机,单片机接收到数据,再通过串口1发送到XCOM串口助手,显示数据,同时在modbus调试精灵中,读寄存器,读到设置的数值。
在这里插入图片描述
在这里插入图片描述
目前只测试了0x06和0x03的作用。
0x05位状态的,没有进行读取位状态测试。

模仿应该比较容易得到。

代码:

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "usart.h"

#include "KeyStatusMachineTimer3.h"
#include "RS485.h"
#include "CRC16ModBus.h"

uint8_t PWMduty=0;//pwm占空比

void Rs485Uart3Action(unsigned char *buf, unsigned char len);

int main(void)
{ 
	int i,t;
	uint8_t bufxxx[5]={0x3A,0x32,0x33,0x48,0x52};
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);  //初始化延时函数
	uart_init(115200);//初始化串口波特率为115200
	LED_Init();
	Keyii.KEY_IOInit();
	timer3.Timer3_Init(50-1,8400-1);//250---25ms;;;50---5ms
	Rs485ConfigUART3(9600);
 Rs485Timer4Init(50-1,8400-1);
 LED0=0;
	delay_ms(300);
	LED0=1;
	delay_ms(300);
	 LED0=0;
	delay_ms(300);
	LED0=1;
	delay_ms(300);
	 LED0=0;
	delay_ms(300);
	LED0=1;
	delay_ms(300);
	PWMduty=20;
	printf("PWMduty is set by PC =%d\r\n",PWMduty);
  PWMduty=0;
	delay_ms(2000);
   while(1) //实现比较值从0-300递增,到300后从300-0递减,循环
	{
		Keyii.KEY_Press();
		//测试485的发送字节和发送字符串功能
//		Rs485Usart3_SendByte(0x31);
//		Rs485Uart3Send_Enter();
//		delay_ms(1000);
//		Rs485Usart3_SendByte(0x41);
//		Rs485Uart3Send_Enter();
//		delay_ms(1000);
//		Rs485Uart3Write(bufxxx,5);
//		Rs485Uart3Send_Enter();
		
   //测试接收是否正常,将接收的数据在串口1发送出来
		Rs485Uart3Driver();

		
   }
}


/* 串口动作函数,根据接收到的命令帧执行响应的动作
   buf-接收到的命令帧指针,len-命令帧长度 */
void Rs485Uart3Action(unsigned char *buf, unsigned char len)
{
    unsigned char i;
    unsigned char cnt;
    unsigned char str[4];
    unsigned int  crc;
    unsigned char crch, crcl;
    
    if (buf[0] != RS485ModbusAddr) //本例中的本机地址设定为0x01,
    {                   //如数据帧中的地址字节与本机地址不符,
        return;         //则直接退出,即丢弃本帧数据不做任何处理
    }
    //地址相符时,再对本帧数据进行校验
    crc = GetCRC16(buf, len-2);  //计算CRC校验值
    crch = crc >> 8;
    crcl = crc & 0xFF;
    if ((buf[len-2]!=crch) || (buf[len-1]!=crcl))
    {
        return;   //如CRC校验不符时直接退出
    }
    //地址和校验字均相符后,解析功能码,执行相关操作
    switch (buf[1])
    {		
			 case ModbusReadReg:  //0x03读取一个或连续的寄存器
				 //buf2=ADDRH,buf3=ADDRL,buf4=numH,buf5=numL
			 //返回的buf2=字节长度,buf3=data1
            if ((buf[2]==0x00) && (buf[3]<=0x05)) //只支持0x0000~0x0005
            {
							//-----------------读取多个寄存器的数据
                if (buf[3] <= 0x04)//读取到的数据地址在0x00--0x04范围内
                {
                    i   = buf[3];      //提取寄存器地址,,,读取寄存器的起始地址
                    cnt = buf[5];    //提取待读取的寄存器数量,--读多少个寄存器
									
                    buf[2] = cnt*2;  //读取数据的字节数,为寄存器数*2
                    len = 3;         //帧前部已有地址、功能码、字节数共3个字节
									//在第三个字节后面补数据,将数据
                    while (cnt--)
                    {
                        buf[len++] = 0x00;          //寄存器高字节补0
                        buf[len++] = regGroup[i++]; //寄存器低字节
											//寄存器低字节,主要是因为读取多个寄存器,因此将该寄存器的内容数据,
											//保存到buflen中,然后进行发送给上位机
											//0x00+寄存器组里面的数据
                    }
                }
								//------------读取单个寄存器的数据
                else  //如果读取的是0x05的寄存器,读取蜂鸣器的状态,地址0x05为蜂鸣器状态
                {
									//buf0=设备地址
									//buf1=功能码
                    buf[2] = 2;  //读取数据的字节数长度,读取的是1个寄存器,
                    buf[3] = 0x00;//读取的是dataH
                    buf[4] = PWMduty;dataL,蜂鸣器启动标志flagBuzzOn,这里这个数据直接是0
                    len = 5;//后面插入CRC校验码
                }
                break;
            }
            else  //寄存器地址不被支持时,返回错误码
            {
                buf[1] = 0x83;  //功能码最高位置1
                buf[2] = 0x02;  //设置异常码为02-无效地址
                len = 3;//后面插入CRC地址
                break;
            }
						break;
				//写单个寄存器,接受和发送的一样
//发送:Addr--0x06--addrh--addrl--datah--datal--crc
//返回:Addr--0x06--addrh--addrl--datah--datal--crc						
        case ModbusWriteReg:  //写入单个寄存器0x06,接受上位机的数据
					//
					//地址的高位和地址的低位
            if ((buf[2]==ModbusRegAddrHPWM) && (buf[3]<=0x06)) //地址0x0000~0x0006分别对应
            {    
					   //提取数据	//PWM-LED亮度数值//验证地址									
							if(buf[3]==ModbusRegAddrLPWM)//PWM的地址
							{
								//buf4--接受到的数据是0,固定数据高位=0
								//将buf5数据给PWM的占空比的变量,用串口1将数据发送到电脑串口助手中		
							//	buf[4]=0x00;
						  	PWMduty=buf[5];
								//将数据,发送给串口1,检测是不是正常
								printf("PWMduty is set by PC =%d\r\n",PWMduty);//在串口1中,发送出去数据。
								delay_ms(1000);
							}
                len -= 2;  //长度-2以重新计算CRC并返回原帧
                break;
            }
            else  //寄存器地址不被支持时,返回错误码
            {
                buf[1] = 0x86;  //功能码最高位置1
                buf[2] = 0x02;  //设置异常码为02-无效地址
                len = 3;
                break;
            }
            
        default:  //其它不支持的功能码
            buf[1] |= 0x80;  //功能码最高位置1
            buf[2] = 0x01;   //设置异常码为01-无效功能
            len = 3;
            break;
    }
		//
    crc = GetCRC16(buf, len); //计算返回帧的CRC校验值
    buf[len++] = crc >> 8;    //CRC高字节
    buf[len++] = crc & 0xFF;  //CRC低字节
		printf("buflen-2=%d\r\n",buf[len-2]);
		printf("buflen-1=%d\r\n",buf[len-1]);
		printf("crc=%d\r\n",crc);
    Rs485Uart3Write(buf, len); //发送返回帧
}

RS485.h

#ifndef __RS485_H
#define __RS485_H
#include "sys.h"
#include "delay.h"

extern uint8_t regGroup[5];  //Modbus寄存器组,地址为0x00~0x04
extern uint8_t flag200ms;

//资源:定时器4,定时5ms,串口3,波特率可以设置
#define RS485ModbusAddr	0x01  //modbus地址

#define ModbusReadSingleBitState    0x01  //modbus地址
#define ModbusWriteSingleBitState	  0x05  //modbus地址
#define ModbusWriteManyBitState	    0x15 //modbus地址

#define ModbusReadReg	  0x03  //modbus地址
#define ModbusWriteReg	0x06  //modbus地址

#define ModbusRegAddrHPWM 0x00  //modbus控制PWM的占空比
#define ModbusRegAddrLPWM 0x01
//端口定义
#define RS485_DIR  PFout(10)  //RS485方向选择引脚,设置高低电平
//定时器Time3的设置,定时器5ms进行扫描
typedef enum
{
		Timer4_5ms=(uint16_t)1,
		Timer4_10ms=(uint16_t)2,
		Timer4_20ms=(uint16_t)4,
		Timer4_30ms=(uint16_t)6,
		Timer4_40ms=(uint16_t)8,
		Timer4_200ms=(uint16_t)40,//刷新屏幕用
		Timer4_1s=(uint16_t)200,		
}TIMER4_Value_t;
/*----------------------RS485收发端口的配置宏--------------*/
#define RS485Ctrl_PIN                 GPIO_Pin_10   		    // LED1 引脚      
#define RS485Ctrl_PIN_PORT            GPIOF                 // LED1 GPIO端口     
#define RS485Ctrl_PIN_CLK             RCC_AHB1Periph_GPIOF  // LED1 GPIO端口时钟_
#define RS485Ctrl_PIN_CLK_ENABLE()    {RCC_AHB1PeriphClockCmd(RS485Ctrl_PIN_CLK,ENABLE);} //使能GPID时钟
/*----------------------USART配置宏 ------------------------*/
#define UART3_TXD_PIN             GPIO_Pin_8   		    // LED1 引脚      
#define UART3_TXD_PORT            GPIOD                 // LED1 GPIO端口     
#define UART3_TXD_CLK             RCC_AHB1Periph_GPIOD  // LED1 GPIO端口时钟_
#define UART3_TXD_CLK_ENABLE()    {RCC_AHB1PeriphClockCmd(UART3_TXD_CLK,ENABLE);} //使能GPID时钟
#define UART3_TXD_PINSOURCE       GPIO_PinSource8

#define UART3_RXD_PIN             GPIO_Pin_9     		    // LED1 引脚      
#define UART3_RXD_PORT            GPIOD                 // LED1 GPIO端口     
#define UART3_RXD_CLK             RCC_AHB1Periph_GPIOD  // LED1 GPIO端口时钟_
#define UART3_RXD_CLK_ENABLE()    {RCC_AHB1PeriphClockCmd(UART3_RXD_CLK,ENABLE);} //使能GPIOD时钟
#define UART3_RXD_PINSOURCE       GPIO_PinSource9

#define UART3_CLK_ENABLE()       {RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);}
/*---------------------------------------*/
void Rs485ConfigUART3(unsigned int BaudRate);//初始化		 
void Rs485Usart3_SendByte(u8 byte);//发送一个字节
void Rs485Uart3Send_Enter();
void Rs485Uart3Write(unsigned char *buf, unsigned char len);
unsigned char Rs485Uart3Read(unsigned char *buf, unsigned char len);
void Rs485Uart3RxMonitor(unsigned char ms);
void Rs485Uart3Driver();
void Rs485Timer4Init(uint16_t arr,uint16_t psc);
unsigned int GetCRC16(unsigned char *ptr,  unsigned char len);

#endif

RS485.c(包括了MODBUS协议,串口3初始化,CRC代码等)

#include "RS485.h"
#include "sys.h"

uint8_t flagFrame = 0;  //帧接收完成标志,即接收到一帧新数据
uint8_t flagTxd = 0;    //单字节发送完成标志,用来替代TXD中断标志位
uint8_t cntRxd = 0;   //接收字节计数器
uint8_t bufRxd[64];  //接收字节缓冲区
uint8_t regGroup[5];  //Modbus寄存器组,地址为0x00~0x04

uint8_t flag200ms=0;
extern void Rs485Uart3Action(uint8_t *buf,uint8_t len);
/* 串口配置函数,baud-通信波特率 */
void Rs485ConfigUART3(unsigned int BaudRate)
{
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;	
	NVIC_InitTypeDef NVIC_InitStructure;	
	//F10
	RCC_AHB1PeriphClockCmd(RS485Ctrl_PIN_CLK, ENABLE);//使能GPIOF时钟
  GPIO_InitStructure.GPIO_Pin   = RS485Ctrl_PIN;
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;//上拉
  GPIO_Init(RS485Ctrl_PIN_PORT, &GPIO_InitStructure);//初始化
	
	UART3_TXD_CLK_ENABLE();
	//USART3端口配置TXD
  GPIO_InitStructure.GPIO_Pin = UART3_TXD_PIN;//GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(UART3_TXD_PORT,&GPIO_InitStructure); //初始化P
	
	UART3_RXD_CLK_ENABLE();
	//USART3端口配置RXD
  GPIO_InitStructure.GPIO_Pin = UART3_RXD_PIN;//GPIO
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(UART3_RXD_PORT,&GPIO_InitStructure); //初始化PA9,PA10
	
	//使能USART3时钟
	UART3_CLK_ENABLE();
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(UART3_TXD_PORT,UART3_TXD_PINSOURCE,GPIO_AF_USART3); //GPIOA9复用为USART1
	GPIO_PinAFConfig(UART3_RXD_PORT,UART3_RXD_PINSOURCE,GPIO_AF_USART3); //GPIOA10复用为USART1

   //USART3 初始化设置
	USART_InitStructure.USART_BaudRate = BaudRate;//波特率设置

	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
  USART_Init(USART3, &USART_InitStructure); //初始化串口3
	
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//设置中断分组2
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启相关中断
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; // 选择 USART1 通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级设置为3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //响应优先级设置为1
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //开启中断
	
	
  USART_Cmd(USART3, ENABLE);  //使能串口3 	
  NVIC_Init(&NVIC_InitStructure); //初始化配置
	RS485_DIR = 0; //RS485初始设置为接收方向


}

//----------------------------------------
void Rs485Usart3_SendByte(u8 byte)//发送一个字节
{
	  RS485_DIR = 1; delay_ms(1);   //RS485设置为发送
	  USART_SendData(USART3,byte);
    while(USART_GetFlagStatus(USART3,USART_FLAG_TC) == RESET);//发送完成标志位
	  RS485_DIR = 0;delay_ms(1);  
}
//===========================================
void Rs485Uart3Send_Enter()
{
	RS485_DIR = 1; delay_ms(1);   //RS485设置为发送
	Rs485Usart3_SendByte(0x0d); // 转义字符常量\r,ASCII码值(10进制)=13,光标移到本行行首
	Rs485Usart3_SendByte(0x0a); // 转义字符常量\n,ASCII码值(10进制)=10,光标移到下行行首
	RS485_DIR = 0;delay_ms(1);  
}
/* 串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度 */
void Rs485Uart3WriteArray(unsigned char *buf, unsigned char len)
{
		uint16_t i=0;
    RS485_DIR = 1;  delay_ms(1);  //RS485设置为发送
		for(i=0;i<len;i++)
		{
			Rs485Usart3_SendByte(*(buf+i)); 	
		}
    delay_ms(1);  //等待最后的停止位完成,延时时间由波特率决定
    RS485_DIR = 0;  //RS485设置为接收
}
/* 串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度 */
void Rs485Uart3WriteString(unsigned char *buf)
{
		uint16_t i=0;
    RS485_DIR = 1; delay_ms(1);   //RS485设置为发送
		while (*buf)   //循环发送所有字节
    {   
			  delay_us(2);  
		  	Rs485Usart3_SendByte(*(buf++));   
    }
    delay_ms(1);  //等待最后的停止位完成,延时时间由波特率决定
    RS485_DIR = 0;  //RS485设置为接收
}

/* 串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度 */
void Rs485Uart3Write(unsigned char *buf, unsigned char len)
{
    RS485_DIR = 1; delay_ms(1);   //RS485设置为发送
    while (len--)   //循环发送所有字节
    {   
			  delay_us(2);  
		  	Rs485Usart3_SendByte(*buf++);   
    }
    delay_us(50);  //等待最后的停止位完成,延时时间由波特率决定
    RS485_DIR = 0;delay_ms(1);    //RS485设置为接收
}
/* 串口数据读取函数,buf-接收指针,len-指定的读取长度,返回值-实际读到的长度 */
unsigned char Rs485Uart3Read(unsigned char *buf, unsigned char len)
{
    unsigned char i;
    
    if (len > cntRxd)  //指定读取长度大于实际接收到的数据长度时,
    {                  //读取长度设置为实际接收到的数据长度
        len = cntRxd;
    }
    for (i=0; i<len; i++)  //拷贝接收到的数据到接收指针上
    {
        *buf++ = bufRxd[i];
    }
    cntRxd = 0;  //接收计数器清零
    
    return len;  //返回实际读取长度
}
/* 串口接收监控,由空闲时间判定帧结束,需在定时中断中调用,ms-定时间隔 */
void Rs485Uart3RxMonitor(unsigned char ms)
{
    static unsigned char cntbkp = 0;
    static unsigned char idletmr = 0;

    if (cntRxd > 0)  //接收计数器大于零时,监控总线空闲时间
    {
        if (cntbkp != cntRxd)  //接收计数器改变,即刚接收到数据时,清零空闲计时
        {
            cntbkp = cntRxd;
            idletmr = 0;
        }
        else                   //接收计数器未改变,即总线空闲时,累积空闲时间
        {
            if (idletmr < 30)  //空闲计时小于30ms时,持续累加
            {
                idletmr += ms;//5*6=30
                if (idletmr >= 6)  //空闲时间达到30ms时,即判定为一帧接收完毕
                {
                    flagFrame = 1;  //设置帧接收完成标志
                }
            }
        }
    }
    else
    {
        cntbkp = 0;
    }
}
/* 串口驱动函数,监测数据帧的接收,调度功能函数,需在主循环中调用 */
void Rs485Uart3Driver()
{
    unsigned char len;
    unsigned char buf[40];

    if (flagFrame) //有命令到达时,读取处理该命令
    {
        flagFrame = 0;
        len = Rs485Uart3Read(buf, sizeof(buf)-2); //将接收到的命令读取到缓冲区中
   //     Rs485Uart3Write(buf,len);

  			Rs485Uart3Action(buf, len);  //传递数据帧,调用动作执行函数
    }
}
/* 串口中断服务函数 */
void USART3_IRQHandler(void)
{
	 static uint8_t Res;
    if (USART3->SR & USART_SR_RXNE)        // 如果是RXNE中断	 if the	interrupt source is RXNE
    { 
			Res =USART_ReceiveData(USART3);
			if (cntRxd < sizeof(bufRxd)) //接收缓冲区尚未用完时,
        {                            //保存接收字节,并递增计数器
            bufRxd[cntRxd++] = Res;
        }
    }

}

//-------------------------//定时5ms
void Rs485Timer4Init(uint16_t arr,uint16_t psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;	
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);  	//=====TIM8时钟使能 	

  TIM_TimeBaseInitStructure.TIM_Period = arr; 	//自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  //定时器分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;// 重复计数器的值,没用到不用管
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);//初始化TIM8
	
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //允许定时器8更新中断  
  TIM_ClearFlag(TIM4,TIM_FLAG_Update);
	TIM_Cmd(TIM4,ENABLE); //定时器8
	
  //确定定时器8的中断优先级,
  //		//定时器8中断配置
	NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn; //定时器8更新中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x00; //抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; //子优先级01
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}


void TIM4_IRQHandler(void)
{
	static int count00=0;
  if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {  
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
			  Rs485Uart3RxMonitor(5);  //串口接收监控
			
 			if(count00>=Timer4_200ms)//125/5=25
				{
					LED1=!LED1;
					flag200ms=1;//主函数
					count00=0;
				}
    } 
		  
}

unsigned int GetCRC16(unsigned char *ptr,  unsigned char len)
{ 
    unsigned int index;
    unsigned char crch = 0xFF;  //高CRC字节
    unsigned char crcl = 0xFF;  //低CRC字节
    unsigned char  TabH[] = {  //CRC高位字节值表
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40  
    } ;  
    unsigned char  TabL[] = {  //CRC低位字节值表
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,  
        0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,  
        0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,  
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,  
        0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,  
        0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,  
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,  
        0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,  
        0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,  
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,  
        0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,  
        0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,  
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,  
        0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,  
        0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,  
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,  
        0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,  
        0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,  
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,  
        0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,  
        0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,  
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,  
        0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,  
        0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,  
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,  
        0x43, 0x83, 0x41, 0x81, 0x80, 0x40  
    } ;
 
    while (len--)  //计算指定长度的CRC
    {
        index = crch ^ *ptr++;
        crch = crcl ^ TabH[index];
        crcl = TabL[index];
    }
    
    return ((crch<<8) | crcl);  
}


LED和状态机按键,在另外e be 博文中。

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值