IMX6ULL与STM32F103的CAN通信实现

2023-05-16

IMX6ULL与STM32F103的CAN通信实现

  • 硬件连接
  • 驱动层实现
    • IMX6ULL
    • STM32F103ZET6
  • 应用层实现
    • IMX6ULL
    • STM32F103ZET6
  • 结果

在上一篇博文中,我们利用USBCAN设备及其上位机软件,测试了一块以IMX6ULL为核心芯片的开发板的CAN通信收发功能,了解了在Linux应用层基于套接字实现CAN网络应用的基本方法。本次我们将USBCAN替换为一块以STM32F103ZET6为核心芯片的开发板,尝试实现双机之间的CAN通信基本功能。

硬件连接

在硬件上,两个板子各自均只留出1路CAN接口,只需将两个CAN接口的CAN_H与CAN_L对应连接起来即可。由于CAN信号是两根线的差分信号,故不需要再连接其它。两板的CAN收发器均采用经典的TJA1050,器件连接的原理图如下。用线连好后双机的CAN通信连接示意图如下。芯片内部集成的CAN控制器与外接的CAN收发器交换CAN报文数据,收发器是控制器与物理总线的中间媒介,负责在控制器的逻辑电平和总线的差分信号间做转换。而CAN控制器的具体行为由相关寄存器的状态决定,需要我们编写程序来控制。两个收发器之间的CAN_H和CAN_L两条线的两段之间,需要各自接一个120Ω的电阻,由原理图可见板载已经接入了该电阻,无需额外接入。两个板子连接的实物图如下。
IMX6开发板上的CAN收发器
双机CAN通信连线示意图
双机CAN通信连接实物

驱动层实现

这里所说的驱动层,是从Linux系统体系的角度而言的。Linux具有明显的内核与应用层之分,对底层硬件的驱动程序是内核的组成部分,在应用层控制硬件工作的流程中充当了一种“中间件”的作用,对上提供相关接口供应用层调用,对下通过操作寄存器等真正操控硬件的行为。但对于STM32裸机的开发方式来说,程序并不需要明确划分哪些部分属于app、哪些属于driver,所有的代码是可以放在一起的。我们本次的开发基于ST官方提供的标准库,所以这里把基于标准库封装开发所得的用于实现STM32的CAN外设基本功能的接口,称为STM32的CAN“驱动层”。

IMX6ULL

为驱动IMX6ULL上的CAN外设,首先要找到描述该设备硬件信息的设备树,其中外设引脚信息和控制器描述信息是必不可少的。我们在内核源码/arch/arm/boot/dts/目录下找到有关该CAN外设的设备树代码如下:

// 引脚配置信息
pinctrl_flexcan1: flexcan1grp {
	fsl,pins = <
		MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX	0x1b020 // CAN的RX引脚复用UART3_RTS
		MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX	0x1b020 // CAN的TX引脚复用UART3_CTS
	>;
};
// CAN1控制器
flexcan1: can@02090000 {
	compatible = "fsl,imx6ul-flexcan", "fsl,imx6q-flexcan"; // 匹配NXP原厂CAN驱动
	reg = <0x02090000 0x4000>;
	interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_CAN1_IPG>,
		 <&clks IMX6UL_CLK_CAN1_SERIAL>;
	clock-names = "ipg", "per";
	stop-mode = <&gpr 0x10 1 0x10 17>;
	status = "disabled";
};

上面CAN控制器的status为disabled,表示不可用,需要改成okay才能正常使用。一般我们不会直接去修改原控制器的默认设置,而是采用“追加”的方式更改属性值,如下为对flexcan1硬件信息的追加补充:

&flexcan1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_flexcan1>; // 指定引脚
	xceiver-supply = <&reg_can_3v3>; // 指定CAN收发器电压为3.3V
	status = "okay"; // 状态为可用
};

这样设备树的配置基本就完成了,然后需要使能IMX6ULL的flexcan驱动。一般而言,NXP在官方提供的内核里已经集成了多种类型外设的驱动,如果仅仅使用基础通信等基本功能而不涉及复杂需求的实现,那么大多数情况下直接使能内核自带驱动后就可以了。在内核源码顶层目录下执行内核配置make menuconfig,依次进入Networking support、CAN bus subsystem support、CAN Device Driver,找到Support for Freescale FLEXCAN based chips选项,编译类型选成编译为内核一部分即built-in,如下:
使能NXP自带的flexcan驱动
保存设置后CAN驱动的编译选项就存在了编译配置文件中,再编译内核就能使上面的设备树配置和驱动配置生效了,将生成的Image和dtb文件载入IMX6ULL开发板,即可看到系统中的CAN设备,如下。在/sys/class/net/目录下的can0即为我们配置的外设,查看其控制器设备树的status可见值为okay,和我们设置的一样。
CAN设备
CAN控制器的设备树

STM32F103ZET6

要使用STM32F103的CAN外设,我们需要考虑这些方面:①引脚配置,要考虑CAN_RX和CAN_TX所在引脚号、GPIO的时钟使能、GPIO参数如何配置等;②CAN控制器配置,包括CAN外设时钟使能、CAN工作模式、波特率、接收FIFO属性等;③CAN过滤器配置,主要决定CAN的接收策略;④CAN外设的接收和发送接口;⑤在需要利用CAN中断的场合,还应该配置NVIC和CAN中断类型。后面和IMX6ULL交互时需要用到CAN接收中断,所以我们本次需要配置中断。基于ST提供的标准库,可以将上述几方面开发成自己的接口,即为所谓的CAN“驱动层”。我们开发的接口主要代码如下:

#define CANx              CAN1 // 使用CAN1
#define CAN_CLK           RCC_APB1Periph_CAN1 // CAN1的时钟
#define CAN_RX_IRQ        USB_LP_CAN1_RX0_IRQn // CAN接收中断号
#define CAN_RX_IRQHandler USB_LP_CAN1_RX0_IRQHandler // CAN接收中断服务函数

#define CAN_RX_PIN        GPIO_Pin_11 // Rx:PA11
#define CAN_TX_PIN        GPIO_Pin_12 // Tx:PA12
#define CAN_TX_GPIO_PORT  GPIOA
#define CAN_RX_GPIO_PORT  GPIOA
#define CAN_TX_GPIO_CLK   RCC_APB2Periph_GPIOA // GPIOA的时钟
#define CAN_RX_GPIO_CLK   RCC_APB2Periph_GPIOA

typedef enum{
	STANDARD = 0,
	EXTENDED
} CAN_ID_TYPE; // 报文ID类型

typedef enum{
	DATA = 0,
	REMOTE
} CAN_DATA_TYPE; // 帧类型

/* GPIO配置 */
static void CAN_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE); // 使能Rx和Tx对应GPIO的时钟
	
	GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	// Tx配置为复用推挽输出         
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // Rx配置为上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);
}

/* NVIC配置 */
static void CAN_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 设置中断优先级分组1

	NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ; // CAN接收中断号
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

/* CAN控制器配置 */
static void CAN_Mode_Config(void)
{
	CAN_InitTypeDef CAN_InitStructure;
    RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE); // 使能CAN时钟

	CAN_DeInit(CANx);
	CAN_StructInit(&CAN_InitStructure);

	CAN_InitStructure.CAN_TTCM = DISABLE;		
	CAN_InitStructure.CAN_ABOM = ENABLE;	   
	CAN_InitStructure.CAN_AWUM = ENABLE;
	CAN_InitStructure.CAN_NART = DISABLE;		   
	CAN_InitStructure.CAN_RFLM = DISABLE; // 禁止FIFO锁定模式	   
	CAN_InitStructure.CAN_TXFP = DISABLE;
	CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
	CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; //	同步跳跃宽度
	CAN_InitStructure.CAN_BS1 = CAN_BS1_5tq; // 位段1
	CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq; // 位段2	 
	CAN_InitStructure.CAN_Prescaler = 8; // 时钟分频
	CAN_Init(CANx, &CAN_InitStructure);
}

/* CAN过滤器配置 */
static void CAN_Filter_Config(void)
{
	CAN_FilterInitTypeDef CAN_FilterInitStructure;
	u16 FilterId = 0x123; // 可接收的报文ID
	u16 FilterMask = 0xFFFF; // 掩码

	CAN_FilterInitStructure.CAN_FilterNumber = 0; // 过滤器号
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;	// 掩码模式
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;	
	CAN_FilterInitStructure.CAN_FilterIdHigh = FilterId<<5; // 标准标识符
	CAN_FilterInitStructure.CAN_FilterIdLow = (u16)(CAN_ID_STD|CAN_RTR_DATA); // 数据帧
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = FilterMask; // 掩码
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = FilterMask;			
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 关联FIFO0			
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;			
	CAN_FilterInit(&CAN_FilterInitStructure);

	CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE); // 使能FIFO0的消息挂起中断
}

/* CAN配置 */
void CAN_Config(void)
{
	 CAN_GPIO_Config();
	 CAN_NVIC_Config();
	 CAN_Mode_Config();
	 CAN_Filter_Config();
}

/* 接收消息初始化 */
void CAN_InitRxMessage(CanRxMsg *RxMessage, CAN_ID_TYPE idtype, CAN_DATA_TYPE datatype, u8 dlc, u8 fmi)
{
	RxMessage->StdId = 0;
	RxMessage->ExtId = 0;
	
	if(idtype == STANDARD)
		RxMessage->IDE = CAN_ID_STD;
	else if(idtype == EXTENDED)
		RxMessage->IDE = CAN_ID_EXT;
	
	if(datatype == DATA)
		RxMessage->RTR = CAN_RTR_DATA;
	else if(datatype == REMOTE)
		RxMessage->RTR = CAN_RTR_REMOTE;

	RxMessage->DLC = dlc;
	memset(RxMessage->Data, 0x00, 8);
	RxMessage->FMI = fmi;
}

/* 设置发送消息 */
void CAN_SetTxMessage(CanTxMsg *TxMessage, u32 id, CAN_ID_TYPE idtype, CAN_DATA_TYPE datatype, u8 dlc, unsigned char *data)
{
	TxMessage->StdId = id;
	
	if(idtype == STANDARD)
		TxMessage->IDE = CAN_ID_STD;
	else if(idtype == EXTENDED)
		TxMessage->IDE = CAN_ID_EXT;

	if(datatype == DATA)
		TxMessage->RTR = CAN_RTR_DATA;
	else if(datatype == REMOTE)
		TxMessage->RTR = CAN_RTR_REMOTE;

	TxMessage->DLC = dlc;
	memcpy(TxMessage->Data, data, dlc);
}

/* CAN发送消息 */
u8 CAN_SendTxMsg(CAN_TypeDef *CANxx, CanTxMsg *TxMessage)
{
	u8 mailbox = 3;
	if((mailbox = CAN_Transmit(CANxx, TxMessage)) != CAN_TxStatus_NoMailBox) // 调用库函数CAN_Transmit实现发送
	{
		while((CAN_TransmitStatus(CANxx, mailbox)) != CAN_TxStatus_Ok); // 检查发送状态是否为完成
		return 0;
	}
	else
	{
		printf("CAN_SendTxMsg error and no mail box\r\n");
		return 1;
	}
}

上面代码中,CAN接收中断号定义在文件stm32f10x.h中的枚举类型IRQn_Type,该类型规定了STM32F10x的所有中断号。CAN接收中断服务函数的名称定义在启动文件start_up_stm32f10x_hd.s中。在CAN控制器配置函数中,设定了禁止FIFO锁定模式,意味着FIFO溢出时新接收的报文会覆盖旧的报文而不会被丢弃。由时间参数的设定可以得出,CAN波特率即为36MHz/(8*(1+5+3))=500kbps。在CAN过滤器的配置中,采用了32位掩码模式,根据ID为0x123和掩码为0xFFFF可知,这里设定了仅接受ID为0x123的报文,并且为标准ID和数据帧。使能了FIFO0的消息挂起中断,即该邮箱中接收到消息就进入CAN接收服务函数。在CAN_SendTxMsg接口中,调用了库函数CAN_Transmit进行发送,该函数返回有效的邮箱号或表示无可用邮箱的标志。利用库函数CAN_TransmitStatus检查发送状态是否为完成。

应用层实现

我们设计一个非常简单的双机交互流程,如下:

IMX6 STM32 send 0x123 send 0x456 send 0x123 send 0x456 ...... IMX6 STM32

IMX6这边首先发送ID为x0123的CAN报文,STM32接收到后回复ID为0x456的CAN报文,IMX6收到后继续发送0x123,然后重复这个过程。

IMX6ULL

关于IMX6ULL的CAN通信应用层的基本知识,可参考之前的博文《imx6ull开发板的CAN通信》,这里不详细说明了。我们设置2个线程分别处理发送和接收任务,发送线程在收到报文后延时2秒发送,两个线程之间使用简单的标志位同步。应用层代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <pthread.h>

struct can_frame receiveframe = {0};
struct can_frame sendframe = {0};
struct can_filter filters[3];
int sockfd;
int flag = 1;

void CAN_Config(void){
	struct ifreq ifr = {0};
	struct sockaddr_can can_addr = {0};
	int i;
	int ret;

	if((sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0){
		perror("socket error");
		exit(EXIT_FAILURE);
	}

	strcpy(ifr.ifr_name, "can0");
	if((ioctl(sockfd, SIOCGIFINDEX, &ifr)) != 0){
		perror("ioctl error");
		exit(EXIT_FAILURE);
	}

	can_addr.can_family = AF_CAN;
	can_addr.can_ifindex = ifr.ifr_ifindex;
	if((ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr))) < 0){
		perror("bind error");
		close(sockfd);
		exit(EXIT_FAILURE);
	}

	filters[0].can_id = 0x123;
	filters[1].can_id = 0x234;
	filters[2].can_id = 0x456;
	for(i = 0;i < 3;i++)
		filters[i].can_mask = 0x7FF; // ID掩码
	setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, &filters, sizeof(filters)); // 设置接收过滤

	sendframe.can_id = 0x123; // 帧ID为0x123(标准帧)
	sendframe.can_dlc = 8; // 一次发送8个字节
	sendframe.data[0] = 0x01;
	sendframe.data[1] = 0x02;
	sendframe.data[2] = 0x03;
	sendframe.data[3] = 0x04;
	sendframe.data[4] = 0x05;
	sendframe.data[5] = 0x06;
	sendframe.data[6] = 0x07;
	sendframe.data[7] = 0x08;
}

void *canReceive(void *arg){
	while(1){
		int i = 0;
		if((read(sockfd, &receiveframe, sizeof(struct can_frame))) < 0){
			perror("read error");
			break;
		}

		if(receiveframe.can_id & CAN_ERR_FLAG){ // 校验错误帧
			printf("Error frame!\n");
			break;
		}

		printf("Received CAN data from STM32F103ZET6\n");
		if(receiveframe.can_id & CAN_EFF_FLAG) // 校验扩展帧
			printf("扩展帧 <0x%08x> \n", receiveframe.can_id & CAN_EFF_MASK);
		else
			printf("标准帧 <0x%03x> \n", receiveframe.can_id & CAN_SFF_MASK);

		if(receiveframe.can_id & CAN_RTR_FLAG){ // 校验远程帧
			printf("remote request\n");
			continue;
		}

		printf("can_dlc: [%d] \ndata: ", receiveframe.can_dlc);
		for(i;i < receiveframe.can_dlc;i++)
			printf("%02x ", receiveframe.data[i]);
		printf("\n\n");
		//usleep(5000);
		flag = 1;
	}
}

void *canSend(void *arg){
	while(1){
		sleep(2);
		if(flag == 1){
			if((write(sockfd, &sendframe, sizeof(sendframe))) != sizeof(sendframe)){ // 发送
				perror("write error");
				break;
			}
			flag = 0;
		}
	}	
}

void threadCreate(void){
	pthread_t t1;
	pthread_t t2;
	int ret;
	
	if(ret = pthread_create(&t1, NULL, canReceive, NULL)){
		printf("canReceive thread create fail\r\n");
		return;
	}
	pthread_detach(t1);

	if(ret = pthread_create(&t2, NULL, canSend, NULL)){
		printf("canSend thread create fail\r\n");
		return;
	}
	pthread_detach(t2);	
}

int main(void){
	CAN_Config();
	//printf("CAN_Config end\n");
	threadCreate();
	//printf("threadCreate end\n");
	while(1){
		sleep(10000);
	}
	return 0;
}

STM32F103ZET6

STM32侧的接收是在CAN接收中断服务函数中完成的,在文件stm32f10x_it.c中添加如下代码:

extern CanRxMsg RxMessage; // 接收缓冲区
extern u8 flag; // 接收中断通知主函数的标志

void CAN_RX_IRQHandler(void)
{
	CAN_Receive(CANx, CAN_FIFO0, &RxMessage); // 调用库函数实现报文接收
	flag = 1; // 通知主函数报文已收到
}

有报文到来则FIFO0的消息挂起中断触发,调用库函数CAN_Receive实现接收,并且通过简单的标志位通知主函数消息收到。接收缓冲区和通知标志均在主函数中声明。主函数循环检测该标志,如检测到被置1则通过串口把收到的数据打印出来,并立即发送报文给IMX6ULL。主函数的主要代码如下:

CanTxMsg TxMessage; // 发送缓冲区
CanRxMsg RxMessage; // 接收缓冲区
unsigned char senddata[] = {0x78, 0x89, 0x9A, 0xAB, 0xBC, 0xCD, 0xDE, 0xEF}; // 报文数据内容
u8 flag = 0; // 接收中断通知主函数的标志

int main(void) {
	uartInit(115200); // 串口初始化
	CAN_Config(); // CAN配置
	CAN_SetTxMessage(&TxMessage, 0x456, STANDARD, DATA, 8, senddata); // 组织发送报文
	CAN_InitRxMessage(&RxMessage, STANDARD, DATA, 8, 0); // 初始化接收缓冲区
	while(1) {
		if(flag == 1) // 有消息到来
		{
			u8 i = 0;
			printf("[Received CAN data from IMX6ULL] ID[0x%X] IDE[0x%X] RTR[0x%X] DLC[0x%X]\r\n",
		    		RxMessage.StdId, RxMessage.IDE, RxMessage.RTR, RxMessage.DLC);
			for(i;i < RxMessage.DLC;i++)
				printf("data[%d]:0x%X ", i, RxMessage.Data[i]);
			printf("\r\n");
			printf("\n");
			if(CAN_SendTxMsg(CANx, &TxMessage)) // 发送报文
				break;
			//printf("send from mailbox:%d\r\n", ret);
			flag = 0; // 等待下一次消息到来
		}
	}
}

结果

两边编译成功后烧写到各自板子中,启动IMX6侧的应用进程,可以看到STM32侧打印的串口消息和IMX6侧进程打印的接收消息如下。可以看出STM32成功接收到了ID为0x123的标准数据帧,每次接收8字节0x01到0x08,IMX6ULL侧成功接收到了ID为0x456的标准数据帧,每次接收8字节:0x78、0x89、0x9a、0xab、0xbc、0xcd、0xde和0xef。
STM32打印的接收消息
IMX6进程打印的接收消息

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

IMX6ULL与STM32F103的CAN通信实现 的相关文章

  • Rt-thread的CAN应用2

    1 rtt中使能CAN1 2 CubeMX生成部分代码 xff0c 完成下面操作然后生成MDK ARM项目文件 3 将CubeMX生成的 HAL CAN MspInit 函数 粘贴到drv can c中 并在rt hw can init 函
  • STM32 CAN通信的学习笔记总结

    转载地址 xff1a STM32 CAN通信的学习笔记总结 xff08 从小白开始 xff09 目录 1 总体概述 1 1 基本概念1 2 通讯方式1 3 为什么使用CAN 1 4 CAN的协议及组成 2 上帝视角看CAN的通讯过程 2 1
  • STM32F103驱动LD3320语音识别模块

    STM32F103驱动LD3320语音识别模块 LD3320语音识别模块简介模块引脚定义STM32F103ZET6开发板与模块接线测试代码实验结果 LD3320语音识别模块简介 基于 LD3320 xff0c 可以在任何的电子产品中 xff
  • 100ask_imx6ull视频监控项目-内网穿透(六)

    100ask imx6ull视频监控项目 内网穿透 六 在前面的课程 Ffmpeg和Nginx都运行在开发板上 拉流端只能在同一个局域网内 不能通过局域网外的互联网访问Ngnix 想在任何地方 都可以通过互联网访问Nginx 怎么办 方法1
  • i.MX 6ULL 驱动开发 一:搭建开发环境

    一 搭建编译环境 1 工具 工具 版本 开发板 正点原子 imx6ull 开发板 gcc gcc linaro 4 9 4 2017 01 x86 64 arm linux gnueabihf uboot uboot imx 2016 03
  • 山石岩读丨前沿领域探析——汽车CAN总线协议详解及攻击面分析

    1 CAN总线的基本概念以及由来 CAN Controller Area Network 总线协议是由 BOSCH 发明的一种基于消息广播模式的串行通信总线 它起初用于实现汽车内ECU之间可靠的通信 后因其简单实用可靠等特点 而广泛应用于工
  • STM32F103 UART4串口使用DMA接收不定长数据和DMA中断发送

    一 前言 使用DMA通信的好处是 不占用单片机资源 不像普通串口中断 发送一个字节触发一次中断 发送100个字节触发100次中断 接收一个字节触发一次中断 接收200个字节触发200次中断 数据接收完毕触发一次DMA中断 发送数据完毕触发一
  • 基于STM32的超声波感应垃圾桶

    目录 成果演示 材料 主要代码 总结 成果演示 材料 STM323f103开发板 最小系统均可 超声波模块 HC SR04模块 舵机一个 垃圾桶模型 主要代码 超声波模块 include ultrasonsic h include dela
  • IMX6学习记录(14)-屏幕使用,显示图片、文字

    上面是我的微信和QQ群 欢迎新朋友的加入 1 内核配置 启动板子 能看到一个企鹅在界面上 2 修改文件系统 etc inittab sysinit etc init d rcS console askfirst bin sh tty1 as
  • 【汽车电子】浅谈汽车四大总线:LIN、CAN、FlexRay、MOST

    目录 1 前言 2 汽车四大总线 2 1 LIN总线 2 1 1 LIN总线概述 2 1 2 LIN总线工作原理 2 2 CAN总线 2 2 1 CAN总线概述 2 2 2 CAN总线工作原理 2 2 3 CAN总线的优点 2 3 Flex
  • STM32定时器-基本定时器

    目录 定时器分类 基本定时器功能框图讲解 基本定时器功能 时钟源 计数器时钟 计数器 自动重装载寄存器 定时时间的计算 定时器初始化结构体详解 实验 定时器分类 STM32F1 系列中 除了互联型的产品 共有 8 个定时器 分为基本定时器
  • STM32CUBEMX F103 HAL库开发 两路定时器的Encoder编码器模式

    机器人开发过程中 对于直流电机来说 编码器至关重要 它不仅可以使我们对电极进行精确的速度闭环 位置闭环 还可以通过时间积分 根据运动学关系 获得速度 位置等信息 STM32的定时器有编码器模式 大大的方便我们的开发 使用STM32cubeM
  • STM32进入STOP模式并唤醒实验总结

    项目需求 需要实现设备低功耗功能 实现过程中遇到几个问题 以此记录总结 stm32f103ret6 问题一 执行PWR EnterSTOPMode PWR Regulator LowPower PWR STOPEntry WFI 后 程序继
  • 迅为i.MX6ULL开发板Platform设备驱动运行测试

    文章目录 1 编译驱动和设备程序 2 编译应用测试程序 3 运行测试 1 编译驱动和设备程序 和前面章节中驱动测试程序一样需要一个Makefile文件 只是将obj m的值改为led device o led driver o Makefi
  • STM32F103 - 配置规则通道参数 - 05 - unfinished -unfinished-unfinished

    五 配置规则通道参数 设置指定ADC的规则组通道 一个序列 采样时间 常规通道配置 ADC RegularChannelConfig ADC1 ch 1 ADC SampleTime 239Cycles5 ADC1 ADC通道 采样时间为2
  • Canoe 安装流程

    硬件 VN5620 软件 CANoe V15 0 软件 Vector License Client 6 2 驱动 Vector Driver Setup license 购买硬件时 vector会分配 参考文档 First Steps to
  • STM32 CAN通信的学习笔记总结(从小白开始)

    知识来源于互联网 回馈于互联网 目录 1 总体概述 1 1 基本概念 1 2 通讯方式 1 3 为什么使用CAN 1 4 CAN的协议及组成 2 上帝视角看CAN的通讯过程 2 1 数据传输原理实现 2 2 通信的整个过程 2 2 1 空闲
  • Ubuntu 周立功CAN分析仪 USBCAN-II 驱动

    首先从官网https www zlg cn Index Search search key linux的下载资料界面下载 Linux驱动 USBCAN I I II II 2A I MINI安装驱动 USBCAN II新版驱动基于libus
  • 解决keil中 点击setting 程序中断问题

    自己写了一个LED常亮的程序 入门嘛 但是程序在下载后 点击 debug setting 在软件识别J link后 程序是成功的 但是LED不亮了 下面是解决方法 记住把2标记处的对勾去掉就可以了 这个功能是 在你连接完成时自动在Reset
  • IMX6学习记录(7)-编译脚本

    上面是我的微信和QQ群 欢迎新朋友的加入 1 效果 这几天搞这个东西 会有一大堆的命令行操作 很多重复的内容 现在做一个简单的脚本 方便自己平时的开发工作 2 脚本内容 bin bash uboot编译 function mk uboot

随机推荐

  • ROS学习笔记7_服务端Server

    在上图所示的模型中 xff0c Client作为请求的发送端 xff0c Server端接收Client发送的指令 xff0c 并且完成topic指令的发送 其中请求的信息类型是std srvs Trigger类 xff0c 同时返回一个R
  • char和int转换

    char和int的转换有两种方式 最简单的方法就是利用ASSCII码的差值 xff0c 直接用char的值减去 0 就行了 eg xff1a char a 61 39 9 39 int a 61 a 39 0 39 或者就用atof函数 x
  • printf缓冲区踩坑

    问题 碰到了这样一段代码 经过简化的 span class token macro property span class token directive hash span span class token directive keywo
  • PSINS学习笔记---姿态解算(1)---圆锥运动

    PSINS堪称中国导航领域的福音了 计划将工具箱中常用于工程实际中的相关算法根据个人理解做个解读注释 并且利用严老师网站中公开的数据集进行测试 由于个人水平有限 料想会漏洞百出 希望大家发现了不吝赐教 xff0c 感谢 xff01 1 加载
  • 使用matlab读取excel并作图

    在写论文时无奈非要用matlab xff0c 于是用地面站把传感器数据导出到了excel xff0c 用matlab画个图 用地面站向excel中读入数据 读入完保存即可 我读的是两种传感器数据 xff0c 一个是光流的位置值 xff0c
  • 坐标系梳理

    在很久以来 xff0c 我以为世界上只有两种坐标系 n系导航系和b系机体坐标系 最近在调试厂实验期间才知道原来还有特么这么多坐标系 机体系 xff1a 无人机 潜航器这些刚体自身的坐标系 xff0c 坐标系符合右手法则 xff0c x轴为机
  • 串口通信实现Int或float类型数据传输的方法

    方法 xff1a 发送方拆分数据为多个字节 xff0c 接收方再合并 串口通信程序中发送和接受数据以字节为单位 xff0c 将int或float类型的数据拆成单个字节存放到发送字符数组中 xff0c 然后接收方按照大小端模式将其重新合并为i
  • Gazebo Plugins教程

    Overview of Gazebo plugins Gazebo插件通过标准C 43 43 类直接控制Gazebo模型 xff0c 其具有以下优点 可以控制gazebo中几乎各个方面 xff1b 容易共享 xff1b 能够在运行的系统中插
  • 基于STM32读取SBG Ellipse A型号惯导数据

    之前在飞控上用的都是mpu6050 xff0c 但AUV在执行任务时主要在水下环境 xff0c 收不到GPS信号 xff0c 因此对INS的精度要求较高 xff0c 在姿态解算时要考虑地球自转等因素 xff0c 因此需要一款惯导器件能够感受
  • gcc-arm-none-eabi + scons环境搭建、固件烧录小记

    环境搭建在win7系统下进行 1 装git https gitforwindows org 正常下载即可 xff0c 装完后将git路径 xff08 D Program Files Git bin xff09 添加到环境变量中 添加方法 x
  • C语言中常用宏__FUNCTION_和__FILE__、__LINE__的用法介绍

    C C 43 43 提供了三个宏 FUNCTION FILE 和 LINE 用于定位程序运行时发生错误的位置 程序预编译时预编译器将用所在的函数名 xff0c 文件名和行号替换 当运行时错误产生后这三个宏分别能返回错误所在的函数 xff0c
  • Linux命令发送Http的get或post请求(curl和wget两种方法) 及定时任务的设置

    Http请求指的是客户端向服务器的请求消息 xff0c Http请求主要分为get或post两种 xff0c 在Linux系统下可以用curl和wget命令来模拟Http的请求 下面就来介绍一下Linux系统如何模拟Http的get或pos
  • ROS path问题解决方案

    1 问题描述 1 1 在Roboware里面直接右键运行launch文件会出现如下错误 ResourceNotFound hector gazebo worlds ROS path 0 61 opt ros kinetic share ro
  • ubuntu下socket通信

    点击打开原文链接 第一部分为C 43 43 实现 xff0c 第二部分为python实现 第一部分 该socket 的功能是在客户端输入了两个浮点数组成的字符串 xff0c 在服务端接受后将其转换为浮点数输出 xff0c 统一时刻只能有一个
  • ZED相机深度模式+ZED sl中函数介绍

    ZED SDK提供两种深度感应模式 xff1a STANDARD和FILL 该STANDARD模式是ZED的默认深度感应模式 该STANDARD模式保留距离度量和形状 xff0c 并且比FILL模式运行得更快 xff0c 但它包含由于视觉遮
  • 串行通信的数据位,起始位,停止位?

    参考链接 单片机串口通信一般是11位 xff0c 起始位1bit xff0c 数据位8bit xff0c 校验位1bit xff0c 停止位1bit xff0c 串口通信分两种一种是同步通信 xff0c 一种是异步通信 xff0c 像ps
  • linux的UDP通信编程

    这篇文章将对linux下udp socket编程重要知识点进行总结 xff0c 无论是开发人员应知应会的 xff0c 还是说udp socket的一些偏僻知识点 xff0c 本文都会讲到 尽可能做到 xff0c 读了一篇文章之后 xff0c
  • UDP组播服务端与客户端代码实例

    1 组播服务端代码 xff08 数据发送端 xff09 include lt stdio h gt include lt stdlib h gt include lt stdbool h gt include lt string h gt
  • HTTP报文

    HTTP请求报文解剖 http请求报文由3部分组成 xff08 请求行 请求头 请求体 xff09 实际请求报文 是请求方法 xff0c GET和POST是最常见的HTTP方法 xff0c 除此以外还包括DELETE HEAD OPTION
  • IMX6ULL与STM32F103的CAN通信实现

    IMX6ULL与STM32F103的CAN通信实现 硬件连接驱动层实现IMX6ULLSTM32F103ZET6 应用层实现IMX6ULLSTM32F103ZET6 结果 在上一篇博文中 xff0c 我们利用USBCAN设备及其上位机软件 x