精华内容
下载资源
问答
  • 在STM32F1上面实现的三个串口CAN总线可以同时进行传输数据
  • 这是一个基于单片机的串口CAN总线的程序,它可以实现串口CAN总线协议的相互转换。 这是一个基于单片机的串口CAN总线的程序,它可以实现串口CAN总线协议的相互转换。
  • 爱威轮 RS232串口CAN总线收发器说明书rar,爱威轮 RS232串口CAN总线收发器说明书
  • s5p4418 android 驱动 hal 应用之led 串口 rs485 can总线应用 系列文章配套源码,完整工程,绝对值得拥有。
  • s5p4418 android 驱动 hal 应用之led 串口 rs485 can总线应用(JNI层) 源码,文章s5p4418 android 驱动 hal 应用之led 串口 rs485 can总线应用(JNI层)配套,需要朋友欢迎下载,在realarm的s5p4418开发板源码中...
  • 本文是对串口设备的CAN总线方式远距离解决方案的简要介绍。
  • 结合设计中AT89C51单片机有串行通信口且接口简单的特点,选用RS-232作为CAN总线与PC机之间的连接方式。将就这一基于CAN控制器SJA1000与AT89C51的具有通用性的工业测控系统设计的软硬件设计方案作详细介绍。
  • STM32F103将GPS数据打包后从CAN总线发送,并将CAN总线数据打包后从串口发送,该工程包含GPS解码,与CAN总线通讯信息转发。
  • 1 CAN总线简介CAN是控制器局域网络(Controller Area Network,CAN)的简称,由德国BOSCH公司开发,并最终成为国际标准(ISO 11898-1)。CAN总线主要应用于工业控制和汽车电子领域,是国际上应用最广泛的现场总线之一。...

    1 CAN总线简介

    CAN是控制器局域网络(Controller Area Network,CAN)的简称,由德国BOSCH公司开发,并最终成为国际标准(ISO 11898-1)。CAN总线主要应用于工业控制和汽车电子领域,是国际上应用最广泛的现场总线之一。CAN总线是一种串行通信协议,能有效地支持具有很高安全等级的分布实时控制。CAN总线的应用范围很广,从高速的网络到低价位的多路接线都可以使用CAN。在汽车电子行业里,使用CAN 连接发动机的控制单元、传感器、防刹车系统等,传输速度可达1Mbps。

    5562f242cdb7d18e1aac1e62d9bf755f.png

    与前面介绍的一般通信总线相比,CAN总线的数据通信具有突出的可靠性、实时性和灵活性,在汽车领域的应用最为广泛,世界上一些著名的汽车制造厂商都采用CAN总线来实现汽车内部控制系统与各检测和执行机构之间的数据通信。目前,CAN总线的应用范围已不仅仅局限于汽车行业,而且已经在自动控制、航空航天、航海、过程工业、机械工业、纺织机械、农用机械、机器人、数控机床、医疗器械及传感器等领域中得到了广泛应用。CAN总线规范从最初的CAN 1.2规范(标准格式)发展为兼容CAN 1.2规范的CAN 2.0规范(CAN 2.0A为标准格式,CAN 2.0B为扩展格式),目前应用的CAN器件大多符合CAN 2.0规范。

    2 CAN总线的工作原理

    当CAN总线上的节点发送数据时,以报文形式广播给网络中的所有节点,总线上的所有节点都不使用节点地址等系统配置信息,只根据每组报文开头的 11 位标识符(CAN 2.0A 规范)解释数据的含义来决定是否接收。这种数据收发方式称为面向内容的编址方案。

    当某个节点要向其他节点发送数据时,这个节点的处理器将要发送的数据和自己的标识符传送给该节点的CAN总线接口控制器,并处于准备状态;当收到总线分配时,转为发送报文状态。数据根据协议组织成一定的报文格式后发出,此时网络上的其他节点处于接收状态。处于接收状态的每个节点对接收到的报文进行检测,判断这些报文是否是发给自己的以确定是否接收。

    由于CAN总线是一种面向内容的编址方案,因此很容易建立高水准的控制系统并灵活地进行配置我们可以很容易地在CAN总线上加进一些新节点而无须在硬件或软件上进行修改。

    当提供的新节点是纯数据接收设备时,数据传输协议不要求独立的部分有物理目的地址。此时允许分布过程同步化。也就是说,当总线上的控制器需要测量数据时,数据可由总线上直接获得,而无需每个控制器都有自己独立的传感器。

    3 CAN总线的工作特点

    CAN总线的有以下三方面特点:可以多主方式工作,网络上的任意节点均可以在任意时刻主动地向网络上的其他节点发送信息,而不分主从,通信方式灵活。网络上的节点(信息)可分成不同的优先级,可以满足不同的实时要求。采用非破坏性位仲裁总线结构机制,当两个节点同时向网络上传送信息时,优先级低的节点主动停止数据发送,而优先级高的节点可不受影响地继续传输数据。

    4 CAN总线协议层次结构

    与前面介绍的简单总线逻辑不同,CAN是一种复杂逻辑的总线结构。从层次上可以将 CAN 总线划分为三个不同层次:

    (1) 物理层

    在物理层中定义实际信号的传输方法,包括位的编码和解码、位的定时和同步等内容,作用是定义不同节点之间根据电气属性如何进行位的实际传输。在物理连接上,CAN总线结构提供两个引脚:CANH和CANL,总线通过CANH和CANL之间的差分电压完成信号的位传输。在不同系统中,CAN总线的位速率不同;在系统中,CAN总线的位速率是唯一的,并且是固定,这需要对总线中的每个节点配置统一的参数。

    (2) 传输层

    传输层是CAN总线协议的核心。传输层负责把接收到的报文提供给对象层,以及接收来自对象层的报文。传输层负责位的定时及同步、报文分帧、仲裁、应答、错误检测和标定、故障界定。

    (3) 对象层

    在对象层中可以为远程数据请求以及数据传输提供服务,确定由实际要使用的传输层接收哪一个报文,并且为恢复管理和过载通知提供手段。

    5 CAN总线的报文结构

    CAN 总线上的报文传输由以下 4 个不同的帧类型表示和控制。

    (1) 数据帧

    数据帧携带数据从发送器至接收器。总线上传输的大多是这种帧。从标识符长度上,又可以把数据帧分为标准帧(11 位标识符)和扩展帧(29 位标识符)。

    数据帧由 7 个不同的位场组成:帧起始、仲裁场、控制场、数据场、CRC场、应答场、帧结束。其中,数据场的长度为0~8个字节。标识符位于仲裁场中,报文接收节点通过标识符进行报文滤波。帧结构如图所示

    efe810f7c383e34243e24a1b67f827bd.png

    (2) 远程帧

    由总线上的节点发出,用于请求其他节点发送具有同一标识符的数据帧。当某个节点需要数据时,可以发送远程帧请求另一节点发送相应数据帧。与数据帧相比,远程帧没有数据场,结构如图所示。

    6854cdaded8ebdab3b8da42b865db2db.png

    (3) 错误帧

    任何单元,一旦检测到总线错误就发出错误帧。错误帧由两个不同的场组成,第一个场是由不同站提供的错误标志的叠加(错误标志),第二个场是错误界定符。3bda54a511792672c1ad31f8a731d74a.png

    (4) 过载帧

    过载帧用于在先行的和后续的数据帧(或远程帧)之间提供附加延时。过载帧包括两个场:过载标志和过载界定符。

    6 CAN接口配置

    在 Linux 系统中,CAN总线接口设备作为网络设备被系统进行统一管理。在控制台下,CAN总线的配置和以太网的配置使用相同的命令。

    7053a7c62248a129274312c73ad95de6.png      在上面的结果中,eth0 和 eth1 设备为以太网接口,can0 设备为 CAN 总线接口。接下来使用 ip 命 令来配置 CAN 总线的位速率:

    ip link set can0 type cantq 125 prop-seg 6phase-seg1 7 phase-seg2 2 sjw 1ip link set can0 type can bitrate 500000 triple-sampling onifconfig can0 up cansend can0 145#1122334455667788candump can0

    当设置完成后,可以通过下面的命令查询 can0 设备的参数设置:

    ip -details link show can0

    7 代码  

    /*1.报文发送程序*/#include#include#include#include#include#include#include#include#includeint main(){    int s,nbytes;    struct sockaddr_can addr;    struct ifreq ifr;    struct can_frame frame[2] = {{0}};    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字    strcpy(ifr.ifr_name, "can0");    ioctl(s, SIOCGIFINDEX, &ifr);//指定can0设备    addr.can_family = AF_CAN;    addr.can_ifindex = ifr.ifr_ifindex;    bind(s, (structsockaddr*)&addr, sizeof(addr));//将套接字与can0绑定    //禁用过滤规则,本进程不接收报文,只负责发送    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);    //生成两个报文    frame[0].can_id = 0x11;    frame[0].can_dlc = 1;    frame[0].data[0] = 'Y';    frame[0].can_id = 0x22;    frame[0].can_dlc = 1;    frame[0].data[0] = 'N';    //循环发送两个报文    while(1)    {        nbytes = write(s,&frame[0], sizeof(frame[0]));//发送frame[0]        if(nbytes! = sizeof(frame[0]))        {            printf("Send Error frame[0]\n!");            break;//发送错误,退出        }        sleep(1);        nbytes = write(s,&frame[1], sizeof(frame[1]));//发送frame[1]        if(nbytes != sizeof(frame[0]))        {            printf("SendErrorframe[1]\n!");            break;        }        sleep(1);    }    close(s);    return0;}/*2.报文过滤接收程序*/#include#include#include#include#include#include#include#include#includeint main(){    int s,nbytes;    struct sockaddr_can addr;    struct ifreq ifr;    struct can_frame frame;    struct can_filter rfilter[1];    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字    strcpy(ifr.ifr_name, "can0");    ioctl(s, SIOCGIFINDEX, &ifr);//指定can0设备    addr.can_family = AF_CAN;    addr.can_ifindex = ifr.ifr_ifindex;    bind(s,(struct sockaddr*)&addr, sizeof(addr));//将套接字与can0绑定    //定义接收规则,只接收表示符等于0x11的报文    rfilter[0].can_id=0x11;    rfilter[0].can_mask = CAN_SFF_MASK;    //设置过滤规则    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));    while(1)    {        nbytes = read(s, &frame, sizeof(frame));//接收报文        //显示报文        if(nbytes>0)        {            printf("ID=0x%XDLC=%ddata[0]=0x%X\n",frame.can_id, frame.can_dlc,frame.data[0]);        }    }    close(s);    return 0;}
    展开全文
  • 介绍了一种基于C8051F502单片机的CAN总线串口转换电路,介绍了电路的基本器件选型,并对软件的设计思路流程进行了详细描述。通过测试,证明此种转换方式高速可靠,可广泛应用。
  • 本篇相对于前面介绍的led操作增加了串口、485和can的应用。对于led的驱动、hal、app我这里就不再介绍,可以参考前面的文章Android应用开发 led 驱动层 hal硬件抽象层 应用层 详细教程记录(含源码)。 一般来说,...

    本篇文章用于记录Android开发学习过程中的一些理解和遇到的一些问题解决,以防忘记,好记性不如烂博客,O(∩_∩)O。

    本篇相对于前面介绍的led操作增加了串口、485和can的应用。对于led的驱动、hal、app我这里就不再介绍,可以参考前面的文章Android应用开发 led 驱动层 hal硬件抽象层 应用层 详细教程记录(含源码)

    一般来说,Linux系统下对串口、485和can的操作都是基于节点的操作,如tty***、can*这样的节点,所以对于应用程序来说,只要有这些设备节点即可完成操作了,所以本篇文章介绍的串口和485应用可以兼容任何一款Android设备或者开发板,can不一定,因为can在系统服务中做了固定设置,默认使用的是can0,一般来系统中只有一个can节点,且为can0,当然不排除其他can1的情况,读者在使用时这个要注意。由于485跟串口区别不是很大,所以直接可以按照串口协议来进行操作,使用的也是tty***这样的节点设备,当然如果要像使用串口一样来使用485,那么在硬件上是要做特别设计的,可以参考下图所示的电路。


    下面就主要介绍hal和app这两层,驱动层是由设备节点完成,只要系统中的设备节点没有问题,那么通信就是正常的,测试节点可在Linux系统下使用nfs进行调试测试(nfs个人认为有助于加快调试速度,因为无需下载等繁琐的操作)。

    对于hal层我这里就直接贴代码,不再一步一步做出解释,代码后边会做一些必要的说明,主要是有疑问,需要注意的地方。

    代码如下:

    #include <stdio.h>  
    #include <stdlib.h>
    #include <termios.h>
    #include <unistd.h> 
    #include <sys/types.h>
    #include <sys/stat.h> 
    #include <fcntl.h>
    #include <string.h>
    #include "jni.h"
    #include "JNIHelp.h"
    #include <assert.h> 
    #include "can.h"
    #include <sys/socket.h>
    #include <net/if.h>
    #include <cutils/properties.h>
    #include <sys/wait.h>
    // 引入log头文件
    #include <android/log.h>  
    // log标签
    #define  TAG    "Led_Load_JNI"
    // 定义info信息
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
    // 定义debug信息
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
    // 定义error信息
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
    
    #define DEVICE_NAME		"/dev/real_led"
    
    //#define NO_REGISTER
    /*LED JNI*/
    #ifndef NO_REGISTER
    static jint Java_realarm_hardware_HardwareControl_LedSetState
      	(JNIEnv *env, jobject thiz,jint ledNum,jint ledState)
    #else
    JNIEXPORT jint JNICALL Java_realarm_hardware_HardwareControl_LedSetState
    	(JNIEnv *env, jobject thiz,jint ledNum,jint ledState)
    #endif
    {
    	int fd = open(DEVICE_NAME, 0);
    	
    	if (fd == -1)
    	{
    		LOGE("led open error");
    		return 1;	
    	}
    	
    	if(ledState == 0)
    		LOGD("Led close success");
    	else if(ledState == 1)
    		LOGD("Led open success");
    	else {
    		LOGD("Led ledState parameters ERROR.Only 0 or 1.");
    		return 1;
    	}
    	ledState &= 0x01;
    	ioctl(fd, ledState, 0);
    
    	close(fd);
    	return 0;
    }
    
    /*LED JNI*/
    
    /*UART JNI*/
    int serialfd=-1;
    static speed_t getBaudrate(jint baudrate)
    {
    	switch(baudrate) {
    	case 0: return B0;
    	case 50: return B50;
    	case 75: return B75;
    	case 110: return B110;
    	case 134: return B134;
    	case 150: return B150;
    	case 200: return B200;
    	case 300: return B300;
    	case 600: return B600;
    	case 1200: return B1200;
    	case 1800: return B1800;
    	case 2400: return B2400;
    	case 4800: return B4800;
    	case 9600: return B9600;
    	case 19200: return B19200;
    	case 38400: return B38400;
    	case 57600: return B57600;
    	case 115200: return B115200;
    	case 230400: return B230400;
    	case 460800: return B460800;
    	case 500000: return B500000;
    	case 576000: return B576000;
    	case 921600: return B921600;
    	case 1000000: return B1000000;
    	case 1152000: return B1152000;
    	case 1500000: return B1500000;
    	case 2000000: return B2000000;
    	case 2500000: return B2500000;
    	case 3000000: return B3000000;
    	case 3500000: return B3500000;
    	case 4000000: return B4000000;
    	default: return -1;
    	}
    }
    
    /*
     * Class:     cedric_serial_SerialPort
     * Method:    open
     * Signature: (Ljava/lang/String;)V
     */
    static jobject Java_realarm_hardware_HardwareControl_OpenSerialPort
      (JNIEnv *env, jobject thiz, jstring path, jint baudrate, jint flags)
    {
    	int fd;
    	speed_t speed;
    	jobject mFileDescriptor;
    
    	LOGD("baudrate %d", baudrate);
    	/* Check arguments */
    	{
    		speed = getBaudrate(baudrate);
    		if (speed == -1) {
    			/* TODO: throw an exception */
    			LOGE("Invalid baudrate");
    			return NULL;
    		}
    	}
    
    	/* Opening device */
    	{
    		jboolean iscopy;
    		const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
    		LOGD("Opening serial port %s", path_utf);
    		fd = open(path_utf, O_RDWR);
    		serialfd=fd;
    		LOGD("open() fd = %d", fd);
    		(*env)->ReleaseStringUTFChars(env, path, path_utf);
    		if (fd == -1)
    		{
    			/* Throw an exception */
    			LOGE("Cannot open port");
    			/* TODO: throw an exception */
    			return NULL;
    		}
    	}
    
    	/* Configure device */
    	{
    		struct termios cfg;
    		LOGD("Configuring serial port");
    		if (tcgetattr(fd, &cfg))
    		{
    			LOGE("tcgetattr() failed");
    			close(fd);
    			/* TODO: throw an exception */
    			return NULL;
    		}
    
    		cfmakeraw(&cfg);
    		cfsetispeed(&cfg, speed);
    		cfsetospeed(&cfg, speed);
    
    		if (tcsetattr(fd, TCSANOW, &cfg))
    		{
    			LOGE("tcsetattr() failed");
    			close(fd);
    			/* TODO: throw an exception */
    			return NULL;
    		}
    	}
    
    	// Create a corresponding file descriptor 
    	{
    		jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
    		jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
    		jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
    		mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
    		(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
    	}
    
    	return mFileDescriptor;
    }
    
    
    /*
     * Class:     cedric_serial_SerialPort
     * Method:    close
     * Signature: ()V
     */
    static void Java_realarm_hardware_HardwareControl_CloseSerialPort
      (JNIEnv *env, jobject thiz)
    {
    
    /*	jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
    	jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
    
    	jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
    	jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
    
    	jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
    	jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
    
    	ALOGD("close(fd = %d)", descriptor);
    	close(descriptor);
    */
    	LOGE("close(fd = %d)", serialfd);
    	if(-1!=serialfd)
    		close(serialfd);
    }
    
    
    /*UART JNI*/
    
    
    /*RS485 JNI*/
    /*realarm开发板对485的使能做了硬件处理,所以485操作与串口并无区别*/
    /*RS485 JNI*/
    
    
    /*CAN JNI*/
    
    int canfd=-1;
    struct sockaddr_can addr;
    
    void my_strcpy(char *dest, char *src, size_t n)
    {
    	char i = 0;
    	while(i < n)
    	{	
    		*(dest++) = *(src++);
    		i++;
    	}
    }
    int my_system(const char * cmd) 
    { 
    	FILE * fp; 
    	int res; char buf[1024]; 
    	if (cmd == NULL) 
    	{ 
    		LOGD("my_system cmd is NULL!\n");
    		return -1;
    	} 
    	if ((fp = popen(cmd, "r") ) == NULL) 
    	{ 
    		LOGE("popen");
    	 	LOGE("popen error: %s/n", strerror(errno)); return -1; 
    	} 
    	else
    	{
    	 	while(fgets(buf, sizeof(buf), fp)) 
    		{ 
    			LOGD("%s", buf); 
    		} 
    		  
    		if ( (res = pclose(fp)) == -1) 
    		{ 
    			LOGE("close popen file pointer fp error!\n"); return WEXITSTATUS(res);
    		} 
    		else if (res == 0) 
    		{
    		 	return WEXITSTATUS(res);
    		} 
    		else 
    		{ 
    			LOGD("popen res is :%d\n", res); return WEXITSTATUS(res); 
    		} 
    	}
    	LOGI("popen success!\n");
    	return -1;
    } 
    static void Java_realarm_hardware_HardwareControl_InitCan
      (JNIEnv *env, jobject thiz, jint baudrate)
    {
    
    	/* Check arguments */
    	switch (baudrate)
    	{
    		case 5000   :
    		case 10000  :
    		case 20000  :
    		case 50000  :
    		case 100000 :
    		case 125000 :
    			LOGI("Can Bus Speed is %d",baudrate);
    		break;
    		default:
    			LOGI("Can Bus Speed is %d.if it do not work,try 5000~125000",baudrate);
    	}
    
    	/* Configure device */
    	if(baudrate!=0)
    	{
    		char str_baudrate[16];
    		sprintf(str_baudrate,"%d", baudrate);
    		property_set("net.can.baudrate", str_baudrate); 
    		LOGI("str_baudrate is:%s", str_baudrate);
    		property_set("net.can.change", "yes");
    		/*//下面的方法无法实现波特率的设置,命令未正确执行
    		char cmd[100];
    		sprintf(cmd, "%s%s", "su ip link set can0 up type can bitrate ", str_baudrate);
    		LOGI("cmd is:%s", cmd);
    		my_system("busybox ifconfig can0 down");
    		my_system(cmd); 
    		my_system("busybox ls");
    		*/
    	}	
    }
    static jint Java_realarm_hardware_HardwareControl_OpenCan
      (JNIEnv *env, jobject thiz)
    {
    
    	struct ifreq ifr;
    	int ret;     
    	
    	/* Opening device */
    	canfd = socket(PF_CAN,SOCK_RAW,CAN_RAW);
    
    	if(canfd==-1)
    	{
    		LOGE("Can Write Without Open"); 
    		return   0;
    	}
    
    	strcpy((char *)(ifr.ifr_name),"can0");
    	ioctl(canfd,SIOCGIFINDEX,&ifr);
    
    
    
    	addr.can_family = AF_CAN;
    	addr.can_ifindex = ifr.ifr_ifindex;
    	bind(canfd,(struct sockaddr*)&addr,sizeof(addr));
    
    	return canfd;
    }
    
    static jint Java_realarm_hardware_HardwareControl_CanWrite
      (JNIEnv *env, jobject thiz, jint canId, jstring data)
    {
    
    	int nbytes;
    	int num = 0, i = 0;
    	struct can_frame frame;
    
    	jboolean iscopy;
    	const char *send_data = (*env)->GetStringUTFChars(env, data, &iscopy);	
    	
    	frame.can_id = canId;
    
    	if(strlen(send_data) > 8)//用于支持当输入的字符大于8时的情况,分次数发送
    	{
    		num = strlen(send_data) / 8;
    		for(i = 0;i < num;i++)
    		{
    			my_strcpy((char *)frame.data, &send_data[8 * i], 8);
    			frame.can_dlc = 8;
    			sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr));
    		}
    		memset((char *)frame.data, 0, 8);
    		my_strcpy((char *)frame.data, &send_data[8 * i], strlen(send_data) - num * 8);
    		frame.can_dlc = strlen(send_data) - num * 8;
    		sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr));
    		nbytes = strlen(send_data);
    	}
    	else
    	{	
    		my_strcpy((char *)frame.data, send_data, strlen(send_data));
    		frame.can_dlc = strlen(send_data);
    		sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr));
    		nbytes = strlen(send_data);
    	}
    	(*env)->ReleaseStringUTFChars(env, data, send_data);
    	LOGD("write nbytes=%d",nbytes);
    	return nbytes;
    }
    
    static jobject Java_realarm_hardware_HardwareControl_CanRead
      (JNIEnv *env, jobject thiz, jobject obj, jint time)
    {
    
    	unsigned long nbytes,len;
    
    	struct can_frame frame = {0};
    	int k=0;
    	jstring   jstr; 
    	
    	char temp[16];
    
    	fd_set rfds;
    	int retval;
    	struct timeval tv;
            tv.tv_sec = time;  		
            tv.tv_usec = 0;
    
    	bzero(temp,16);
    	if(canfd==-1){
    		LOGE("Can Read Without Open");
    		frame.can_id=0;
    		frame.can_dlc=0;
    	}else{
    		FD_ZERO(&rfds);
    		FD_SET(canfd, &rfds);
    		retval = select(canfd+1 , &rfds, NULL, NULL, &tv);
    		if (retval == -1){
    			LOGE("Can Read slect error");
    			frame.can_dlc=0;
    			frame.can_id=0;
    		}else if (retval){
    			nbytes = recvfrom(canfd, &frame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr,&len);
    		
    			for(k = 0;k < frame.can_dlc;k++)
    				temp[k] = frame.data[k];
    			temp[k] = 0;
    			
    			frame.can_id = frame.can_id - 0x80000000;//读得的id比实际的有个80000000差值,这里需要处理一下
    			LOGD("Can Read slect success.");
    		}else{
    			frame.can_dlc=0;
    			frame.can_id=0;
    			//LOGD("Can no data.");
    		}
    
    	}
    	
    		
        jclass objectClass = (*env)->FindClass(env,"realarm/hardware/CanFrame");
        jfieldID id = (*env)->GetFieldID(env,objectClass,"can_id","I");
        jfieldID leng = (*env)->GetFieldID(env,objectClass,"can_dlc","C");
        jfieldID str = (*env)->GetFieldID(env,objectClass,"data","Ljava/lang/String;");
        
    	if(frame.can_dlc) {	
    		LOGD("can_id is :%d", frame.can_id);
    		LOGD("can read nbytes=%d", frame.can_dlc);
    		LOGD("can data is:%s", temp);
    	}
    	
        (*env)->SetCharField(env, obj, leng, frame.can_dlc);
        (*env)->SetObjectField(env, obj, str, (*env)->NewStringUTF(env,temp));
        (*env)->SetIntField(env, obj, id, frame.can_id);
    	 
    	return   obj;
    }
    
    static void Java_realarm_hardware_HardwareControl_CloseCan
      (JNIEnv *env, jobject thiz)
    {
    
    	if(canfd!=-1)
    		close(canfd);
    	canfd=-1;
    	LOGD("close can0");
    }
    
    /*CAN JNI*/
    
    #ifndef NO_REGISTER
    static JNINativeMethod gMethods[] = {  
    	{"LedSetState", "(II)I", (void *)Java_realarm_hardware_HardwareControl_LedSetState}, 
    	{"OpenSerialPort", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", (void *)Java_realarm_hardware_HardwareControl_OpenSerialPort},
    	{"CloseSerialPort", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseSerialPort},
    	{"InitCan", "(I)V", (void *)Java_realarm_hardware_HardwareControl_InitCan},  
    	{"OpenCan", "()I", (void *)Java_realarm_hardware_HardwareControl_OpenCan},
    	{"CanWrite", "(ILjava/lang/String;)I", (void *)Java_realarm_hardware_HardwareControl_CanWrite}, 
    	{"CanRead", "(Lrealarm/hardware/CanFrame;I)Lrealarm/hardware/CanFrame;", 
    						(void *)Java_realarm_hardware_HardwareControl_CanRead},
    	{"CloseCan", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseCan}, 
    }; 
     
    static int register_android_realarm_test(JNIEnv *env)  
    {  
       	jclass clazz;
        static const char* const kClassName =  "realarm/hardware/HardwareControl";
    
        /* look up the class */
        clazz = (*env)->FindClass(env, kClassName);
        //clazz = env->FindClass(env,kClassBoa);
        if (clazz == NULL) {
            LOGE("Can't find class %s\n", kClassName);
            return -1;
        }
    
        /* register all the methods */
        if ((*env)->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
        //if (env->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
        {
            LOGE("Failed registering methods for %s\n", kClassName);
            return -1;
        }
    
        /* fill out the rest of the ID cache */
        return 0;
    }
    #endif
    
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {
        
    #ifndef NO_REGISTER
    	JNIEnv *env = NULL;
    	if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
    	//if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {  
    		LOGI("Error GetEnv\n");  
    		return -1;  
    	} 
    	assert(env != NULL);  
    	if (register_android_realarm_test(env) < 0) {  
    		printf("register_android_realarm_test error.\n"); 
    		LOGE("register_android_realarm_test error."); 
    		return -1;  
    	}
    #endif
        /* success -- return valid version number */
    	LOGI("/*****************realarm**********************/");
    
        return JNI_VERSION_1_4;
    }
    
    led需要注意的地方没什么可说的,串口部分我会在can中做说明。

    下面是第需要说明的知识点:

    // 引入log头文件
    #include <android/log.h>  
    // log标签
    #define  TAG    "Led_Load_JNI"
    // 定义info信息
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
    // 定义debug信息
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
    // 定义error信息
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
    可以看到这部分是一些宏定义,定义了LOGI、LOGD、LOGE这三个宏。目的是能够在Android调试的时候可以通过logcat查看到调试信息,对应的Android层的api分别是Log.i、Log.d、Log.e,所以是很必要的。

    可以看到要使用这个功能需要__android_log_print这个函数,当然也需要#include <android/log.h>这个头文件。__android_log_print这个函数有三个参数,这里拿LOGI来说明,可以看到LOGI的实际代码是__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__),第一个参数ANDROID_LOG_INFO是告诉Android系统当前是属于信息类log,第二个参数TAG是指示是哪一个部分的信息(这里是#define  TAG    "Led_Load_JNI",这样易区分是哪里打印的信息,也有助于调试),第三个参数__VA_ARGS__就是LOGI输入的要打印的信息了。具体的解释,大家谷歌吧。

    当然了,如果不想这么定义直接使用__android_log_print函数也是可以的,不过看起应该很烦吧。

    下面是第二个解释的函数:

    <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">static void Java_realarm_hardware_HardwareControl_InitCan
      (JNIEnv *env, jobject thiz, jint baudrate)
    {
    
    	/* Check arguments */
    	switch (baudrate)
    	{
    		case 5000   :
    		case 10000  :
    		case 20000  :
    		case 50000  :
    		case 100000 :
    		case 125000 :
    			LOGI("Can Bus Speed is %d",baudrate);
    		break;
    		default:
    			LOGI("Can Bus Speed is %d.if it do not work,try 5000~125000",baudrate);
    	}
    
    	/* Configure device */
    	if(baudrate!=0)
    	{
    		char str_baudrate[16];
    		<span style="color:#ff0000;">sprintf(str_baudrate,"%d", baudrate);
    		property_set("net.can.baudrate", str_baudrate); 
    		LOGI("str_baudrate is:%s", str_baudrate);
    		property_set("net.can.change", "yes");</span>
    		<span style="color:#3333ff;">/*//下面的方法无法实现波特率的设置,命令未正确执行
    		char cmd[100];
    		sprintf(cmd, "%s%s", "su ip link set can0 up type can bitrate ", str_baudrate);
    		LOGI("cmd is:%s", cmd);
    		my_system("ifconfig can0 down");
    		my_system(cmd); 
    		my_system("busybox ls");
    		*/</span>
    	}	
    }
    
    
    <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">首先来看上面函数的红色部分,sprintf和LOGI函数我这里就不说了,自行谷歌或百度。这里重要说明的是property_set这个函数,该函数是用于访问设置系统的环境属性,该函数是用于c/c++中的,在java中有其他的函数来代替,具体的可以参考http://blog.sina.com.cn/s/blog_55465b470101ngpv.html、http://blog.csdn.net/jackyu613/article/details/6136620等文章,百度或谷歌有很多。</span>
    现在关键的是使用这个环境变量设置怎么就能够能够完成can的波特率设置呢。说起还是比较曲折呢,思路是这样的,在Android系统的时候设置一个can波特率设置的服务,并设置一个启动该服务的一个条件,当启动条件变成yes的时候,can波特率设置的服务就会重启一次,进而完成设置。

    具体实现方法下面介绍。

    /device/nexell/realarm目录下的init.realarm.rc最后的代码如下:

    ##############################**CAN**####################################  
    chmod 0777 /system/bin/can.sh  
    service can /system/bin/can.sh
        oneshot
    
    on property:net.can.change=yes
        restart can
    #########################################################################
    可以看到添加了一个can服务,以及net.can.change这个系统属性,那么启动can服务就是通过net.can.change这个系统属性是否为yes来触发的。

    前面的红色代码最后一句可以看到就是设置net.can.change为yes属性。

    那么net.can.baudrate这个属性怎么用的呢,可以看到红色代码中设置了这个属性,它是存储波特率的。它的使用时在启动的服务can.sh中,代码如下(位置在/device/nexell/realarm/can):

    #!/system/bin/sh
    
    setprop net.can.change no
    
    new_baudrate=`getprop net.can.baudrate`
    
    ifconfig can0 down
    ip link set can0 up type can bitrate $new_baudrate
    
    从can.sh代码中可以知道,先设置net.can.change为no以关闭触发,然后通过getprop(Android系统提供getprop和setprop来读取和设置系统属性的值)命令来获取net.can.baudrate属性的值(在上面红色代码中所设置的值),然后通过ip link set can0 up type can bitrate $new_baudrate命令来完成can的设置和启动。

    这样can的设置就可以完成了。


    另外前面代码还有蓝色部分,该部分被注释掉了,这个是我在尝试另一种设置波特率的方法,只是未成功,不过这里我也说一下原理,若有朋友找到了原因,可以告知我,感激不敬。

    蓝色代码部分主要想通过使用my_system这个函数来执行shell命令完成can的设置,理论上是可以的,可是实际上却不行,因为无论怎么样都无法正确执行ifconfig can0 down和ip命令,但是ls等其他指令却可以正常执行,搞不懂了。如果ifconfig can0 down和ip能够正常执行,那么代码就更简单了。

    下面是my_system()函数的实现:

    int my_system(const char * cmd) 
    { 
    	FILE * fp; 
    	int res; char buf[1024]; 
    	if (cmd == NULL) 
    	{ 
    		LOGD("my_system cmd is NULL!\n");
    		return -1;
    	} 
    	if ((fp = popen(cmd, "r") ) == NULL) 
    	{ 
    		LOGE("popen");
    	 	LOGE("popen error: %s/n", strerror(errno)); return -1; 
    	} 
    	else
    	{
    	 	while(fgets(buf, sizeof(buf), fp)) 
    		{ 
    			LOGD("%s", buf); 
    		} 
    		  
    		if ( (res = pclose(fp)) == -1) 
    		{ 
    			LOGE("close popen file pointer fp error!\n"); return WEXITSTATUS(res);
    		} 
    		else if (res == 0) 
    		{
    		 	return WEXITSTATUS(res);
    		} 
    		else 
    		{ 
    			LOGD("popen res is :%d\n", res); return WEXITSTATUS(res); 
    		} 
    	}
    	LOGI("popen success!\n");
    	return -1;
    }
    关于popen和pclose的使用,以及c提供的system()函数,大家百度谷歌吧。

    下面是第三个要解释的部分:

    #ifndef NO_REGISTER
    static JNINativeMethod gMethods[] = {  
    	{"LedSetState", "(II)I", (void *)Java_realarm_hardware_HardwareControl_LedSetState}, 
    	{"OpenSerialPort", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", (void *)Java_realarm_hardware_HardwareControl_OpenSerialPort},
    	{"CloseSerialPort", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseSerialPort},
    	{"InitCan", "(I)V", (void *)Java_realarm_hardware_HardwareControl_InitCan},  
    	{"OpenCan", "()I", (void *)Java_realarm_hardware_HardwareControl_OpenCan},
    	{"CanWrite", "(ILjava/lang/String;)I", (void *)Java_realarm_hardware_HardwareControl_CanWrite}, 
    	<span style="color:#ff0000;">{"CanRead", "(Lrealarm/hardware/CanFrame;I)Lrealarm/hardware/CanFrame;", 
    						(void *)Java_realarm_hardware_HardwareControl_CanRead},</span>
    	{"CloseCan", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseCan}, 
    }; 
     
    static int register_android_realarm_test(JNIEnv *env)  
    {  
       	jclass clazz;
        static const char* const kClassName =  "realarm/hardware/HardwareControl";
    
        /* look up the class */
        clazz = (*env)->FindClass(env, kClassName);
        //clazz = env->FindClass(env,kClassBoa);
        if (clazz == NULL) {
            LOGE("Can't find class %s\n", kClassName);
            return -1;
        }
    
        /* register all the methods */
        if ((*env)->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
        //if (env->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
        {
            LOGE("Failed registering methods for %s\n", kClassName);
            return -1;
        }
    
        /* fill out the rest of the ID cache */
        return 0;
    }
    #endif
    
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {
        
    #ifndef NO_REGISTER
    	JNIEnv *env = NULL;
    	if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
    	//if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {  
    		LOGI("Error GetEnv\n");  
    		return -1;  
    	} 
    	assert(env != NULL);  
    	if (register_android_realarm_test(env) < 0) {  
    		printf("register_android_realarm_test error.\n"); 
    		LOGE("register_android_realarm_test error."); 
    		return -1;  
    	}
    #endif
        /* success -- return valid version number */
    	LOGI("/*****************realarm**********************/");
    
        return JNI_VERSION_1_4;
    }
    这部分是关于jni向上层提供api注册的部分。关于注册可以参考文章http://blog.csdn.net/wang_shuai_ww/article/details/44456755,关于JNINativeMethod这个结构体,百度谷歌更好。我这里只解释上面代码中红色部分。

    一般的类型有如下:

    字符  Java类型 C类型

    V       void            void
    Z        jboolean     boolean
    I         jint              int
    J        jlong            long
    D       jdouble       double
    F       jfloat            float
    B       jbyte            byte
    C       jchar           char
    S       jshort          short

    数组则以"["开始,用两个字符表示

    [I        jintArray      int[]
    [F      jfloatArray    float[]
    [B      jbyteArray    byte[]
    [C     jcharArray    char[]
    [S     jshortArray   short[]
    [D     jdoubleArray double[]
    [J     jlongArray     long[]
    [Z     jbooleanArray boolean[]

    Ljava/lang/String; String jstring
    Ljava/net/Socket; Socket jobject
    我们需要的结构是CanFrame结构,这个在Android系统中并未提供,而是我们应用的java代码中自定义的一个类,所在的包是realarm.hardware,类名是CanFrame,所以红色代码的写法就是Lrealarm/hardware/CanFrame,指定包和类名。从下图可以看到我们定义的类:


    所以对于这类自定义的类型,在native注册时,参数(JNINativeMethod第二个参数)说明的方法是L+包名+类名。这个一定要理解,否则对于写jni有很大障碍,不懂的多谷歌JNINativeMethod这个结构的说明。


    下面是第四个要解释的部分:

    从第三部分的CanRead函数可知,Java_realarm_hardware_HardwareControl_CanRead函数要接收一个参数jobject obj,是jobject 类型的,实际上是CanFrame类型的,从第三个解释的部分可以知道。CanFrame和jni这层定义的can_frame怎么联系的呢。

    先贴出代码,下面慢慢说。

    static jobject Java_realarm_hardware_HardwareControl_CanRead
      (JNIEnv *env, jobject thiz, jobject obj, jint time)
    {
    
    	unsigned long nbytes,len;
    
    	struct can_frame frame = {0};
    	int k=0;
    	jstring   jstr; 
    	
    	char temp[16];
    
    	fd_set rfds;
    	int retval;
    	struct timeval tv;
            tv.tv_sec = time;  		
            tv.tv_usec = 0;
    
    	bzero(temp,16);
    	if(canfd==-1){
    		LOGE("Can Read Without Open");
    		frame.can_id=0;
    		frame.can_dlc=0;
    	}else{
    		FD_ZERO(&rfds);
    		FD_SET(canfd, &rfds);
    		retval = select(canfd+1 , &rfds, NULL, NULL, &tv);
    		if (retval == -1){
    			LOGE("Can Read slect error");
    			frame.can_dlc=0;
    			frame.can_id=0;
    		}else if (retval){
    			nbytes = recvfrom(canfd, &frame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr,&len);
    		
    			for(k = 0;k < frame.can_dlc;k++)
    				temp[k] = frame.data[k];
    			temp[k] = 0;
    			
    			frame.can_id = frame.can_id - 0x80000000;//读得的id比实际的有个80000000差值,这里需要处理一下
    			LOGD("Can Read slect success.");
    		}else{
    			frame.can_dlc=0;
    			frame.can_id=0;
    			//LOGD("Can no data.");
    		}
    
    	}
    	
    		
    <span style="color:#ff0000;">    jclass objectClass = (*env)->FindClass(env,"realarm/hardware/CanFrame");
        jfieldID id = (*env)->GetFieldID(env,objectClass,"can_id","I");
        jfieldID leng = (*env)->GetFieldID(env,objectClass,"can_dlc","C");
        jfieldID str = (*env)->GetFieldID(env,objectClass,"data","Ljava/lang/String;");</span>
        
    	if(frame.can_dlc) {	
    		LOGD("can_id is :%d", frame.can_id);
    		LOGD("can read nbytes=%d", frame.can_dlc);
    		LOGD("can data is:%s", temp);
    	}
    	
    <span style="color:#ff0000;">    </span><span style="color:#3333ff;">(*env)->SetCharField(env, obj, leng, frame.can_dlc);
        (*env)->SetObjectField(env, obj, str, (*env)->NewStringUTF(env,temp));
        (*env)->SetIntField(env, obj, id, frame.can_id);</span>
    	 
    	return   obj;
    }
    在上面代码中最不好理解的可能就是红色部分了,而这部分就是关键的部分。

    (*env)->FindClass:获得CanFrame结构,并保存于objectClass
    (*env)->GetFieldID:或者objectClass类中的成员,也就是CanFrame结构里的成员。该函数前两个参数不解释了容易理解,第三个参数和第四个参数比较重要,第三个参数标示CanFrame结构里的成员名称(需要名字一致,否则会出错的),第四个是该成员的类型。

    下面举个例子

    (*env)->GetFieldID(env,objectClass,"can_id","I")意思是获取objectClass类中的成员can_id,且类型为int型。与下图中CanFrame的定义对比:


    can_id为Int型,是对应的。其他两个也同理,都是对应的。获得了java的类型后,下面看看是怎么联系的。

    数据交换就是上面蓝色部分的代码了。

    (*env)->SetCharField(env, obj, leng, frame.can_dlc);第二个参数就是Java_realarm_hardware_HardwareControl_CanRead接收的jobject obj参数,leng就是指can_dlc的fieldID,frame.can_dlc就是can读取到的字节数数据了。通过这样一句话,就把frame.can_dlc数据放到了obj中的can_dlc变量中了。

    其他两个函数都类似,只是数据类型不同而已。最后把obj返回给java层,那么一个完整测CanFrame帧就读取完成了。


    到此本篇介绍的JNI层,以及需要注意的地方就介绍完了,下一篇是app应用。


    源码下载地址:http://download.csdn.net/detail/u010406724/8539263

    转载请注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44672531

    展开全文
  • 小编为大家总结了一篇基于 STM32 和 CAN 总线的温度监控系统的设计,通过上位机下位机的通信,实现对温度数据的监控,并经初步实验达到了设计的要求。1 系统总体方案概述系统总体框图如图 1 所示,本系统采用主站+...

    小编为大家总结了一篇基于 STM32 和 CAN 总线的温度监控系统的设计,通过上位机与下位机的通信,实现对温度数据的监控,并经初步实验达到了设计的要求。

    1 系统总体方案概述

    系统总体框图如图 1 所示,本系统采用主站+从站的结构,CAN 主站主要实现温度数据的存储以及 CAN 总线协议和串口协议之间的桥接,CAN 从站主要实现温度的采集。CAN 从站采集的温度,经过 CAN 总线传送到 CAN 主站,主站将各从站的温度值传送到系统上位机中。上位机对各点的数据进行实时曲线显示并进行存储,上位机可以设定报警值,当节点温度超过设定值的时候,上位机发出报警声。在没有上位机的场合,主站将数据以文本文档的形式存储在主站的 SD 卡中。

    943838408cef4aa6e72912324d57c458.png

    图 1 系统总体框图

    2 系统硬件设计

    2.1 CAN 主站硬件设计

    主站电路如图 2 所示,主要有电源模块、STM32 模块、CAN 收发器模块、RS232 串口模块和 SD 卡模块。

    其中 STM32 模块由 STM32F103RBT6 和周边时钟、复位、调试等组成。电源模块由外部输入+5V 电压,经线性压降 AMS1117-3.3V 后供 STM32 使用。CAN 收发器模块采甩 NXP 的高速收发器 TJA1040,TJA1040 是 PCA82C250 的替代品,它完全符合 ISO11898 标准,具有高速、低功耗、低电磁辐射的优点。RS232 电平转换芯片采用 MAX3232,它具有低功耗、高数据速率以及增强型 ESD 保护等特性。采用专有的低压差发送输出级,+3.0V 至+5.5V 供电时利用内部双电荷泵保证 RS-232 性能。工作时,电荷泵仅需要四个 100nF 的小电容。SD 卡模块采用四线制 SPI 总线与 SD 卡相连接。

    2.2 CAN 从站硬件设计

    从站电路如图 3 所示,主要有电源模块、STM32 模块,CAN 收发器模块,PT100 模块和从站地址选择模块。

    3d96f9a88649c91d580f9513076fbacd.png

    图 2CAN 主站电路框图 图 3CAN 从站电路框图

    其中电源模块、STM32 模块和 CAN 收发器模块与 CAN 主站相同。PT100 模块,采用传感器测量电桥,为了保证电桥输出电压信号的稳定性,电桥的输入电压通过 TL431 稳至 2.5V。从电桥获取的差分信号通过两级运放放大后输入 STM32 的 AD 输入口。从站地址选择模块由 8 位拨码开关组成,连接到 STM32I/O 的 PC6-PC13。

    3 系统软件设计

    本系统软件由 CAN 主站软件、从站软件以及 Delphi 上位机软件构成。其中 CAN 主站和从站程序用 C 语言编写,上位机程序用 ObieetPascal 编写。

    3.1 CAN 主站软件设计

    CAN 主站的功能是发送远程帧向从站查询数据、通过 AD 的转换结果计算芯片内部温度传感器的温度值、接收从站发送的数据帧、发送温度数据到上位机或存储数据到 SD 卡。CAN 主站程序如图 4 所示。

    c061173b09d101fee8773fad34ea94db.png

    图 3CAN 主站程序流程图图 5 写 SD 卡程序流程图

    写 SD 卡部分程序流程图如图 5 所示,SD 卡部分主要用到了移植的 FATFS 文件系统提供的访问 FAT 卷的应用接口(ApplicationInterface)。主要用到了以下函数:

    ·f_mount- 注册 / 注销一个工作区域

    ·f_open- 打开 / 创建一个文件

    ·f_close- 关闭一个文件

    ·f_lseek- 移动 / 写指针,扩展文件大小

    ·f_puts- 写字符串

    ·f_printf- 写一个格式化字符串

    3.2 CAN 从站软件设计

    CAN 从站的主要功能是通过 AD 转换器检测 PT100 电桥的差分输出电压,然后计算得出此节点的温度值,最后通过 CAN 总线传送给 CAN 主站。其中只有在 CAN 从站收到与自己节点号相同的主站发送的远程帧的时候,从站 CAN 控制器才发出一个数据帧。CAN 从站程序流程图如图 6 所示。

    dc2e1f8ab89b6c17cd8233f51a7f5c04.png

    图 6CAN 从站程序流程图图 7Delphi 实时曲线部分流程图

    3.3 Delphi 上位机软件设计

    本上位机软件主要实现了五个功能:实时曲线显示当前各从节点的温度;打印实时曲线;将实时曲线保存为图片;将实时曲线的数据保存为 TXT 文档以及实时温度超过报警值时报警。

    本系统上位机串口通讯控件采用 SPCOMM,该控件具有丰富的与串口通信密切相关的属性及事件,支持多线程;提供了对串口的各种操作。图形控件采用 TChart,TCharc 是 Delphi 里面一个标准的图形显示控件。它可以静态设计(AtDesignTIme)也可以动态生成。该系统设计采用的版本为 TeeChart7;实时曲线部分流程图如图 7 所示。上位机程序完成后界面如图 8 所示。

    99044968d198b1eca369c53f5291b5d4.png

    图 7 上位机界面

    4 结束语

    本文介绍了基于 STM32 和 CAN 总线的温度监控系统的设计,初步实验证明,上述的硬件及软件设计基本达到了设计要求。本系统适用于多节点、远距离的场合,并具有实时性好、可靠性高的特点,具有一定的应用价值。

    展开全文
  • 本文主要是讲解一下同步串口SPI总线系统。SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,SPI的速率一般可以到几十Mbps,不同的器件当主机和当作从机的速率一般不同...

    本文主要是讲解一下同步串口SPI总线系统。

    SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,SPI的速率一般可以到几十Mbps,不同的器件当主机和当作从机的速率一般不同。SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。外围设置FLASH-RAM、网络控制器、LCD显示驱动器、A/D转换器和MCU等。SPI是一种高速、高效率的串行接口技术。通常由一个主模块和一个或多个从模块组成,主模块选择一个从模块进行同步通信,从而完成数据的交换。SPI是一个环形结构,通信时需要至少4根线(事实上在单向传输时3根线也可以)该接口一般使用4条线:SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。
    • MISO – Master Input Slave Output,主设备数据输入,从设备数据输出;
    • MOSI – Master Output Slave Input,主设备数据输出,从设备数据输入;
    • SCLK – Serial Clock,时钟信号,由主设备产生
    • CS – Chip Select,从设备使能信号,由主设备控制。CS – 从设备使能信号,由主设备控制(Chip select),有的IC此pin脚叫SS。

    205371486_1_20201024100119522

    图一:SPI数据线连接示意图

    CS是从设备是否被主设备选中的控制信号,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),主设备对此从设备的操作才有效。这就使在同一条总线上连接多个SPI设备成为可能。

    SCLK信号线只由主设备控制,从设备不能控制信号线,SCLK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCLK时钟线的控制可以完成对通讯的控制。

    【SDI(数据输入);SDO(数据输出)】

    SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。主设备SPI时钟和极性的配置应该由外设来决定,主从设备二者的配置应该保持一致,即主设备的SDO同从设备的SDO配置一致,主设备的SDI同从设备的SDI配置一致。因为主从设备是在SCLK的控制下,同时发送和接收数据,并通过2个双向移位寄存器来交换数据。举例:上升沿主机SDO发送数据1,同时从设备SDO发送数据0;紧接着在SCLK的下降沿的时候从设备的SDI接收到了主机发送过来的数据1,同时主机也接收到了从设备发送过来的数据0,若干个次数后主从设备的数据信息完完成了交换。

    SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。

    205371486_2_20201024100119710 引用 Refreences 

    205371486_3_20201024100119772

    本文由不架构的汽车电子电气

    本文由不架构的汽车电子电气原创,作者:Feynman-Yang。

    展开全文
  • 而实际应用中PC机与CAN总线的人机交互设计尤为重要,它直接影响系统的运行和结果,其连接方法也成为系统设计的重点,通常采取3种连接方式:RS-232串行口通信、PCI卡、USB口通信,由于串行通信端口在系统控制领域中...
  • 由于app相对比较大,说起来也比较麻烦,这里就用一个串口来说明即可,实际上也就串口最具代表性,这个明白了,其他都不是问题。 串口涉及的java文件如下图红色圈所示: 具体的代码下面一一贴出来,捡重要的说明: ...
  • 而实际应用中PC机与CAN总线的人机交互设计尤为重要,它直接影响系统的运行和结果,其连接方法也成为系统设计的重点,通常采取3种连接方式:RS-232串行口通信、PCI卡、USB口通信,由于串行通信端口在系统控制领域中...
  • 因此可以联想到应该有技术文档或者外国文章早已对can总线的通讯已经做过讨论。因此Google搜索:TX2 can bus果然有,只不过是在英伟达官方论坛中,继续深入查找,在网址:...
  • 基于 STM32 和 CAN 总线的温度监控系统的设计,通过上位机下位机的通信,实现对温度数据的监控,并经初步实验达到了设计的要求。1 系统总体方案概述系统总体框图如图 1 所示,本系统采用主站+从站的结构,CAN 主站...
  • 基于STC89C52的串口通信和CAN总线设计,USB供电,原理图
  • 随着汽车电子、工业自动化的蓬勃发展,CAN总线上的设备数量、数据量都大大增加,这就使得传统的CAN总线在传输速率和带宽方面越来越力不从心,因此CAN FD孕育而生。那么如何实现CAN到CAN FD的升级?CANFDCOM的应用...
  • 基于51单片机的CAN总线转串口通信卡的设计实现
  • 阐述了CAN总线在232串口设备远程通信或自组网络中的应用,介绍了CAN到RS-232转换装置的软硬件设计方法。重点对软件设计中CAN波特率的设置、串口波特率的自动检测以及数据流控制等作了详细的叙述。
  • 智能CAN/串口协议转换器LCNET Pro RS-232/485提供一路RS-485、一路RS-232和一路CAN通道,实现CAN与串口RS-485或RS-232之间的双向数据智能转换。每个通道独立隔离,每路通道采用金升阳电源模块和信号隔离芯片实现2500...
  • CAN转串口产品有两款型号:CAN-232B、CAN-485B。前者为CAN转RS232产品,后者除...CAN-232B是智能型RS232总线CAN转换网桥,用于PC或其他带RS232端口的设备与CAN设备之间的通讯 。CAN (Controller Area Network)是一种...
  • STM32 标准库则是在寄存器用户代码之间的软件层。对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输...
  • 爱威轮 CAN总线-RS232串口智能转换器说明书rar,爱威轮 CAN总线-RS232串口智能转换器说明书

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,765
精华内容 2,306
关键字:

串口与can总线