主从机can通讯(stm32)

基于STM32的CAN总线温度检测节点 的硬件设计与制作。1个主节点+3个从节点,每个节点均包含STM32最小系统、CAN通信模块、温度采集模块,能够稳定完成本地温度采集并通过CAN总线实现数据收发。

1、CAN 总线

CAN(Controller Area Network,控制器局域网),1983 年博世为汽车电子开发,是差分串行通信总线,多主仲裁、抗干扰强、实时性高,广泛用于汽车、工业 PLC、嵌入式设备。 核心特点:多主机、无主从区分;差分信号抗 EMI;硬件帧仲裁;错误自动检测重传;最远 10km 低速通信。

本系统选取的是闭环CAN总线网络。特点:高速、短距离,它的总线最大长度为 40m,通信速度最高为 1Mbps,总线的两端各要求有一个120 欧的电阻。如下图所示:

2、CAN 模块

CAN通信模块核心器件选用TJA1050 CAN收发器,该器件支持5kbps至1Mbps波特率,兼容CAN 2.0A/B协议,可将STM32内部CAN控制器的逻辑电平转换为总线差分信号,提升抗干扰能力。其具备热保护、短路保护功能,可避免总线故障损坏器件,且体积小、功耗低,适合节点模块化部署;总线两端配置120Ω终端电阻,抑制信号反射,保障长距离传输稳定性。

3、注意事项:

由于TJA1050 CAN收发器配置了120Ω终端电阻,在连接TJA1050 硬件之前,需要对其中两个TJA1050去掉120Ω终端电阻,保证是闭环CAN总线网络,这样信号传输就不会丢包。

4、器件清单

stm32f103c8t6 4
TJA1050 4
DS18B20 4
BEEP 3
LED 4
电源模块 1
OLED 屏幕 1
按键模块 2

5、主机原理图

6、从机原理图

7、主机代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
#include "MyCAN.h"
#include "STDIO.h"
#include "ds18b20.h"
#include "LED.h"
uint8_t Keycount;
uint8_t Keymode;

uint32_t RxID;
uint8_t RxLength;
uint8_t RxData[8];
uint32_t TxID = 0x111;
uint8_t TxLength = 8;
uint8_t TxData[8] = {0x00, 0x00, 0x00};

float  TA;
float  TB;
float  TC;
float  TD;

char TA_buf[10];
char TB_buf[10];
char TC_buf[10];
char TD_buf[10];

uint8_t TA_s = 30;
uint8_t TB_s = 30;
uint8_t TC_s = 30;
uint8_t TD_s = 30;

void SHOW(void)
{
	OLED_ShowString(1, 1, "A:");
	OLED_ShowString(2, 1, "B:");
	OLED_ShowString(3, 1, "C:");	
	OLED_ShowString(4, 1, "D:");
	if(TA > TA_s)
		OLED_ShowString(1,10,"warning");
	else
		OLED_ShowString(1,10,"normal ");
	if(TB > TB_s)
		OLED_ShowString(2,10,"warning");
	else
		OLED_ShowString(2,10,"normal ");
	if(TC > TC_s)
		OLED_ShowString(3,10,"warning");
	else
		OLED_ShowString(3,10,"normal ");
	if(TD > TD_s)
		OLED_ShowString(4,10,"warning");
	else
		OLED_ShowString(4,10,"normal ");
}

float temp_data;
float temp_data1[1];

int main(void)
{
	OLED_Init();
	Key_Init();
	MyCAN_Init();
	OLED_Clear();
	DS18B20_Init();
	BEEP_Init();
	LED_Init();
	
	while (1)
	{
		Keycount = Key_GetNum();
		
		if(Keycount ==1){
			OLED_Clear();
			Keymode++;
			if(Keymode>4)Keymode = 0;
		}
		if(Keymode==0)
		{
			SHOW();
			TD=(float)DS18B20_Get_Temp()/10.0f;
			TxData[0] = TA_s;
			TxData[1] = TB_s;
			TxData[2] = TC_s;
			MyCAN_Transmit(0X111, TxLength,TxData);
			MyCAN_Transmit(0X222, TxLength,TxData);
			MyCAN_Transmit(0X333, TxLength,TxData);
			
			if(TD > (float)TD_s){
				LED_ON();BEEP_ON();
				Delay_ms(10);
			}
			else{
				LED_OFF();BEEP_OFF();
				Delay_ms(10);
			}
			
			
			if (MyCAN_ReceiveFlag())
			{
				MyCAN_Receive(&RxID, &RxLength, RxData);	
				if(RxID == 0x111)//从机A	
					TA = RxData[0] *10 + RxData[1] + (float)RxData[2]*0.1;
				if(RxID == 0x222)//从机B
					TB = RxData[0] *10 + RxData[1] + (float)RxData[2]*0.1;
				if(RxID == 0x333)//从机C
					TC = RxData[0] *10 + RxData[1] + (float)RxData[2]*0.1;
				
				sprintf(TA_buf,"%.1fC",TA);OLED_ShowString(1,3,TA_buf);
					Delay_ms(2);
				sprintf(TB_buf,"%.1fC",TB);OLED_ShowString(2,3,TB_buf);
					Delay_ms(2);
				sprintf(TC_buf,"%.1fC",TC);OLED_ShowString(3,3,TC_buf);
					Delay_ms(2);
				sprintf(TD_buf,"%.1fC",TD);OLED_ShowString(4,3,TD_buf);
					Delay_ms(2);				                                
			}  
		}
		Delay_ms(2);
		if(Keymode>0)
		{
			OLED_ShowString(1,3,"TA_H:");
			OLED_ShowString(2,3,"TB_H:");
			OLED_ShowString(3,3,"TC_H:");
			OLED_ShowString(4,3,"TD_H:");
			OLED_ShowNum(1,8,TA_s ,2);			
			OLED_ShowNum(2,8,TB_s ,2);	
			OLED_ShowNum(3,8,TC_s ,2);	
			OLED_ShowNum(4,8,TD_s ,2);	
		}
		if(Keymode==1)
		{
			OLED_ShowString(1,11,"<");
			if(Keycount ==2){
			TA_s++;
			if(TA_s>40)TA_s=20;}
		}
		if(Keymode==2)
		{
			OLED_ShowString(2,11,"<");
			if(Keycount ==2){
			TB_s++;
			if(TB_s>40)TB_s=20;}	
		}
		if(Keymode==3)
		{			
			OLED_ShowString(3,11,"<");
			if(Keycount ==2){
			TC_s++;
			if(TC_s>40)TC_s=20;}	
		}
		if(Keymode==4)
		{
			OLED_ShowString(4,11,"<");
			if(Keycount ==2){
			TD_s++;
			if(TD_s>40)TD_s=20;}
		}
	}
}

8、从机代码

下面是从机A代码。从机B、C、D代码只需将CAN的ID改成不同的即可。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "MyCAN.h"
#include "ds18b20.h"
#include <stdio.h>
#include <stdlib.h>

uint32_t RxID;
uint8_t RxLength;
uint8_t RxData[8];

uint32_t TxID = 0x222;
uint8_t TxLength = 8;
uint8_t TxData[8] = {0x00, 0x00, 0x00};

int Ttens, Tunits, Tdecimal;
float temp_data;
float temp_data1[1];
int temp_data_H;
void extract_digits(float num, int *tens, int *units, int *decimal) 
{
    float abs_num = (num < 0) ? -num : num;
    int scaled = (int)(abs_num * 10.0f + 0.5f);
    *decimal = scaled % 10;           // 小数位
    *units   = (scaled / 10) % 10;    // 个位
    *tens    = (scaled / 100) % 10;   // 十位
}

int main(void)
{
	DS18B20_Init();
	LED_Init();
	BEEP_Init();
	MyCAN_Init();
	
	while (1)
	{
		temp_data=(float)DS18B20_Get_Temp()/10.0f;
		
		if(temp_data > temp_data_H){
			LED_ON();BEEP_ON();
			Delay_ms(10);
		}
		else{
			LED_OFF();BEEP_OFF();
			Delay_ms(10);
		}
		
		temp_data1[0]=temp_data;

    extract_digits(temp_data1[0], &Ttens, &Tunits, &Tdecimal);
		
		TxData[0] = Ttens;
		TxData[1] = Tunits;
		TxData[2] = Tdecimal;

		MyCAN_Transmit(TxID, TxLength,TxData);
		
		if (MyCAN_ReceiveFlag())
		{
			MyCAN_Receive(&RxID, &RxLength, RxData);
			if(RxID == 0x222)//从机A
			{
				temp_data_H = RxData[1];
			}
			
		}
		Delay_ms(100);
	}
}

9、实物图

指导

如果需要全套源码(器件清单、含原理图、pcb文件、代码)可以搜上面的号

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐