STM32F103C8T6实现CAN通讯与直流编码电机转速闭环控制

这篇具有很好参考价值的文章主要介绍了STM32F103C8T6实现CAN通讯与直流编码电机转速闭环控制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本次实验目的是通过CAN发送目标转速与转向信息,接收方在接到CAN数据流后开始对直流编码电机进行转速闭环控制。我会尽量说清每个函数,注释每一句代码,希望能对大家有所帮助。

CAN通讯基于STM32自带CAN通讯模块,配合库函数使用十分方便。关于CAN通讯可以参考站内大佬的文章,讲解的十分透彻,末尾会提供链接。 

电机驱动基于定时器1和TB6612,转速测量基于定时器2和直流电机自带编码器。

另外,可通过三个LED来显示电机状态(正转,反转和停止);通过OLED来显示转速和其他信息(如PI输出)。

目录

1.CAN通讯驱动

2.直流电机驱动(PWM)

3.直流电机驱动(转向和转速控制)

4.编码器驱动

5.PI转速闭环控制

1.CAN通讯驱动

因为目前手上只有一个STM32的最小系统板,所以采用CAN通讯的回传模式。这部分的函数包括:CAN配置函数,CAN接收中断服务函数和CAN发送函数。为了简便,我将CAN接收的数据设为外部可调用变量。具体代码如下:

#include "stm32f10x.h"                  // Device header

uint32_t get_STID;//存储标准ID
uint32_t get_EXID;//存储拓展ID
uint8_t get_IDE;//标准/拓展ID识别
uint8_t get_RTR;//数据/遥控帧识别
uint8_t get_DLC;//数据长度识别
uint8_t get_DATA[8];//存储数据
uint8_t get_FMI;//识别所经过的筛选器

void CAN_INIT(void)//CAN初始化函数
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开GPIOA时钟
	GPIO_InitTypeDef GPIO_InitStructure;//GPIO配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//配置为复用推挽
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;//打开11,12引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//开CAN时钟
	CAN_InitTypeDef CAN_InitStructure;//配置CAN
	CAN_InitStructure.CAN_ABOM = DISABLE;//关动休眠
	CAN_InitStructure.CAN_AWUM = DISABLE;//关动唤醒
	CAN_InitStructure.CAN_BS1 = CAN_BS1_5tq;//TBS1长度5Tq
	CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;//TBS2长度5Tq
	CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;//工作模式选择(单机实验下位回传模式)
	CAN_InitStructure.CAN_NART = ENABLE;//禁止重发
	CAN_InitStructure.CAN_Prescaler = 80;//80分频(最终得到10kB速率)
	CAN_InitStructure.CAN_RFLM = DISABLE;//不锁定FIFO
	CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;//最大调整步长2Tq
	CAN_InitStructure.CAN_TTCM = DISABLE;//关闭时间触发
	CAN_InitStructure.CAN_TXFP = DISABLE;//发送按ID优先级,不按邮箱顺序
	CAN_Init(CAN1, &CAN_InitStructure);
	
	CAN_FilterInitTypeDef CAN_FilterInitStructure;//CAN筛选器配置
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;//使能筛选器
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//启用FIFO0
	CAN_FilterInitStructure.CAN_FilterIdHigh = 0x00;
	CAN_FilterInitStructure.CAN_FilterIdLow = 0x00;
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x00;
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x00;//实际上可通过任意ID
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;//掩码模式
	CAN_FilterInitStructure.CAN_FilterNumber = 0;//选择筛选器组0
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;//32位长度	
	CAN_FilterInit(&CAN_FilterInitStructure);
	
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//2个抢占2个响应,这句代码已在定时器中短通道配置部分给出,这里不再需要
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;//指向CAN接收中断,定义在中容量量产非互联型,需要注意一下
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//通道使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//最高抢占等级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//第2响应等级
	NVIC_Init(&NVIC_InitStructure);
	
	CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);//使能接收中断
}

void USB_LP_CAN1_RX0_IRQHandler()//CAN接收中断服务函数。非互联型使用PA11、12引脚时,使用该中断函数名
{
	CanRxMsg RxMessage;//接收数据结构体
	CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//接收函数
	
	get_DLC = RxMessage.DLC;//接下数据长度
	get_EXID = RxMessage.ExtId;//接下拓展ID
	get_FMI = RxMessage.FMI;//接下所经过的筛选器
	get_IDE = RxMessage.IDE;//标准/拓展ID识别
	get_RTR = RxMessage.RTR;//数据/遥控帧识别
	get_STID = RxMessage.StdId;//接下标准ID
	
	get_DATA[0] = RxMessage.Data[0];
	get_DATA[1] = RxMessage.Data[1];
	get_DATA[2] = RxMessage.Data[2];
	get_DATA[3] = RxMessage.Data[3];
	get_DATA[4] = RxMessage.Data[4];
	get_DATA[5] = RxMessage.Data[5];
	get_DATA[6] = RxMessage.Data[6];
	get_DATA[7] = RxMessage.Data[7];
}

void send_CAN(uint32_t STID, uint32_t EXID, uint8_t IDE, uint8_t RTR, uint8_t DLC, uint8_t DATA[8])//CAN发送函数
{
	CanTxMsg TxMessage;
	TxMessage.DLC = DLC;
	TxMessage.StdId = STID;
	TxMessage.ExtId = EXID;
	TxMessage.IDE = IDE;
	TxMessage.RTR = RTR;	
	
	TxMessage.Data[0] = DATA[0];
	TxMessage.Data[1] = DATA[1];
	TxMessage.Data[2] = DATA[2];
	TxMessage.Data[3] = DATA[3];
	TxMessage.Data[4] = DATA[4];
	TxMessage.Data[5] = DATA[5];
	TxMessage.Data[6] = DATA[6];
	TxMessage.Data[7] = DATA[7];
	
	CAN_Transmit(CAN1, &TxMessage);
}

再将各个函数以及变量在头文件中声明一下:

#ifndef __CAN_H
#define __CAN_H

extern uint32_t get_STID;//存储标准ID
extern uint32_t get_EXID;//存储拓展ID
extern uint8_t get_IDE;//标准/拓展ID识别
extern uint8_t get_RTR;//数据/遥控帧识别
extern uint8_t get_DLC;//数据长度识别
extern uint8_t get_DATA[8];//存储数据
extern uint8_t get_FMI;//识别所经过的筛选器

void CAN_INIT(void);
void send_CAN(uint32_t STID, 
			  uint32_t EXID, 
			  uint8_t IDE, 
			  uint8_t RTR, 
			  uint8_t DLC, 
			  uint8_t DATA[8]);//CAN发送函数

#endif

以上为CAN驱动部分,CAN发送函数可以在主函数中直接调用,通过CAN接收中断服务函数来读数据,并转移至内存。

2.直流电机驱动(PWM)

这部分包括PWM配置函数和PWM占空比调节函数。PWM基于定时器1,具体代码如下:

#include "stm32f10x.h"                  // Device header


void TIMER1_INIT(void)//配置带有中断和PWM功能的定时器
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开GPIOA时钟
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//10引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//开定时器1时钟
	TIM_InternalClockConfig(TIM1);//定时器1采用内部时钟
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//时基单元配置
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//不分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//上升计数模式
	TIM_TimeBaseInitStructure.TIM_Period = 100-1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;//72分频配合100的计数周期,则每秒进10k次中断,即载频为10kHz
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;//比较器配置
	TIM_OCStructInit(&TIM_OCInitStructure);//初始化其他未设置的变量
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//高电平有效
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性不翻转
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能比较输出
	TIM_OCInitStructure.TIM_Pulse = 0;//比较器装载值
	TIM_OC3Init(TIM1, &TIM_OCInitStructure);//PA10引脚对应第三通道(OC3)
	TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//开定时器中断
	
//	TIM_BDTRInitTypeDef TIM_BDTRInitStructure;//配置死区,直流电机不需要
//	TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
//	TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
//	TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
//	TIM_BDTRInitStructure.TIM_DeadTime = 11;
//	TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
//	TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
//	TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
//	TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
	TIM_ClearITPendingBit(TIM1, TIM_IT_Update);清中断标志位,防止上电/复位进中断
	TIM_Cmd(TIM1, ENABLE);//开定时器
	TIM_CtrlPWMOutputs(TIM1, ENABLE);//pwm主使能(高级定时器独有)
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级配置
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;//指定到定时器1更新中断
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//通道使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//最高抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//最高响应优先级
	NVIC_Init(&NVIC_InitStructure);//配置中断通道
	
	
}

void set_speed(uint8_t speed)//通过调用比较器赋值函数实现调节PWM占空比,从而实现调速
{
	TIM_SetCompare3(TIM1, speed);
}

对于定时器1(高级定时器),别忘了“TIM_CtrlPWMOutputs(TIM1, ENABLE);”。需要将PWM信号送入6612的PWMA引脚。最后将函数和变量在头文件中声明一下:

#ifndef __TIMER1_H
#define __TIMER1_H

void TIMER1_INIT(void);
void set_speed(uint8_t speed);

#endif

3.直流电机驱动(转向和转速控制)

直流电机的转向控制是通过STM32的两个引脚对6612的AIN1和AIN2引脚发送高低电平来控制,转速是通过STM32的PA10引脚送入6612的PWMA引脚的PWM信号来控制。接线如下图所示:

STM32F103C8T6实现CAN通讯与直流编码电机转速闭环控制

 AIN1和AIN2真值表如下,这里的正反转是相对的,大家可根据需要搭配。

STM32F103C8T6实现CAN通讯与直流编码电机转速闭环控制

 这部分包括PA6,PA7(转向控制)引脚配置函数,CAN接收数据流解码函数和转速转速转向指定控制。特别的,CAN发送和接收最多8个元素的8位无符号整形数组,本实验用数组的第一个元素指定正反转和停止,0代表停止,1代表正转,2代表反转;用数组的第二个元素来表示转速大小。大家也可采用其他方法,我这里只是为了简单。

在开环状态下,PWM输出饱和时(输出100),直流电机转速rpm=126,所以在转速控制部分需要进行转换:spd=spd*100/126。具体代码如下:

#include "stm32f10x.h"                  // Device header
#include "Timer1.h"
#include "CAN.h"

void DCmotor_INIT(void)//转向控制引脚配置
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//配置H桥控制引脚(旋转方向)
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_6| GPIO_Pin_7;//2,3,4脚控制三个LED,6,7脚进6612
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	
}

int8_t speed_trans(uint8_t direction, uint8_t speed)//CAN接收数据流解码
{
	int8_t spd;
	direction = get_DATA[0];//转向/停止
	speed = get_DATA[1];//转速大小
	if(direction==1)
	{
		spd = speed;
	}
	else if(direction==2)
	{
		spd = -speed;
	}
	else
	{
		spd = 0;
	}
	return spd;
}

void speed_CTL(int8_t spd)//调速/转向
{
	spd = spd*100/126;
	if(spd>0)//正向旋转
	{
		GPIO_WriteBit(GPIOA, GPIO_Pin_6, Bit_SET);//转向控制
		GPIO_WriteBit(GPIOA, GPIO_Pin_7, Bit_RESET);
		set_speed(spd);
		GPIO_SetBits(GPIOA, GPIO_Pin_2);//LED指示灯
		GPIO_ResetBits(GPIOA, GPIO_Pin_3);
		GPIO_ResetBits(GPIOA, GPIO_Pin_4);
	}
	else if(spd<0)//反向旋转
	{
		GPIO_WriteBit(GPIOA, GPIO_Pin_7, Bit_SET);
		GPIO_WriteBit(GPIOA, GPIO_Pin_6, Bit_RESET);
		set_speed(-spd);
		GPIO_SetBits(GPIOA, GPIO_Pin_4);
		GPIO_ResetBits(GPIOA, GPIO_Pin_3);
		GPIO_ResetBits(GPIOA, GPIO_Pin_2);
	}
	else//停
	{
		GPIO_WriteBit(GPIOA, GPIO_Pin_6, Bit_SET);
		GPIO_WriteBit(GPIOA, GPIO_Pin_7, Bit_SET);
		GPIO_SetBits(GPIOA, GPIO_Pin_3);
		GPIO_ResetBits(GPIOA, GPIO_Pin_4);
		GPIO_ResetBits(GPIOA, GPIO_Pin_2);
	}
}

 最后将函数与变量在头文件声明一下:

#ifndef __DCMOTOR_H
#define __DCMOTOR_H

void DCmotor_INIT(void);
void speed_CTL(int8_t spd);
int8_t speed_trans(uint8_t direction, uint8_t speed);
	
#endif

4.编码器驱动

本次实验直流电机转速测量基于电机自带两相霍尔编码器和定时器2。AB两相编码器脉冲信号需接入定时器2的1,2通道(PA0和PA1)。特别的,编码器电源需接3.3V。

直流电机减速比位1:48,输出轴旋转1周,编码器AB两相一共输出2496个上升和下降沿,因为载频较高,所以每进100次中断(10ms)读取一次定时器2的计数值。当电机正向旋转(从零上升计数)时,输出轴的转速为rpm=计数值*100*60/2496。

当电机反向旋转(从5000向下计数)时,因为电机最高转速为rpm=126,所以计数器在10ms内,得到的数值不会小于4947(正转时不会大于53),所以可以通过判断计数值确定是否反转,反转时,转速为rpm=(计数值-5000)*100*60/2496。具体代码如下:

#include "stm32f10x.h"                  // Device header
#include "CAN.h"

void encoder1_INIT(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开GPIOA时钟
		
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;//开0,1引脚(定时器2的1,2通道)
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);//GPIOA初始化
	
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开定时器2时钟
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//不分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	TIM_TimeBaseStructure.TIM_Period = 5000;//计数到5000
	TIM_TimeBaseStructure.TIM_Prescaler = 0;
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
	
	TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12,
                                TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//定时器2编码计数配置,1,2通道同时计数
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICStructInit(&TIM_ICInitStructure);
	TIM_ICInitStructure.TIM_ICFilter = 0x00;//不使用滤波
	TIM_ICInit(TIM2, &TIM_ICInitStructure);
	TIM_SetCounter(TIM2,0);//清零定时器计数值
}

int16_t get_rpm(void)//转速计算
{
	int16_t rpm;
	uint16_t count;
	count = TIM_GetCounter(TIM2);//接定时器2编码计数值
	if(count>2500)//如果反转
	{
		rpm = (count-5000)*100*60/2496;
	}
	else
	{
		rpm = (count)*100*60/2496;
	}
	
	return rpm;
}

 最后将函数与变量在头文件声明一下:

#ifndef __ENCODER_H
#define __ENCODER_H

void encoder1_INIT(void);
int16_t get_rpm(void);

#endif

5.PI转速闭环控制

PI转速闭环控制可表示为:PI_out=Kp*err+Ki*err_integral,PI调节的输出量直接送给转速控制。这部分与定时器1中断服务函数以及主函数放在了一页,具体代码如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CAN.h"
#include "encoder.h"
#include "DCmotor.h"
#include "Timer1.h"

uint16_t i;
int16_t rpm;
uint16_t count;
int8_t spd_tgt,PI_out;
float RPM,err,err_old_intg,err_intg;//PI调节相关参数
float kp = 0.3;
float ki = 0.015;
float kp_out = 0;
float PI_value = 0;

int main()
{	
	uint8_t DATA[8] = {1,55,2,3,4,5,6,7};//第一个参数控制转向/停止,第二个参数指定转速,限制100
	
	OLED_Init();
	CAN_INIT();
	ADC_INIT();
	TIMER1_INIT();
	DCmotor_INIT();
	encoder1_INIT();
		
	OLED_ShowString(1,1,"CAN Data:");
	OLED_ShowString(3,1,"Speed rpm:");
		
	send_CAN(0x00,               //标准帧ID(uint32)
			 0xFE,               //扩展帧ID(uint32,但只有29位,0 to 0x1FFFFFFF)
	         CAN_Id_Extended,    //标准/拓展ID识别
		     CAN_RTR_Data,       //数据/遥控帧识别
		     8,                  //数据长度识别
			 DATA);              //8个字节数据

	while(1)
	{
		OLED_ShowSignedNum(2, 1, PI_out, 5);
		OLED_ShowSignedNum(4, 1, rpm, 5);
	}
}

int8_t PI(int8_t target_value)//PI调节函数
{
	RPM = get_rpm();//获取测量转速
	err = target_value - RPM;//得到转速偏差
	kp_out = err*kp;
	err_intg = err_old_intg + err;//计算偏差的积累量
	err_old_intg = err_intg;
	PI_value = kp_out + ki*err_intg;//PI输出
	
	if(PI_value>125)//限幅
	{
		PI_value = 125;
	}
	if(PI_value< (-125))
	{
		PI_value= -125;
	}
    PI_out = PI_value;
	return PI_out;
}

void TIM1_UP_IRQHandler(void)//定时器1中断服务函数,执行转速闭环控制
{
	if(TIM_GetITStatus(TIM1, TIM_IT_Update)==SET)//查定时器1中断标志位
	{	
		i++;
		if(i>=100)//每进100次中断,计算一次转速
		{
			rpm = get_rpm();//调用转速计算
			spd_tgt = speed_trans(get_DATA[0], get_DATA[1]);//CAN数据解码为转速(大小,方向)
			PI_out = PI(spd_tgt);//调用PI调节
			speed_CTL(PI_out); //赋予转向/转速			
			TIM_SetCounter(TIM2,0);//定时器重载
			i = 0;
		}
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清定时器1中断标志位
	}
}

实验效果如下图所示,中间的6050模块大家请忽略,与本实验无关。

STM32F103C8T6实现CAN通讯与直流编码电机转速闭环控制

 目标转速为0

 STM32F103C8T6实现CAN通讯与直流编码电机转速闭环控制

  目标转速为36

 STM32F103C8T6实现CAN通讯与直流编码电机转速闭环控制

   目标转速为-36

介于水平有限,文中的错误和不足还望大家批评指正,谢谢!

CAN通讯参考:

STM32 CAN通信详解_sgh0609的博客-CSDN博客_stm32can通信转:https://blog.csdn.net/CSDN_Yoa/article/details/81384924 并结合自己项目上CAN的配置觉得该文章很好希望帮助想了解CAN的网友。首先是自己工程中自己的东西分享给大家,后面内筒是转载他人的优秀文档。由于STM32中我使用的扩展标识符(ID)是29位(28~0),STM32的过滤器和掩码是32位分别映射到10~0、28~0、IDE、RTR、0,上;那么我们就可以根据这些内容建立自己的过滤和掩码。其中不建议使用CAN接收中的EXtID,因为掩码中为零.https://blog.csdn.net/sgh69/article/details/121243632?spm=1001.2014.3001.5506文章来源地址https://www.toymoban.com/news/detail-400125.html

到了这里,关于STM32F103C8T6实现CAN通讯与直流编码电机转速闭环控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 功耗测评 | STM32F103C8T6

    STM32F103C8T6 MCU越来越广泛的应用在生产生活的各个领域,外接丰富的传感器、功能模块、通信模块、显示存储等可以形成各种可样的产品项目应用。对于功耗要求比较高的产品,一般会选择STM32L系列的MCU,但是从功耗的评测角度,逻辑上是基本相似的。 在很多应用场合中都对

    2024年02月07日
    浏览(45)
  • STM32F103C8T6系统板

    1.电源部分 2.复位 3.晶振 4. 电源电路——防反接 有关二极管 漫谈二极管防电源反接电路 本次采用上图右下角的NMOS防反接电路。 电源电路——电源芯片 AMS1117是AMS公司的 ,LM1117是NS(美国国家半导体)的,LM1117要贵很多,所以一般的用AMS1117就可以了。 (以下分析参考STM32F10

    2024年02月02日
    浏览(73)
  • 舵机控制(STM32F103C8T6)

            本文是以 STM32F103C8T6 作为主控芯片,通过PB6端口输出PWM,实现控制180°舵机。 (一)概述         舵机是一种位置伺服驱动器器,是一种带有输出轴的小装置。当我们向伺服器发送一个控制信号时,输出轴就可以转到特定的位置。只在控制信号持续不变,伺服机构就

    2023年04月09日
    浏览(57)
  • STM32F103C8T6串口通信

      首先来看一下需要操作的函数,以及配置的步骤: 图1                                                  图2   Code: usart.c #include \\\"usart.h\\\" void ustart_Init(void ) { GPIO_InitTypeDef GPIO_Init_Ustar ; // 定义输出端口TX的结构体对象 USART_InitTypeDef USTART_Init; // 定义串口初始化结构体对象

    2024年02月16日
    浏览(53)
  • 【STM32 IAP技术实现】适合小白“食用”(以STM32F103C8T6为例)

      想必大家对 单片机烧录 一词都不陌生,就是将程序下载到我们的板子(MCU)里面。常见的烧录方法有用Keil下载,或者是编译出Hex文件通过烧录软件(上位机例如:muisp、flymcu)、烧录器软件(例如:J-LINK、ST-LINK)烧录,从程序的角度来看通过烧录,它被“更新”了。

    2024年02月03日
    浏览(79)
  • STM32F103C8T6串口调试篇

    项目开发中,当出现bug时,由于不知道某个变量的值,所以很难定位问题,针对此问题,串口调试脱颖而出。通过串口printf()实时将需要显示的信息打印出来,这样就很方便的定位问题。 串口设置方法 1.购买调试器pwlink2。参考STM32F103C8T6程序烧录方法_stm32f103c8t6如何烧录_流

    2024年02月12日
    浏览(64)
  • stm32f103c8t6的外部中断

    在单片机中存在着中断系统,这个系统的逻辑和功能在51单片机中已经有所了解。 1.在32单片机中的内核有一个nvic 里面存放许多优先级的设定,外接许多中断源,比如“exti、tim、adc、usart等”接入之后,nvic再通过中断优先级进行排队,再内接入cpu中进行处理,这样子大大减少

    2024年02月09日
    浏览(54)
  • [STM32F103C8T6]ADC转换

    什么是ADC转换? ADC转换的全称是: Analog-to-Digital Converter ,指模拟 / 数字转换器 ADC的性能指标: ADC分辨率: SSA与VREF-一起接到地,DDA与VREF+接到3.3v,所以ADC转换的范围是0---3.3v 所以最后的ADC转换值应该是我们的测量值*分辨率    分辨率 = 3.3v/2^12 = (3.3/4096)   12位的转换器所

    2024年02月06日
    浏览(47)
  • STM32F103C8T6移植FreeRTOS

    根据自己开发板型号下载地址:STM32相关内容下载地址 SDK直接下载地址:STM32直接下载地址 下载参考博客 FreeROTS下载地址 选用V9.0.0 版本 个人创建目录如下,可做参考 keil目录 链接头文件 • 修改堆栈大小 • 修改中断函数名 去掉stm32f10x_it.c终端函数 增加FreeRTOS中断 特别解释

    2024年02月12日
    浏览(56)
  • STM32F103C8T6 按键扫描输入

    第一章 STM32F103C8T6 点亮LED灯 系列文章目录 前言 一、原理  1.按键类型  2.按键消抖 3.IO口输入配置 1)模拟输出 2)浮空输入模式 3)下拉输入模式(PULL DOWN) 4)上拉输入模式(PULL UP) 二、代码部分 main.c key.c key.h 总结         上一章我们成功入门了STM32F103C8T6,今天我们来

    2023年04月23日
    浏览(78)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包