# STM32--CAN+RS485+ESP8266 **Repository Path**: aizizai/stm32--can--rs485--esp8266 ## Basic Information - **Project Name**: STM32--CAN+RS485+ESP8266 - **Description**: 熟悉CAN,RS485,ESP8266通信协议,使用STM32进行简单通信. - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 6 - **Created**: 2023-08-30 - **Last Updated**: 2023-08-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # STM32--CAN+RS485+ESP8266通信示例 ## 1.简介 熟悉CAN,RS485,ESP8266通信协议,使用STM32进行简单通信. ## 2.系统框图: ![](http://www.rabidhare.com/wp-content/uploads/2021/06/1.png) ## 3. 编程语言和环境 **编程语言**:C语言 **编译环境**:Keil5 **开发板**:STM32F407ZGT6 **STM32库:**标准库,版本:STM32F4xx_DFP 1.0.8.pack **实时操作系统:**UCOSIII **通信协议**:USART,CAN,RS485,TCP/IP ## 4.实现功能 CAN端通信:实现通过按键控制板载LED和BEEP 和 上传数据 RS485:实现通过按键控制板载LED和BEEP 和 上传数据 ESP8266:上传数据到TCP服务器和接受TCP服务器下发指令 LED和BEEP简单驱动 **功能描述:** **中心主控:** ​ 1.通过KEY1进行模式切换:模式1:RS485通信 模式2:CAN通信 ​ 在模式1下: ​ 按键向RS485从机发送指令 ​ 在模式2下: ​ 按键向CAN从机发送指令 ​ 2.接受CAN从机和RS485从机的数据 ​ 3.通过ESP8266连接到TCP服务器,发送数据和接受服务器指令 ​ 4.数据采集 **左端CAN主机:** ​ 1.按键控制中心主控板板载LED和蜂鸣器 ​ 2.数据采集 ​ 3.上传传感器采集的数据 **右端CAN主机:** ​ 1.按键控制中心主控板板载LED和蜂鸣器 ​ 2.数据采集 ​ 3.上传传感器采集的数据 ## 5.ESP866编程 ### **5.1 ESP8266.c** ```c //单片机头文件 #include "stm32f4xx.h" //网络设备驱动 #include "esp8266.h" //硬件驱动 #include "delay.h" #include "usart.h" //C库 #include #include #define ESP8266_WIFI_INFO "AT+CWJAP=\"###\",\"88888888\"\r\n" #define ESP8266_ONENET_INFO "AT+CIPSTART=\"TCP\",\"192.168.137.1\",8000\r\n" static void ESP8266_GPIO_Config(void); unsigned char esp8266_buf[128]; unsigned short esp8266_cnt = 0, esp8266_cntPre = 0; unsigned char recvFlag = 0; //========================================================== // 函数名称: ESP8266_GPIO_Config // // 函数功能: 配置CH_PD使能引脚 和 RST复位引脚 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; ESP8266_RST_APBxClock_FUN ( ESP8266_RST_CLK, ENABLE); //ESP8266复位引脚 GPIO_InitStructure.GPIO_Pin = ESP8266_RST_PIN; 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(ESP8266_RST_PORT, &GPIO_InitStructure); //ESP8266_CH_PD ESP8266_CH_PD_APBxClock_FUN ( ESP8266_CH_PD_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = ESP8266_CH_PD_PIN; GPIO_Init(ESP8266_CH_PD_PORT, &GPIO_InitStructure); } //========================================================== // 函数名称: ESP8266_Clear // // 函数功能: 清空缓存 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_Clear(void) { memset(esp8266_buf, 0, sizeof(esp8266_buf)); esp8266_cnt = 0; } //========================================================== // 函数名称: ESP8266_WaitRecive // // 函数功能: 等待接收完成 // // 入口参数: 无 // // 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成 // // 说明: 循环调用检测是否接收完成 //========================================================== _Bool ESP8266_WaitRecive(void) { if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数 return REV_WAIT; if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕 { esp8266_cnt = 0; //清0接收计数 return REV_OK; //返回接收完成标志 } esp8266_cntPre = esp8266_cnt; //置为相同 return REV_WAIT; //返回接收未完成标志 } //========================================================== // 函数名称: ESP8266_SendCmd // // 函数功能: 发送命令 // // 入口参数: cmd:命令 // res:需要检查的返回指令 // // 返回参数: 0-成功 1-失败 // // 说明: //========================================================== _Bool ESP8266_SendCmd(char *cmd, char *res) { unsigned char timeOut = 200; Usart_SendString(UART5, (unsigned char *)cmd, strlen((const char *)cmd)); while(timeOut--) { if(ESP8266_WaitRecive() == REV_OK) //如果收到数据 { if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词 { ESP8266_Clear(); //清空缓存 return 0; } } delay_ms(10); } return 1; } //========================================================== // 函数名称: ESP8266_SendData // // 函数功能: 发送数据 // // 入口参数: data:数据 // len:长度 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_SendData(unsigned char *data, unsigned short len) { char cmdBuf[32]; ESP8266_Clear(); //清空接收缓存 sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len); //发送命令 if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’时可以发送数据 { Usart_SendString(UART5, data, len); //发送设备连接请求数据 } } //========================================================== // 函数名称: ESP8266_GetIPD // // 函数功能: 获取平台返回的数据 // // 入口参数: 等待的时间(乘以10ms) // // 返回参数: 平台返回的原始数据 // // 说明: 不同网络设备返回的格式不同,需要去调试 // 如ESP8266的返回格式为 "+IPD,x:yyy" x代表数据长度,yyy是数据内容 //========================================================== unsigned char *ESP8266_GetIPD(unsigned short timeOut) { char *ptrIPD = NULL; do { if(ESP8266_WaitRecive() == REV_OK) //如果接收完成 { ptrIPD = strstr((char *)esp8266_buf, "IPD,"); //搜索“IPD”头 if(ptrIPD == NULL) //如果没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间 { //UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n"); } else { ptrIPD = strchr(ptrIPD, ':'); //找到':' if(ptrIPD != NULL) { ptrIPD++; return (unsigned char *)(ptrIPD); } else return NULL; } } delay_ms(5); //延时等待 } while(timeOut--); return NULL; //超时还未找到,返回空指针 } //========================================================== // 函数名称: ESP8266_Init // // 函数功能: 初始化ESP8266 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_Init(void) { ESP8266_GPIO_Config(); ESP8266_RST_LOW_LEVEL(); delay_ms(250); ESP8266_RST_HIGH_LEVEL(); delay_ms(500); ESP8266_CH_ENABLE(); ESP8266_Clear(); UsartPrintf(USART_DEBUG, "0. AT\r\n"); while(ESP8266_SendCmd("AT\r\n", "OK")) delay_ms(500); UsartPrintf(USART_DEBUG, "1. RST\r\n"); ESP8266_RST_LOW_LEVEL(); delay_ms(500); ESP8266_RST_HIGH_LEVEL(); UsartPrintf(USART_DEBUG, "2. CWMODE\r\n"); while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK")) delay_ms(500); UsartPrintf(USART_DEBUG, " WF-ESP8266模块为:1- Station模式;\r\n"); UsartPrintf(USART_DEBUG, "3. AT+CWDHCP\r\n"); while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK")) delay_ms(500); UsartPrintf(USART_DEBUG, " AT+CWDHCP=1,1\r\n"); UsartPrintf(USART_DEBUG, "4. CWJAP\r\n"); while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP")) delay_ms(500); UsartPrintf(USART_DEBUG, "%s\r\n",ESP8266_WIFI_INFO); UsartPrintf(USART_DEBUG, "5. CIPSTART\r\n"); while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT")) delay_ms(500); UsartPrintf(USART_DEBUG, "%s\r\n",ESP8266_ONENET_INFO); UsartPrintf(USART_DEBUG, "6. ESP8266 Init OK\r\n"); } //Uart5 RX PD2 void Uart5_Init(u32 baud) { //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE); //使能GPIOC时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE); //使能GPIOD时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);//使能USART1时钟 //串口1对应引脚复用映射 GPIO_PinAFConfig(GPIOC,GPIO_PinSource12,GPIO_AF_UART5); //GPIOC12复用为UART5 GPIO_PinAFConfig(GPIOD,GPIO_PinSource2,GPIO_AF_UART5); //GPIOD2复用为UART5 //USART1端口配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //GPIOC12 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(GPIOC,&GPIO_InitStructure); //初始化PC12 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //GPIOD2 GPIO_Init(GPIOD,&GPIO_InitStructure); //初始化PD2 //UART5 初始化设置 USART_InitStructure.USART_BaudRate = baud;//波特率设置 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(UART5, &USART_InitStructure); //初始化串口1 USART_Cmd(UART5, ENABLE); //使能串口1 USART_ClearFlag(UART5, USART_FLAG_TC); USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);//开启相关中断 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;//串口5中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、 } /* ************************************************************ * 函数名称: Usart_SendString * * 函数功能: 串口数据发送 * * 入口参数: USARTx:串口组 * str:要发送的数据 * len:数据长度 * * 返回参数: 无 * * 说明: ************************************************************ */ void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len) { unsigned short count = 0; for(; count < len; count++) { USART_SendData(USARTx, *str++); //发送数据 while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //等待发送完成 } } /* ************************************************************ * 函数名称: UsartPrintf * * 函数功能: 格式化打印 * * 入口参数: USARTx:串口组 * fmt:不定长参 * * 返回参数: 无 * * 说明: ************************************************************ */ void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...) { unsigned char UsartPrintfBuf[296]; va_list ap; unsigned char *pStr = UsartPrintfBuf; va_start(ap, fmt); vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化 va_end(ap); while(*pStr != 0) { USART_SendData(USARTx, *pStr++); while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); } } //========================================================== // 函数名称: UART5_IRQHandler // // 函数功能: 串口5收发中断 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void UART5_IRQHandler(void) { if(USART_GetITStatus(UART5, USART_IT_RXNE) != RESET) //接收中断 { if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆 esp8266_buf[esp8266_cnt++] = UART5->DR; USART_ClearFlag(UART5, USART_FLAG_RXNE); } } ``` ### 5.2 **ESP8266.h** ```c #ifndef _ESP8266_H_ #define _ESP8266_H_ #include "stdio.h" #include "stdarg.h" #define REV_OK 0 //接收完成标志 #define REV_WAIT 1 //接收未完成标志 #define ESP8266_CH_PD_APBxClock_FUN RCC_AHB1PeriphClockCmd #define ESP8266_CH_PD_CLK RCC_AHB1Periph_GPIOE #define ESP8266_CH_PD_PORT GPIOE #define ESP8266_CH_PD_PIN GPIO_Pin_11 #define ESP8266_RST_APBxClock_FUN RCC_AHB1PeriphClockCmd #define ESP8266_RST_CLK RCC_AHB1Periph_GPIOE #define ESP8266_RST_PORT GPIOE #define ESP8266_RST_PIN GPIO_Pin_8 #define ESP8266_CH_ENABLE() GPIO_SetBits ( ESP8266_CH_PD_PORT, ESP8266_CH_PD_PIN ) #define ESP8266_CH_DISABLE() GPIO_ResetBits ( ESP8266_CH_PD_PORT, ESP8266_CH_PD_PIN ) #define ESP8266_RST_HIGH_LEVEL() GPIO_SetBits ( ESP8266_RST_PORT, ESP8266_RST_PIN ) #define ESP8266_RST_LOW_LEVEL() GPIO_ResetBits ( ESP8266_RST_PORT, ESP8266_RST_PIN ) void ESP8266_Init(void); void ESP8266_Clear(void); void ESP8266_SendData(unsigned char *data, unsigned short len); unsigned char *ESP8266_GetIPD(unsigned short timeOut); void Uart5_Init(u32 baud); void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len); void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...); #endif ``` ### 5.3 cmd.c **获取网络调试助手和串口调试助手发来的信息,解析控制** ```c #include "cmd.h" #include #include #include #include #include "led.h" #include "beep.h" /** * @brief 获取网络调试助手和串口调试助手发来的信息 * @param 如ESP8266的返回格式为 "+IPD,x:yyy" x代表数据长度,yyy是数据内容 * @retval 无 */ void Get_ESP82666_Cmd(unsigned char * cmd ) { uint8_t i=0; char *p; // 数据发送控制命令 +IPD,x:板子编号LED-0-ON# if(strstr((char *)cmd,"01LED")) { //以等号分割字符串 strtok((char *)cmd,":-#"); //+IPD,x p=strtok(NULL,":-#"); // 01LED //获取状态 p=strtok(NULL,"-#"); i = atoi(p); if(i==0) { p=strtok(NULL,"-#"); //ON OFF if(strcmp(p, "OFF") == 0) { LED0 = 1; } if(strcmp(p, "ON") == 0) { LED0 = 0; } } if(i==1) { p=strtok(NULL,"-#"); if(strcmp(p, "OFF") == 0) { LED1 = 1; } if(strcmp(p, "ON") == 0) { LED1 = 0; } } if(i==2) { p=strtok(NULL,"-#"); if(strcmp(p, "OFF") == 0) { LED2 = 1; } if(strcmp(p, "ON") == 0) { LED2 = 0; } } } // 数据发送控制命令 +IPD,x:板子编号BEEP-ON# //示例:BEEP-ON# BEEP-OFF# BEEP ON //蜂鸣器控制 if(strstr((char *)cmd,"01BEEP")) { //以等号分割字符串 strtok((char *)cmd,":-#"); //+IPD,x p=strtok(NULL,":-#"); //01BEEP //获取状态 p=strtok(NULL,":-#"); if(strcmp(p, "ON") == 0) { BEEP = 1; } if(strcmp(p, "OFF") == 0) { BEEP = 0; } } } ``` ```c #ifndef _CMD_H_ #define _CMD_H_ void Get_ESP82666_Cmd(unsigned char * cmd ); #endif ``` ## 6.rs485编程 ### **6.1rs485.c** ```c #include "rs485.h" #include "string.h" #include "stdlib.h" #include "includes.h" //ucos 使用 #include "usart.h" #include "led.h" #include "beep.h" //接收缓存区 u8 RS485_receive_str[512]; //接收缓冲,最大128个字节. u8 uart_byte_count=0; //接收到的数据长度 u8 num; //初始化IO 串口2 bound:波特率 void RS485_Init(u32 bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟 //串口2引脚复用映射 GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //GPIOA2复用为USART2 GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); //GPIOA3复用为USART2 //USART2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //GPIOA2与GPIOA3 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA2,PA3 //PG8推挽输出,485模式控制 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOG6 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOG,&GPIO_InitStructure); //初始化PG8 RS485_TX_EN=0; //初始化默认为接收模式 //USART2 初始化设置 USART_InitStructure.USART_BaudRate = bound;//波特率设置 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(USART2, &USART_InitStructure); //初始化串口2 USART_Cmd(USART2, ENABLE); //使能串口 2 USART_ClearFlag(USART2, USART_FLAG_TC); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启接受中断 //Usart2 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、 } //串口2接收中断服务函数 void USART2_IRQHandler(void) { u8 rec_data; //进入中断 OSIntEnter(); if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)//接收到数据 { rec_data =(u8)USART_ReceiveData(USART2); //(USART2->DR) 读取接收到的数据 if(rec_data=='$') //如果是S,表示是命令信息的起始位 { uart_byte_count=0x01; } else if(rec_data=='#') //如果E,表示是命令信息传送的结束位 { //printf("%s\r\n",RS485_receive_str); if(strcmp("LED0",(char *)RS485_receive_str)==0) { num++; if (num == 2) { num =0; } if(num == 1) { LED0 = 1; } if(num == 0) { LED0 = 0; } } else if(strcmp("LED1",(char *)RS485_receive_str)==0) { num++; if (num == 2) { num =0; } if(num == 1) { LED1 = 1; } if(num == 0) { LED1 = 0; } } else if(strcmp("LED2",(char *)RS485_receive_str)==0) { num++; if (num == 2) { num =0; } if(num == 1) { LED2 = 1; } if(num == 0) { LED2 = 0; } } else if(strcmp("BEEP",(char *)RS485_receive_str)==0) { num++; if (num == 2) { num =0; } if(num == 1) { BEEP = 1; //蜂鸣器响 } if(num == 0) { BEEP = 0; //蜂鸣器不响 } } for(uart_byte_count=0;uart_byte_count<32;uart_byte_count++) RS485_receive_str[uart_byte_count]=0x00; uart_byte_count=0; } else if((uart_byte_count>0)&&(uart_byte_count<=128)) { RS485_receive_str[uart_byte_count-1]=rec_data; uart_byte_count++; } } //退出中断 OSIntExit(); } /**************************************************************************** * 名 称: void RS485_Send_Data(u8 *buf,u8 len) * 功 能:RS485发送len个字节 * 入口参数:buf:发送区首地址 len:发送的字节数 * 返回参数:无 * 说 明:(为了和本代码的接收匹配,这里建议数据长度不要超过512个字节) ****************************************************************************/ void RS485_Send_Data(u8 *buf,u8 len) { u8 t; RS485_TX_EN=1; //设置为发送模式 for(t=0;tDR) 读取接收到的数据 if(rec_data=='$') //如果是S,表示是命令信息的起始位 { uart_byte_count=0x01; } else if(rec_data=='#') //如果E,表示是命令信息传送的结束位 { //printf("%s\r\n",RS485_receive_str); if(strcmp("LED0",(char *)RS485_receive_str)==0) { num++; if (num == 2) { num =0; } if(num == 1) { LED0 = 1; } if(num == 0) { LED0 = 0; } } else if(strcmp("LED1",(char *)RS485_receive_str)==0) { num++; if (num == 2) { num =0; } if(num == 1) { LED1 = 1; } if(num == 0) { LED1 = 0; } } else if(strcmp("LED2",(char *)RS485_receive_str)==0) { num++; if (num == 2) { num =0; } if(num == 1) { LED2 = 1; } if(num == 0) { LED2 = 0; } } else if(strcmp("BEEP",(char *)RS485_receive_str)==0) { num++; if (num == 2) { num =0; } if(num == 1) { BEEP = 1; //蜂鸣器响 } if(num == 0) { BEEP = 0; //蜂鸣器不响 } } for(uart_byte_count=0;uart_byte_count<32;uart_byte_count++) RS485_receive_str[uart_byte_count]=0x00; uart_byte_count=0; } else if((uart_byte_count>0)&&(uart_byte_count<=128)) { RS485_receive_str[uart_byte_count-1]=rec_data; uart_byte_count++; } } //退出中断 OSIntExit(); } ``` ## 7.CAN编程 ### 7.1can.c ```c #include "bsp_can.h" #include "includes.h" //ucos 使用 extern __IO uint32_t flag ; //用于标志是否接收到数据,在中断函数中赋值 extern CanRxMsg RxMessage; //接收缓冲区 /// 不精确的延时 static void can_delay(__IO u32 nCount) { for(; nCount != 0; nCount--); } /* * 函数名:CAN_GPIO_Config * 描述 :CAN的GPIO 配置 * 输入 :无 * 输出 : 无 * 调用 :内部调用 */ static void CAN_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /* Enable GPIO clock */ RCC_AHB1PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE); /* Connect CAN pins to AF9 */ GPIO_PinAFConfig(CAN_TX_GPIO_PORT, CAN_RX_SOURCE, CAN_AF_PORT); GPIO_PinAFConfig(CAN_RX_GPIO_PORT, CAN_TX_SOURCE, CAN_AF_PORT); /* Configure CAN TX pins */ GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure); /* Configure CAN RX pins */ GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure); } /* * 函数名:CAN_NVIC_Config * 描述 :CAN的NVIC 配置,第1优先级组,0,0优先级 * 输入 :无 * 输出 : 无 * 调用 :内部调用 */ static void CAN_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Configure one bit for preemption priority */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /*中断设置*/ NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ; //CAN RX0中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级为0 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /* * 函数名:CAN_Mode_Config * 描述 :CAN的模式 配置 * 输入 :无 * 输出 : 无 * 调用 :内部调用 */ static void CAN_Mode_Config(void) { CAN_InitTypeDef CAN_InitStructure; /************************CAN通信参数设置**********************************/ /* Enable CAN clock */ RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE); /*CAN寄存器初始化*/ CAN_DeInit(CANx); CAN_StructInit(&CAN_InitStructure); /*CAN单元初始化*/ CAN_InitStructure.CAN_TTCM=DISABLE; //MCR-TTCM 关闭时间触发通信模式使能 CAN_InitStructure.CAN_ABOM=ENABLE; //MCR-ABOM 自动离线管理 CAN_InitStructure.CAN_AWUM=ENABLE; //MCR-AWUM 使用自动唤醒模式 CAN_InitStructure.CAN_NART=DISABLE; //MCR-NART 禁止报文自动重传 DISABLE-自动重传 CAN_InitStructure.CAN_RFLM=DISABLE; //MCR-RFLM 接收FIFO 锁定模式 DISABLE-溢出时新报文会覆盖原有报文 CAN_InitStructure.CAN_TXFP=DISABLE; //MCR-TXFP 发送FIFO优先级 DISABLE-优先级取决于报文标示符 CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //回环工作模式 CAN_Mode_LoopBack CAN_Mode_Normal CAN_InitStructure.CAN_SJW=CAN_SJW_2tq; //BTR-SJW 重新同步跳跃宽度 2个时间单元 /* ss=1 bs1=4 bs2=2 位时间宽度为(1+4+2) 波特率即为时钟周期tq*(1+4+2) */ CAN_InitStructure.CAN_BS1=CAN_BS1_4tq; //BTR-TS1 时间段1 占用了4个时间单元 CAN_InitStructure.CAN_BS2=CAN_BS2_2tq; //BTR-TS1 时间段2 占用了2个时间单元 /* CAN Baudrate = 1 MBps (1MBps已为stm32的CAN最高速率) (CAN 时钟频率为 APB 1 = 42 MHz) */ CAN_InitStructure.CAN_Prescaler =6; ////BTR-BRP 波特率分频器 定义了时间单元的时间长度 42/(1+4+2)/6=1 Mbps CAN_Init(CANx, &CAN_InitStructure); } /* * 函数名:CAN_Filter_Config * 描述 :CAN的过滤器 配置 * 输入 :无 * 输出 : 无 * 调用 :内部调用 */ static void CAN_Filter_Config(void) { CAN_FilterInitTypeDef CAN_FilterInitStructure; /*CAN筛选器初始化*/ CAN_FilterInitStructure.CAN_FilterNumber=14; //筛选器组14 CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //工作在掩码模式 CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //筛选器位宽为单个32位。 /* 使能筛选器,按照标志的内容进行比对筛选,扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。 */ CAN_FilterInitStructure.CAN_FilterIdHigh= ((((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16; //要筛选的ID高位 CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要筛选的ID低位 CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF; //筛选器高16位每位必须匹配 CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF; //筛选器低16位每位必须匹配 CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ; //筛选器被关联到FIFO0 CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能筛选器 CAN_FilterInit(&CAN_FilterInitStructure); /*CAN通信中断使能*/ CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE); } /* * 函数名:CAN_Config * 描述 :完整配置CAN的功能 * 输入 :无 * 输出 : 无 * 调用 :外部调用 */ void CAN_Config(void) { CAN_GPIO_Config(); CAN_NVIC_Config(); CAN_Mode_Config(); CAN_Filter_Config(); } /** * @brief 初始化 Rx Message数据结构体 * @param RxMessage: 指向要初始化的数据结构体 * @retval None */ void Init_RxMes(CanRxMsg *RxMessage) { uint8_t ubCounter = 0; /*把接收结构体清零*/ RxMessage->StdId = 0x00; RxMessage->ExtId = 0x00; RxMessage->IDE = CAN_ID_STD; RxMessage->DLC = 0; RxMessage->FMI = 0; for (ubCounter = 0; ubCounter < 8; ubCounter++) { RxMessage->Data[ubCounter] = 0x00; } } /* * 函数名:CAN_SetMsg * 描述 :CAN通信报文内容设置,设置一个数据内容为0-7的数据包 * 输入 :发送报文结构体 * 输出 : 无 * 调用 :外部调用 */ // len <8 void CAN_SetMsg(CanTxMsg *TxMessage,uint8_t *buf,uint8_t len) { uint8_t ubCounter = 0; //TxMessage.StdId=0x00; TxMessage->ExtId=0x1314; //使用的扩展ID TxMessage->IDE=CAN_ID_EXT; //扩展模式 TxMessage->RTR=CAN_RTR_DATA; //发送的是数据 TxMessage->DLC=8; //数据长度为8字节 /*设置要发送的数据0-7*/ for (ubCounter = 0; ubCounter < len; ubCounter++) { TxMessage->Data[ubCounter] = buf[ubCounter]; } } // len <= 8 void CAN_SendMsg(CanTxMsg *TxMessage,uint8_t *buf,uint8_t len) { CAN_SetMsg(TxMessage,buf,len); CAN_Transmit(CANx, TxMessage); can_delay(10000);//等待发送完毕,可使用CAN_TransmitStatus查看状态 } void CAN_RX_IRQHandler(void) { //进入中断 OSIntEnter(); /*从邮箱中读出报文*/ CAN_Receive(CANx, CAN_FIFO0, &RxMessage); /* 比较ID是否为0x1314 */ if((RxMessage.ExtId==0x1314) && (RxMessage.IDE==CAN_ID_EXT) && (RxMessage.DLC==8) ) { flag = 1; //接收成功 } else { flag = 0; //接收失败 } //退出中断 OSIntExit(); } /**************************END OF FILE************************************/ ``` ### 7.2can.h ```c #ifndef __CAN_H #define __CAN_H #include "stm32f4xx.h" #include #define CANx CAN2 #define CAN_CLK RCC_APB1Periph_CAN1 |RCC_APB1Periph_CAN2 #define CAN_RX_IRQ CAN2_RX0_IRQn #define CAN_RX_IRQHandler CAN2_RX0_IRQHandler #define CAN_RX_PIN GPIO_Pin_12 #define CAN_TX_PIN GPIO_Pin_13 #define CAN_TX_GPIO_PORT GPIOB #define CAN_RX_GPIO_PORT GPIOB #define CAN_TX_GPIO_CLK RCC_AHB1Periph_GPIOB #define CAN_RX_GPIO_CLK RCC_AHB1Periph_GPIOB #define CAN_AF_PORT GPIO_AF_CAN2 #define CAN_RX_SOURCE GPIO_PinSource12 #define CAN_TX_SOURCE GPIO_PinSource13 /*debug*/ #define CAN_DEBUG_ON 1 #define CAN_DEBUG_ARRAY_ON 1 #define CAN_DEBUG_FUNC_ON 1 // Log define #define CAN_INFO(fmt,arg...) printf("<<-CAN-INFO->> "fmt"\n",##arg) #define CAN_ERROR(fmt,arg...) printf("<<-CAN-ERROR->> "fmt"\n",##arg) #define CAN_DEBUG(fmt,arg...) do{\ if(CAN_DEBUG_ON)\ printf("<<-CAN-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\ }while(0) #define CAN_DEBUG_ARRAY(array, num) do{\ int32_t i;\ uint8_t* a = array;\ if(CAN_DEBUG_ARRAY_ON)\ {\ printf("<<-CAN-DEBUG-ARRAY->>\n");\ for (i = 0; i < (num); i++)\ {\ printf("%02x ", (a)[i]);\ if ((i + 1 ) %10 == 0)\ {\ printf("\n");\ }\ }\ printf("\n");\ }\ }while(0) #define CAN_DEBUG_FUNC() do{\ if(CAN_DEBUG_FUNC_ON)\ printf("<<-CAN-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\ }while(0) static void CAN_GPIO_Config(void); static void CAN_NVIC_Config(void); static void CAN_Mode_Config(void); static void CAN_Filter_Config(void); void CAN_Config(void); void CAN_SetMsg(CanTxMsg *TxMessage,uint8_t *buf,uint8_t len); void Init_RxMes(CanRxMsg *RxMessage); void CAN_SendMsg(CanTxMsg *TxMessage,uint8_t *buf,uint8_t len); #endif ``` ### 7.3can数据采集 ```c if(flag==1) { BEEP = 1; //鸣叫提示接入成功 delay_ms(250); BEEP = 0; printf("\r\nCAN接收到数据:\r\n"); printf("%s\r\n",RxMessage.Data); CAN_DEBUG_ARRAY(RxMessage.Data,8); if(strcmp("LED0_OFF",(char *)(RxMessage.Data))==0) { LED0 = 1; } if(strcmp("LED0__ON",(char *)(RxMessage.Data))==0) { LED0 = 0; } if(strcmp("LED1_OFF",(char *)(RxMessage.Data))==0) { LED1 = 1; } if(strcmp("LED1__ON",(char *)(RxMessage.Data))==0) { LED1 = 0; } if(strcmp("LED2_OFF",(char *)(RxMessage.Data))==0) { LED2 = 1; } if(strcmp("LED2__ON",(char *)(RxMessage.Data))==0) { LED2 = 0; } if(strcmp("BEEP_OFF",(char *)(RxMessage.Data))==0) { BEEP = 0; } if(strcmp("BEEP__ON",(char *)(RxMessage.Data))==0) { BEEP = 1; } flag=0; } ``` ## 8.按键控制 ```c void task3(void *parg) { uint8_t RS485Flag = 1; uint8_t CANFlag = 0; uint8_t key1flag = 0; uint8_t key2flag = 0; uint8_t key3flag = 0; uint8_t key4flag = 0; printf("task3 is create ok\r\n"); while(1) { key_scan(0); // 数据发送 RS485 和 CAN 切换 // RS485Flag = 1 :发送RS485数据 // CANFlag = 1: 发送CAN数据 if(keydown_data==KEY0_DATA) //key0按下后马上执行相应代码 { if(key1flag == 0) { BEEP = 1; //鸣叫提示接入成功 delay_ms(250); BEEP = 0; delay_ms(250); RS485Flag = 1; CANFlag = 0; printf("RS485 通信模式\r\n"); } else { RS485Flag = 0; CANFlag = 1; printf("CAN 通信模式\r\n"); } key1flag++; if(key1flag == 2) { key1flag = 0; } } if(keyup_data==KEY1_DATA) //key1按下抬起之后执行相应代码 { if(RS485Flag == 1) { BEEP = 1; //鸣叫提示接入成功 delay_ms(250); BEEP = 0; delay_ms(250); RS485_Send_Data("$LED0#",6); printf("RS485 控制LED0 \r\n"); } if(CANFlag == 1) { if(key2flag == 0) { sprintf(can_sendbuf,"LED0__ON"); CAN_SendMsg(&TxMessage,(uint8_t *)can_sendbuf,8); printf("CAN 控制LED0--开\r\n"); } else { sprintf(can_sendbuf,"LED0_OFF"); CAN_SendMsg(&TxMessage,(uint8_t *)can_sendbuf,8); printf("CAN 控制LED0--关\r\n"); } } key2flag++; if(key2flag == 2) { key2flag = 0; } } if(keyup_data==KEY2_DATA) //key2长按1秒后执行相应代码 由于延时5ms扫描一次按键所以5ms*200=1S { if(RS485Flag == 1) { BEEP = 1; //鸣叫提示接入成功 delay_ms(250); BEEP = 0; delay_ms(250); RS485_Send_Data("$LED1#",6); printf("RS485 控制LED1 \r\n"); } if(CANFlag == 1) { if(key3flag == 0) { sprintf(can_sendbuf,"LED1__ON"); CAN_SendMsg(&TxMessage,(uint8_t *)can_sendbuf,8); printf("CAN 控制LED1--开\r\n"); } else { sprintf(can_sendbuf,"LED1_OFF"); CAN_SendMsg(&TxMessage,(uint8_t *)can_sendbuf,8); printf("CAN 控制LED1--关\r\n"); } } key3flag++; if(key3flag == 2) { key3flag = 0; } } if(keyup_data==KEY3_DATA) //key3长按2秒后执行相应代码 由于延时5ms扫描一次按键所以5ms*400=2S { if(RS485Flag == 1) { BEEP = 1; //鸣叫提示接入成功 delay_ms(250); BEEP = 0; delay_ms(250); RS485_Send_Data("$BEEP#",6); printf("RS485 控制BEEP \r\n"); } if(CANFlag == 1) { if(key4flag == 0) { sprintf(can_sendbuf,"BEEP__ON"); CAN_SendMsg(&TxMessage,(uint8_t *)can_sendbuf,8); printf("CAN 控制BEEP--开\r\n"); } else { sprintf(can_sendbuf,"BEEP_OFF"); CAN_SendMsg(&TxMessage,(uint8_t *)can_sendbuf,8); printf("CAN 控制BEEP--关\r\n"); } } key4flag++; if(key4flag == 2) { key4flag = 0; } } delay_ms(50); } } ```