战舰V3移植CANopenNode并驱动电机全流程

目录

CANopenNode基础代码移植

电机驱动代码编写


CANopenNode基础代码移植

使用cubemx新建工程,设置RCC,设置SYS。激活CAN,配置CAN的波特率500k(这个波特率需要和电机驱动器的波特率对应,并不一定是500k)。

配置定时器6,时间为1ms,根据公式计算得到参数PSC取72-1,ARR取1000-1

配置板子上的两个LED

配置串口,方便调试

把CANopenNode的代码复制到项目文件夹中,注意一共两个文件

在keil里添加文件

设置头文件路径,在设置CANopenNode的时候只需要添加下面一个头文件路径就行

CANopenNode_STM32项目的代码就添加到这一级

main函数,记得引入头文件

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "CO_app_STM32.h"
/* USER CODE END Includes */

/* USER CODE BEGIN PV */
CANopenNodeSTM32 canOpenNode;
/* USER CODE END PV */


/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_CAN_Init();
  MX_TIM6_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  
  canOpenNode.CANHandle = &hcan;
  canOpenNode.HWInitFunction = MX_CAN_Init;
  canOpenNode.timerHandle = &htim6;
  canOpenNode.desiredNodeID = 1;
  canOpenNode.baudrate = 500;
  canopen_app_init(&canOpenNode);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, !canOpenNode.outStatusLEDRed);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, !canOpenNode.outStatusLEDGreen);
    canopen_app_process();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

定时器回调函数编写

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim==canOpenNode.timerHandle)
	{
	    canopen_app_interrupt();
	}
}
/* USER CODE END 4 */

串口进行printf重定向

在usart.h中引入stdio.h

在usart.c中进行重定向

/* USER CODE BEGIN 1 */
struct __FILE
{
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
};
FILE __stdout;

int fputc(int ch, FILE *f)
{
    while ((USART1->SR & 0X40) == 0);     /* 等待上一个字符发送完成 */

    USART1->DR = (uint8_t)ch;             /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}
/* USER CODE END 1 */

如果提示这个报错,勾选使用MicroLIB

下载到单片机,运行

打开调试助手,如果出现下面的提示,原因是堆空间不够

在CUBEMX中修改Heap Size,由0x200,修改为0x2000

生成项目,下载到单片机,出现以下提示,说明代码正常运行

通过CAN分析仪接收到板子的上线报文说明移植成功

电机驱动代码编写

首先确保电机支持CANopen通讯。

本次使用的电机符合CIA402协议,发送的SDO报文格式如下

第一部分是COB-ID,长度1个字节,电机的node-ID默认为0x01,所以报文ID一直是601。

第二部分是CMD,长度1个字节,其实就是数据部分的长度,

0x2F: 一个字节长度 // 1 byte data length
0x2B: 两个字节长度 // 2 byte data length
0x27: 三个字节长度 // 3 byte data length
0x23: 四个字节长度 // 4 byte data length
0x22: 未指定字节长度

第三部分是电机的对象字典(也就是节点的OD)中的索引,长度2个字节。索引发送的是小段模式,比如这一部分为0X40 0X60,实际操作的索引地址为0X6040。

第四部分是电机的对象字典中的子索引,长度1个字节

第五部分是要写入的数据,数据发送的时候是小端模式,低字节数据先发送,长度由第二部分决定。

新建一个demo_motor.c

写入以下代码

#include "CO_app_STM32.h"
#include "301/CO_SDOclient.h"
#include "GPIO.h"
#include "usart.h"


/*
*********************************************************************************************************
*	                                           宏定义和变量
*********************************************************************************************************
*/
#define CANopenSlaveID   0x01

uint8_t g_ucTempBuf[20];//存放SDO报文的数据部分

extern CO_t* CO;



/*
*********************************************************************************************************
*	                                            本地函数声明
*********************************************************************************************************
*/
CO_SDO_abortCode_t write_SDO(CO_SDOclient_t *SDO_C, uint8_t nodeId,
                             uint16_t index, uint8_t subIndex,
                             uint8_t *data, size_t dataSize);
void motorAccelerate(void);
void motorDecelerate(void);

/*
*********************************************************************************************************
*	函 数 名: DemoMotor
*	功能说明: 根据按键不同进行响应
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void DemoMotor(void)
{
		uint8_t key=key_scan(0);//得到键值
		if(key==3)
		{
			
		}
		else if(key==0)
		{
			printf("key0按下,电机启动\r\n");
			motorAccelerate();
		}
		else if(key==1)
		{
			printf("key1按下,电机停止\r\n");
			motorDecelerate();
		}
		else if(key==2)
		{
			printf("key is key2\r\n");
		}
		else 
		{
			printf("key error\r\n");
		}
}

/*
*********************************************************************************************************
*	函 数 名: motorAccelerate
*	功能说明: 电机在PV模式下开始运行
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void motorAccelerate(void)
{
		//初始化伺服状态机
		g_ucTempBuf[0]=0x00;
		g_ucTempBuf[1]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x6040, 0x00, g_ucTempBuf, 2);
		//设置细分参数4000 pulse/r
		g_ucTempBuf[0]=0xA0;
		g_ucTempBuf[1]=0x0F;
		g_ucTempBuf[2]=0x00;
		g_ucTempBuf[3]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x2001, 0x00, g_ucTempBuf, 4);
		//设置电流3000mA
		g_ucTempBuf[0]=0xB8;
		g_ucTempBuf[1]=0x0B;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x2000, 0x00, g_ucTempBuf, 2);
		//设置加速时间2000pulse/^2
		g_ucTempBuf[0]=0xD0;
		g_ucTempBuf[1]=0x07;
		g_ucTempBuf[2]=0x00;
		g_ucTempBuf[3]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x6083, 0x00, g_ucTempBuf, 4);
		//设置减速时间2000pulse/^2
		g_ucTempBuf[0]=0xD0;
		g_ucTempBuf[1]=0x07;
		g_ucTempBuf[2]=0x00;
		g_ucTempBuf[3]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x6084, 0x00, g_ucTempBuf, 4);
		//设置目标速度8000pulse/^2
		g_ucTempBuf[0]=0x40;
		g_ucTempBuf[1]=0x1F;
		g_ucTempBuf[2]=0x01;
		g_ucTempBuf[3]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x60FF, 0x00, g_ucTempBuf, 4);
		//切换工作模式PV
		g_ucTempBuf[0]=0x03;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x6060, 0x00, g_ucTempBuf, 1);
		//切换驱动器状态机
		g_ucTempBuf[0]=0x06;
		g_ucTempBuf[1]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x6040, 0x00, g_ucTempBuf, 2);
		//切换驱动器状态机
		g_ucTempBuf[0]=0x07;
		g_ucTempBuf[1]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x6040, 0x00, g_ucTempBuf, 2);
		//开始运行
		g_ucTempBuf[0]=0x0F;
		g_ucTempBuf[1]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x6040, 0x00, g_ucTempBuf, 2);
}

/*
*********************************************************************************************************
*	函 数 名: motorDecelerate
*	功能说明: 电机停止
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void motorDecelerate(void)
{
		//减速停止
		g_ucTempBuf[0]=0x00;
		g_ucTempBuf[1]=0x00;
		g_ucTempBuf[2]=0x00;
		g_ucTempBuf[3]=0x00;
		write_SDO(CO->SDOclient, CANopenSlaveID, 0x60FF, 0x00, g_ucTempBuf, 4);
}



CO_SDO_abortCode_t write_SDO(CO_SDOclient_t *SDO_C, uint8_t nodeId,
                             uint16_t index, uint8_t subIndex,
                             uint8_t *data, size_t dataSize)
{
    CO_SDO_return_t SDO_ret;
    bool_t bufferPartial = false;
 
    // setup client (this can be skipped, if remote device is the same)
    SDO_ret = CO_SDOclient_setup(SDO_C,
                                 CO_CAN_ID_SDO_CLI + nodeId,
                                 CO_CAN_ID_SDO_SRV + nodeId,
                                 nodeId);
    if (SDO_ret != CO_SDO_RT_ok_communicationEnd) {
        return -1;
    }
 
    // initiate download
    SDO_ret = CO_SDOclientDownloadInitiate(SDO_C, index, subIndex,
                                           dataSize, 1000, false);
    if (SDO_ret != CO_SDO_RT_ok_communicationEnd) {
        return -1;
    }
 
    // fill data
    size_t nWritten = CO_SDOclientDownloadBufWrite(SDO_C, data, dataSize);
    if (nWritten < dataSize) {
        bufferPartial = true;
        // If SDO Fifo buffer is too small, data can be refilled in the loop.
    }
 
    //download data
    do {
        uint32_t timeDifference_us = 10000;
        CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE;
 
        SDO_ret = CO_SDOclientDownload(SDO_C,
                                       timeDifference_us,
                                       false,
                                       bufferPartial,
                                       &abortCode,
                                       NULL, NULL);
        if (SDO_ret < 0) {
            return abortCode;
        }
 
        //bsp_DelayUS(timeDifference_us);
				HAL_Delay(timeDifference_us/1000);
    } while(SDO_ret > 0);
 
    return CO_SDO_AB_NONE;
}

在CO_fifo.h中,把这行代码中括号中的0修改为1

在CO_SDOclient.h中,把这行代码中括号中的0修改为1

在CUBEMX配置按键GPIO

在gpio.h中声明按键扫描函数

在gpio.c中写按键扫描的代码

/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */
#define KEY0 HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)
#define KEY1 HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)
#define KEY2 HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_2)
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
//mode 0 不支持连按
//mode 1 支持连按
uint8_t key_scan(uint8_t mode)
{
	static uint8_t key_up=1;//按键按松开标志
	if(mode)key_up=1; //支持连按
	if(key_up&&(KEY0==0||KEY1==0||KEY2==0))
	{
		HAL_Delay(10);//去抖动
		key_up=0;
		if(KEY0==0)return 0;
		else if(KEY1==0)return 1;
		else if(KEY2==0)return 2;
	}
	else if(KEY0==1&&KEY1==1&&KEY2==1)
		key_up=1;
	return 3;// 无按键按下
}
/* USER CODE END 2 */

在main函数中声明demo函数为外部引用

在while循环中调用这个函数

把板子上的CAN模块和电机的CANopen通讯端口连接起来。上电之后,板子上按KEY0启动电机,按KEY1电机停止

可以配合调试助手,方便查看过程

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 战舰v3是一种针对学习和开发的机器人控制器,其使用的是Hal库。Hal(Hardware abstraction layer)库是一个硬件抽象层,简化了机器人开发的过程。 战舰v3 Hal库例程提供了一系列代码示例,用于不同功能和操作。通过这些例程,我们可以更好地理解和掌握战舰v3的控制和编程。 例如,在舵机控制方面,Hal库例程可以演示如何使用代码来控制舵机的旋转角度和速度。我们可以通过调整代码中的参数,使舵机旋转到指定角度,并控制其转动的速度。这为我们在机器人开发中实现舵机控制提供了参考。 另外,Hal库例程还包括了关于电机控制、传感器读取、通信等方面的示例。这些例程提供了不同硬件和功能的代码范例,我们可以通过学习和理解这些代码,实现自己的机器人项目。 战舰v3 Hal库例程的存在使得机器人开发者能够更好地使用战舰v3控制器,并且能够更方便地学习和掌握机器人编程。通过研究这些例程,我们可以更好地了解机器人的各个方面,并应用到我们自己的机器人项目中去。随着对这些例程的不断熟悉和掌握,我们可以编写出更复杂和功能强大的机器人控制代码。 ### 回答2: 战舰V3是一款由创客维艾科技推出的开源教育单片机主控板,配有强大的HAL库。HAL(Hardware Abstraction Layer)库是一种硬件抽象层,用于简化硬件编程,使开发者能够更轻松地调用底层硬件功能。 在战舰V3的HAL库例程中,有许多示例代码可供参考和学习。这些例程覆盖了各种功能和模块,包括GPIO(通用输入输出)、串口通信、电机控制、LCD显示、传感器读取等等。通过学习这些例程,用户可以了解如何使用战舰V3的HAL库进行开发,从而快速上手和开展各种项目。 例如,在GPIO方面,HAL库例程可以教会我们如何设置和读取IO口的状态,如何使用中断来实现事件触发等。在串口通信方面,例程展示了如何进行数据的发送和接收,如何配置波特率等。电机控制方面,示例代码可以指导我们如何使用PWM信号来控制电机的速度和方向。在LCD显示方面,HAL库提供了一系列函数来简化字符和图形的显示,并且例程展示了如何使用这些函数进行LCD的初始化和数据输出。传感器读取方面,HAL库可以方便地读取各类传感器的数据,并通过例程展示了如何读取和解析传感器的数据。 战舰V3的HAL库例程的存在极大地简化了硬件开发的过程,减少了开发者的学习和调试时间。无论是初学者还是有一定经验的开发者,都可以通过参考和借鉴这些例程来实现自己的项目。 ### 回答3: 战舰V3是一种基于Arduino平台的教育机器人套件,其核心控制库为HAL库(Hardware Abstraction Layer library)。HAL库提供了一系列实用的例程,可以帮助用户快速上手并编写程序控制战舰V3。 首先,HAL库提供了一些基础功能的例程,比如灯光控制、蜂鸣器控制以及按键检测等。通过这些例程,用户可以学习如何通过编程控制战舰V3的各个组件,实现一些简单的功能。 此外,HAL库还提供了许多传感器的例程,比如红外传感器、超声波传感器和光电传感器等。这些传感器可以帮助用户感知周围环境,并根据感知结果进行相应的控制。通过学习这些例程,用户可以掌握如何使用传感器来构建互动性更强的机器人应用。 HAL库还包括了一些高级功能的例程,比如舵机控制、电机控制以及串口通信等。这些例程可以帮助用户实现更复杂的控制任务,比如机器人的运动控制和与外部设备的通信。通过学习这些例程,用户可以深入了解战舰V3的硬件结构和扩展性,为进一步的项目开发打下基础。 总而言之,战舰V3 HAL库例程提供了丰富的教学资源和示例代码,能够帮助用户快速上手并掌握战舰V3的编程和控制技能。通过学习这些例程,用户可以拓展自己的创造力,设计并实现更多有趣的机器人应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值