你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【NUCLEO-C031C6】基于TobudOS的步进电机控制系统

[复制链接]
lugl 发布时间:2024-3-12 14:27

【实验器材】

1、NOCLEO-C031C6开发板

2、0.96寸OLED屏

3、旋转编码器

4、双微步进电机开发板

5、步进电机。

【软件】

1、TobudOS——国产开源的实时操作系统

2、STM32CubeMAX——意法半导体提供的免费图形化配置工具。

3、MDK5.38

【程序流程】

image.png

【实现步骤】

1、使用stm32cubeMAX新建工程,初始化TIM3为编码器模式,修改TIM3的通道GPIO为PA6、PA7来采集IO,采集旋转编码器的脉冲信号。

image.png

2、配置TIM1的通道1为PWM输出通首,配置分频为48-1,计数值是1000-1,即输出为1KHz的PWM波,同时设置脉冲什500输出50%的占空比。

image.png

3、同时配置I2C,配置速度为400K,IO配置为PB8、PB9。

image.png

4、配置时钟,主频为48M。

image.png

生成工程后开始用keil打开工程。

【移植TobudOS]

1、复制TobudOS源码的kernel、arch到生成工程的OS目录下面:

image.png

2、新建工程分组OS/kernel、OS/arch、OS/config,并把OS\kernel\core下面所有的.c添加进工程kernel工程分组。把\arm\arm-v7m\common下面的所有的.c添回进工程arch分组,把arch\arm\arm-v7m\cortex-m0+\gcc下面的port.c、port_s.S添加进工程分组arch中。

image.png

image.png

同时新建OS/config分组,添加tos_config.h,内容如下:

#ifndef _TOS_CONFIG_H_
#define  _TOS_CONFIG_H_

#include "main.h"

#define TOS_CFG_TASK_PRIO_MAX           10u

#define TOS_CFG_ROUND_ROBIN_EN          1u

#define TOS_CFG_OBJECT_VERIFY_EN        1u

#define TOS_CFG_OBJ_DYNAMIC_CREATE_EN   1u

#define TOS_CFG_EVENT_EN                1u

#define TOS_CFG_MMBLK_EN                1u

#define TOS_CFG_MMHEAP_EN               1u

#define TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE        0x400

#define TOS_CFG_MUTEX_EN                1u

#define TOS_CFG_TIMER_EN                1u

#define TOS_CFG_PWR_MGR_EN              0u

#define TOS_CFG_TICKLESS_EN             0u

#define TOS_CFG_SEM_EN                  1u

#define TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN      1u

#define TOS_CFG_FAULT_BACKTRACE_EN      0u

#define TOS_CFG_IDLE_TASK_STK_SIZE      512u

#define TOS_CFG_CPU_TICK_PER_SECOND     1000u

#define TOS_CFG_CPU_CLOCK               (SystemCoreClock)

#define TOS_CFG_TIMER_AS_PROC           1u

#define TOS_CFG_MAIL_QUEUE_EN           1u

#endif

3、添加../OS/arch/arm/arm-v7m/common/include、../OS/arch/arm/arm-v7m/cortex-m0+/gcc、../OS/kernel/core/include到头文件引用。

image.png

【OLED驱动】

移植OLED开源驱动,修改写命令、写数据两个函数内容如下:

/**
  * 函    数:OLED写命令
  * 参    数:Command 要写入的命令值,范围:0x00~0xFF
  * 返 回 值:无
  */
void OLED_WriteCommand(uint8_t Command)
{
        uint8_t buff[2];
        buff[0] = 0x00;
        buff[1] = Command;
        HAL_I2C_Master_Transmit(&hi2c1, 0x78, buff, 2, 100);
}

/**
  * 函    数:OLED写数据
  * 参    数:Data 要写入数据的起始地址
  * 参    数:Count 要写入数据的数量
  * 返 回 值:无
  */
void OLED_WriteData(uint8_t *Data, uint8_t Count)
{
        uint8_t i;
        uint8_t buff[Count +1];
  buff[0] = 0x40;

        for (i = 1; i < Count+1; i ++)
        {
                buff[i] = Data[i-1];        //依次发送Data的每一个数据
        }

        HAL_I2C_Master_Transmit(&hi2c1, 0x78, buff, Count+1, 100);
}

其余代码见工程附件。

【旋转编码器】

增加Encoder.c到工程中,代码如下:

int16_t Encoder_Get(void)
{

        int16_t temp;
        temp = (__HAL_TIM_GET_COUNTER(&htim3)+2)/4;
        if(temp)
        {
                __HAL_TIM_SET_COUNTER(&htim3,__HAL_TIM_GET_COUNTER(&htim3)-(temp*4));
                return temp;
        }
        return 0;
}

【按键代码】

按键的IO选择为PC13,与开发板的用户按键同接。配置为中断上面拉输入,下降沿触发,代码如下:

uint8_t Key_Enter;        //确认键
static void EXTI4_15_IRQHandler_Config(void);
void Key_Init(void)
{
        EXTI4_15_IRQHandler_Config();
}

/********************************************/

int8_t Key_Enter_Get(void)
{
        if(Key_Enter)
        {
                Key_Enter = 0;
                return 1;
        }
        return 0;
}

static void EXTI4_15_IRQHandler_Config(void)
{
  GPIO_InitTypeDef   GPIO_InitStructure;


  /* Enable GPIOC clock */
  __HAL_RCC_GPIOC_CLK_ENABLE();

  /* Configure PC.13 pin as input floating */
  GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;


  GPIO_InitStructure.Pull = GPIO_PULLUP;
  GPIO_InitStructure.Pin = BUTTON_USER_Pin;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);


  /* Enable and set line 13 Interrupt to the lowest priority */
  HAL_NVIC_SetPriority(EXTI4_15_IRQn, 10, 0);
  HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
}

void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == BUTTON_USER_Pin)
  {

                        Key_Enter += 1;

  }
//        __HAL_GPIO_EXTI_CLEAR_IT(BUTTON_USER_Pin);
}

【菜单设计】

先设计结构体如下:

typedef struct display_menu{
        char Name[16];                        //显示字符串
        uint8_t X;         //起始坐标
        uint8_t Y;         //起始坐标
        uint8_t Width;         //起始坐标
        uint8_t Height;         //起始坐标
        uint8_t Select;     //选中状态
        void (*func)(void);                //函数指针
}_display_block;

初始化5个显示块分别为运行状态、方向、前进、后退、运行/停止按键。其初始化代码如下:

_display_block displasy_block[5];
void main_menu(void)
{
        //显示4个块
        strcpy(displasy_block[0].Name,"stoping");
        displasy_block[0].X = 40;
        displasy_block[0].Y = 0;
        displasy_block[0].Width = 60;
        displasy_block[0].Height = 16;
        displasy_block[0].Select = SET;
        strcpy(displasy_block[1].Name,"REV");
        displasy_block[1].X = 0;
        displasy_block[1].Y = 36;
        displasy_block[1].Width = 24;
        displasy_block[1].Height = 16;
        displasy_block[1].Select = SET;
        strcpy(displasy_block[2].Name,"RUN");
        displasy_block[2].X = 48;
        displasy_block[2].Y = 36;
        displasy_block[2].Width = 32;
        displasy_block[2].Height = 16;
        displasy_block[2].Select = RESET;
        strcpy(displasy_block[3].Name,"FWD");
        displasy_block[3].X = 100;
        displasy_block[3].Y = 36;
        displasy_block[3].Width = 24;
        displasy_block[3].Height = 16;
        displasy_block[3].Select = RESET;
        strcpy(displasy_block[4].Name,"FWD");
        displasy_block[4].X = 0;
        displasy_block[4].Y = 0;
        displasy_block[4].Width = 24;
        displasy_block[4].Height = 16;
}

具体菜音实现的代码如下,获取编码状态,根据状态更新选择状态,如果被选中,则置背景为反色。当按下按键时,根据相应的位置,做出相应的状态,代码如下:

void run_play_option_class(void)
{

        uint8_t i;

        int8_t Show_i = 2;                 //显示起始下标

        int8_t Max = 3;                        //选项数量
        int8_t Incident = 0;        //编码器事件
        while(1)
        {
                Incident = Encoder_Get();                //获取按键事件//轮询;

                if(Incident)                                        //如果有按键事件;
                {
                        Show_i += Incident;

                        if(Show_i < 1)
                                        {Show_i = 1;}                        //限制选中下标

                        else
                        {

                                if(Show_i > Max )
                                        {Show_i = Max;}                        //限制选中下标
                        }
                }
                //如果有按键事件
                if(Key_Enter_Get())                //获取按键
                {
                        //如果是FWD 测设置
                        if(Show_i ==1)
                        {
                                //设置DIR为返转
                                strcpy(displasy_block[4].Name,"REV");
                                displasy_block[1].Select = SET;
                                displasy_block[3].Select = RESET;
                                HAL_GPIO_WritePin(DIR_GPIO_Port,DIR_Pin,GPIO_PIN_SET);
                        }
                        else if(Show_i == 3)
                        {
                                //设置DIR为正转
                                strcpy(displasy_block[4].Name,"FWD");
                                displasy_block[3].Select = SET;
                                displasy_block[1].Select = RESET;
                                HAL_GPIO_WritePin(DIR_GPIO_Port,DIR_Pin,GPIO_PIN_RESET);
                        }
                        else
                        {
                                //设置是否开启定时器
                                if(displasy_block[2].Select == RESET)
                                {
                                        displasy_block[2].Select = SET;
                                        strcpy(displasy_block[0].Name,"runing");
                                        strcpy(displasy_block[2].Name,"STOP");
                                        OLED_ReverseArea(displasy_block[0].X, displasy_block[0].Y, displasy_block[0].Width, displasy_block[0].Height);
                                        //开启定时器
                                        HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);        //开启定时器1的通道1的PWM输出
                                }
                                else
                                {
                                        displasy_block[2].Select = RESET;
                                        strcpy(displasy_block[0].Name,"stoping");
                                        strcpy(displasy_block[2].Name,"RUN");
                                        //开启定时器
                                        HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);        //开启定时器1的通道1的PWM输出
                                }
                        }
                }
                OLED_Clear();
                for(i=0;i<5;i++)
                {
                        OLED_ShowString(displasy_block[i].X, displasy_block[i].Y, displasy_block[i].Name, OLED_8X16);
                }
                OLED_ReverseArea(displasy_block[Show_i].X, displasy_block[Show_i].Y, displasy_block[Show_i].Width, displasy_block[Show_i].Height);
                OLED_Update();
                tos_task_delay(10);
        }
}

【程序调度】

创建两个任务,一个任务为LED定期闪烁。另一个任务为菜单显示。代码如下:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "OLED.h"
#include "menu.h"
#include "tos_k.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
k_task_t task, led_task;

k_stack_t task_stack[1024], task_stack_led[1024];

void test_task(void *Parameter)

{

        while(1)

        {

                HAL_GPIO_TogglePin(LED4_GPIO_Port,LED4_Pin);

                tos_task_delay(100);

        }

}

void led_task_entry(void *Parameter)

{
                menu_Init();

                while(1)

                {

                }

}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
        uint32_t enc1;
        uint8_t DirectionA;
  /* USER CODE END 1 */

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

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

  /* USER CODE BEGIN Init */
        k_err_t err;
  /* 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_TIM3_Init();
  MX_USART2_UART_Init();
  MX_TIM1_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
        OLED_Init();
        Key_Init();
        HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);

        HAL_GPIO_WritePin(EN_GPIO_Port,EN_Pin,GPIO_PIN_SET);
        HAL_GPIO_WritePin(DIR_GPIO_Port,DIR_Pin,GPIO_PIN_SET);
        tos_knl_init();

        err = tos_task_create(&task, "task1",test_task,NULL, 2, task_stack,1024,20);

        err = tos_task_create(&led_task, "task_led",led_task_entry,NULL, 2, task_stack_led,1024,20);

        tos_knl_start();

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

    /* USER CODE END WHILE */

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

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

【电机驱动板】

使用的步进电机的驱动器为双极微步电机评估版:

image.png

CLK接PA0,接供时钟驱动脉冲信号,EN接开发板的PA1,提供使能信号。DIR为PA4,提供正反转信号。

附工程源代码:

Encode.zip

stm32c031电机综合控制系统-封面.jpg

附视频介绍:

<iframe src="https://player.bilibili.com/player.html?aid=1601627138&bvid=BV1H2421T75D&cid=1467394607&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe> [/i][/i][/i][/i]

image.png
image.png
收藏 评论2 发布时间:2024-3-12 14:27

举报

2个回答
STMCU-管管 回答时间:2024-3-12 14:35:15
补充视频:




背影101 回答时间:2024-4-3 16:48:46

学到了

所属标签

ST中文论坛活动

即日起开启活动话题入口,之后的活动统一都放在此处,欢迎大家的加入!


最新内容

相似分享

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版