一、工程实现
1.led模块


仅实现状态指示,这里不多做介绍。
2.新建time_open_control模块

-
驱动板使能管脚
-
TIM4实现1ms中断一次,time4_count计数++
-
TIM2输出3路25KHz PWM
-
TIM2配置为中央对齐模式(和向上计数模式模式频率计算不同),频率 = 72M / ((预分频 + 1) × 2 × ARR )
time_open_control.h#ifndef __TIME_OPEN_CONTROL_H
#define __TIME_OPEN_CONTROL_H#include "system.h" //使用位带操作
#define PWM_Period 1440 //在BLDCMotor.c文件中TIM_SetCompare4(TIM2, Tc*PWM_Period)中会使用
#define MOTOR_ENABLE PCout(3) = 1
#define MOTOR_DISABLE PCout(3) = 0extern uint32_t time4_count;
void MOTOR_EN_PIN_Init(void);
void TIM4_1MS_Init(void);
void TIM2_CH1_CH3_CH4_PWM_Init(u16 per, u16 psc, u16 pulse1, u16 pulse2, u16 pulse3);#endif
time_open_control.c
#include "system.h"
#include "time_open_control.h"
uint32_t time4_count = 0;
void MOTOR_EN_PIN_Init(void)
{
GPIO_InitTypeDef MY_GPIO_Init; //定义结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //在led.c中使能了,重复使能不影响
MY_GPIO_Init.GPIO_Pin = GPIO_Pin_3; //开发板LED4复用了,不影响
MY_GPIO_Init.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出模式
MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOC, &MY_GPIO_Init);
GPIO_ResetBits(GPIOC, GPIO_Pin_3); //电机disable,但LED4会被点亮
}
/***************************************************************************/
//功能:定时器2 通道1、3、4输出高有效的PWM(这里使用的是中央对齐模式)
//参数1:per 自动重装寄存器值
//参数2:psc 分频系数
//参数3:pulse1 占空比控制 第一路占空比为 = pulse1/per
//参数4:pulse2 占空比控制 第二路占空比为 = pulse2/per
//参数5:pulse3 占空比控制 第三路占空比为 = pulse3/per
//向上计数模式:
//频率 = 72M / ((预分频 + 1) × (ARR+1) )
//中央对齐模式:
//频率 = 72M / ((预分频 + 1) × 2 × ARR )
void TIM2_CH1_CH3_CH4_PWM_Init(u16 per, u16 psc, u16 pulse1, u16 pulse2, u16 pulse3)
{
GPIO_InitTypeDef MY_GPIO_Init;//定义结构体变量
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能IO复用功能端口时钟(因为IO口需要重映射) 这里在TIM3_CH1_CH2_PWM_Init也有调用
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB端口时钟
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA端口时钟 在串口中已经使能过了
//GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //由查表可知,PB10、PB11使用的是完全重映射管脚
GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2, ENABLE); //由查表可知,PB10(CH3)、PB11(CH4)使用的也可以是重映射2管脚,以及使用PA0(CH1)
//CH1
MY_GPIO_Init.GPIO_Pin = GPIO_Pin_0;
MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOA, &MY_GPIO_Init);
//CH3
MY_GPIO_Init.GPIO_Pin = GPIO_Pin_10;
MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOB, &MY_GPIO_Init);
//CH4
MY_GPIO_Init.GPIO_Pin = GPIO_Pin_11;
MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOB, &MY_GPIO_Init);
//初始化定时器参数
TIM_DeInit(TIM2);
TIM_TimeBaseStructure.TIM_Period = per;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //一般配置为TIM_CKD_DIV1
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
//初始化PWM CH1输出参数
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM输出模式1
TIM_OCInitStructure.TIM_Pulse = pulse1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出高有效,9g舵机控制要求
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//初始化PWM CH3输出参数
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM输出模式1
TIM_OCInitStructure.TIM_Pulse = pulse2;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出高有效,9g舵机控制要求
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
//初始化PWM CH4输出参数
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM输出模式1
TIM_OCInitStructure.TIM_Pulse = pulse3;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出高有效,9g舵机控制要求
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
//使能输出比较预装载寄存器
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
//使能 ARR 预装载寄存器
TIM_ARRPreloadConfig(TIM2, ENABLE);
//开启定时器
TIM_Cmd(TIM2, ENABLE);
//TIM_CtrlPWMOutputs() 如果使用高级定时器输出PWM,还需要调用该函数使能主输出,否则不能输出PWM !!!
}
/***************************************************************************/
//1MS产生一次中断
void TIM4_1MS_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//TIM4时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
//初始化NVIC,这里未配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_TimeBaseInitStructure.TIM_Period = 1000-1; //1ms
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1; //72分频 = 1MHz
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure);
//开启定时器中断,即使用定时器4的更新中断作为NVIC的中断源,如计数器到达设定自动重装载寄存器值时,触发更新中断
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM4, ENABLE);
}
//定时器中断服务函数:只做一件事,计数器加一
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update) == SET) //溢出中断
{
time4_count++;
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除中断标志位
}
/***************************************************************************/
3.SysTick系统定时器
-
SysTick系统定时器新增SysTick_CountMode接口,用于开环控制(禁用延时函数)
-
详情参考代码及注释
SysTick.h#ifndef __SYSTICK_H
#define __SYSTICK_H#include "system.h"
void SysTick_Init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);void SysTick_CountMode(void);
#endif
SysTick.c
#include "SysTick.h"
static u8 fac_us = 0; //即延时1us需要的计数次数
static u16 fac_ms = 0; //即延时1ms需要的计数次数
//使用延时函数需要先进行初始化
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //SysTick时钟源为HCLK的8分频,由时钟树图可以得知,HCLK时钟等于AHB时钟
fac_us = SYSCLK/8; //SYSCLK = 72M为例,fac_us = 9即延时1us需要的计数次数为9
fac_ms = (u16)fac_us * 1000;
}
/*******************************************************************************
* 函 数 名 : delay_us
* 函数功能 : us延时,
* 输 入 : nus:要延时的us数
注 意 : nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
* 输 出 : 无
*******************************************************************************/
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD = nus * fac_us; //将延时对应的预重装值写入LOAD寄存器
SysTick->VAL = 0x00; //清空当前数值寄存器VAL
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // SysTick定时器使能
do
{
temp = SysTick->CTRL; //读取CTRL寄存器
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达后退出
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // SysTick定时器disable
SysTick->VAL = 0x00; //清空当前数值寄存器VAL
}
/*******************************************************************************
* 函 数 名 : delay_ms
* 函数功能 : ms延时,
* 输 入 : nms:要延时的ms数
注 意 :nms的值,SysTick->LOAD为24位寄存器,
不要大于0xffffff*8*1000/SYSCLK对72M条件下,nms<=1864ms
* 输 出 : 无
*******************************************************************************/
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD = (u32)nms * fac_ms; //只有这里不同,其他和delay_us延时相同
SysTick->VAL = 0x00;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL = 0x00;
}
/*******************************************************************************
* 函 数 名 : SysTick_CountMode
* 函数功能 : 让SysTick定时器从0xFFFFFF到0循环计数
* 输 入 : 无
* 注 意 : 在BLDCMotor.c中需要读取SysTick->VAL的值,它要求 SysTick 必须一直自由跑!
* 输 出 : 无
*******************************************************************************/
void SysTick_CountMode(void)
{
SysTick->LOAD = 0xFFFFFF - 1; //set reload register
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; //Enable SysTick Timer
}
4.usart模块改造,修改接收中断回调:
-
中断回调中实现存储字符串,用于串口控制命令解析
-
在不需要不需要选择MicroLIB情况下,适配代码防止编译器报错
-
fputc的实现兼容库函数操作、寄存器操作
usart.h#ifndef __USART_H
#define __USART_H#include "system.h"
#include "stdio.h" //要实现int fputc(int ch,FILE *p) 原型,需要包含该头文件#define USART_REC_LEN 256
/* 声明是全局变量,需要在外部文件使用 */
extern unsigned char USART_RX_BUF[USART_REC_LEN];
extern unsigned short USART_RX_STA;void USART1_Init(u32 bound);
#endif
usart.c
#include "usart.h"
/*
1.在不需要不需要选择MicroLIB情况下,这些代码是为了骗过编译器报错(如果选择了MicroLIB,则不需要这段代码也可以使用printf)
*/
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
#endif
/* fputc的实现以下两种写法都可以,第一种是直接操作寄存器;第二种是通过调用库函数的方式 */
#if 1
int fputc(int ch, FILE *f)
{
while((USART1->SR & 0X40) == 0);
USART1->DR = (u8)ch;
return ch;
}
#else
int fputc(int ch, FILE *p) //函数默认的,函数原型不能改变,在使用printf函数时自动调用
{
USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //USART_FLAG_TXE状态标志表示可以发送下一个数据了,发送数据寄存器已经空了(发送移位寄存器数据可能没有发完)
return ch;
}
#endif
/******************************************************************************/
unsigned char USART_RX_BUF[USART_REC_LEN]; //接收buffer
/*
接收状态标志:
bit15 = 1:接收完成标志
bit14 = 1:接收到0x0D(\r 的 16 进制是:0x0D)
bit13~0:接收的字节数
*/
unsigned short USART_RX_STA = 0;
//bound:参数为输入波特率
void USART1_Init(u32 bound)
{
GPIO_InitTypeDef MY_GPIO_Init; //定义结构体变量
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//默认引脚 PA9/PA10,没有重映射 → 不需要使能 AFIO 时钟!
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能串口1时钟
MY_GPIO_Init.GPIO_Pin = GPIO_Pin_9; //TX -- PA9
MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOA, &MY_GPIO_Init);
MY_GPIO_Init.GPIO_Pin = GPIO_Pin_10; //RX -- PA10
MY_GPIO_Init.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置浮空输入
MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOA, &MY_GPIO_Init);
USART_InitStructure.USART_BaudRate = bound; //设置波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置数据位为8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //设置停止位为1位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送使能和接收使能
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE); //使能 USART1串口
//清除USART_FLAG_TC发送完成标志位,这个是状态标志位,并非中断标志位
USART_ClearFlag(USART1, USART_FLAG_TC); //清除USART_FLAG_TC发送完成标志位,这步最好是清除一下
//设置串口中断类型并使能
//USART_IT_RXNE ------ 接收数据寄存器非空中断,"我收到了一个字节数据,已经放进接收寄存器了,快来读!"
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //我们选择的是接收中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //在"stm32f10x.h"查看
#if 0
/* 在一般测试中,抢占优先级2、响应优先级都设置为0,数值越小优先级越高 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
#else
/* 在FOC电机控制中,抢占优先级、响应优先级都设置为3,数值越小优先级越高 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级3
#endif
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
}
#if 0
/*
这个中断函数只是测试,接收到的数据原样发回。
*/
void USART1_IRQHandler(void)
{
u8 data = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //串口接收中断标志位,检查指定的 USART 中断发生与否,当调用接收函数时会被自动清除。
{
//当你调用 USART_ReceiveData() 读取数据时,硬件会自动清除 USART_IT_RXNE!
data = USART_ReceiveData(USART1);
USART_SendData(USART1, data); //将接收的数据发送出去
//USART_FLAG_TC发送完成标志位,表示当移位寄存器的最后一位数据被发送到 TX 引脚,且总线空闲时,才置 1。表示数据已经完全发完,总线已经空闲。
//USART_FLAG_TXE发送数据寄存器空,当 TDR 寄存器的数据被转移到移位寄存器后,立刻置 1,表示 "我可以写下一个数据了"。
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET); //USART_FLAG_TC发送完成标志位
//while(USART_GetITStatus(USART1, USART_FLAG_TC) != SET); //开始错用了这个函数,导致中断程序在这里卡死!!!
}
USART_ClearFlag(USART1, USART_FLAG_TC); //清除USART_FLAG_TC发送完成标志位,这个是状态标志位,并非中断标志位
}
#else
/*
串口1中断程序:每收到 1 个字节,就进一次这个中断函数,将接收数据存放在全局BUF中
1.将RX收到的字节存放到USART_RX_BUF中,如['data',...,'data','data','\0']
2.USART_RX_STA中bit15、bit14记录接收标志;bit13~0:接收的字节数
3.如发送:T10.5\r\n ,将T10.5存放在USART_RX_BUF中,结尾加上字符串结束符'\0'
*/
void USART1_IRQHandler(void)
{
unsigned char Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断是否是接收中断
{
Res = USART_ReceiveData(USART1); //读取接收到的一个字节
if((USART_RX_STA&0x8000) == 0) //bit15 = 0,接收未完成
{
if(USART_RX_STA&0x4000) //已经接收到了0x0D,即接收到了回车 \r (16进制为0x0D)
{
if(Res != 0x0A) // \n 为(0x0A)
{
USART_RX_STA = 0; //接收错误,重新开始
}
else
{
USART_RX_STA |= 0x8000; //接收到了\n,将bit15 = 1(接收完成)
//USART_RX_STA&0X3FFF 为接收的字节数
USART_RX_BUF[USART_RX_STA&0X3FFF] = '\0'; //最后一个字节放'0',方便判断
}
}
else //还没收到0x0D
{
//如果当前字节是\r
if(Res == 0x0D)
{
USART_RX_STA |= 0x4000;
}
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF] = Res;
USART_RX_STA++;
if(USART_RX_STA > (USART_REC_LEN - 1))
{
USART_RX_STA = 0; //接收数据超过接收buffer大小,接收错误,重新开始
}
}
}
}
}
}
#endif
5.SimpleFOC库移植:
- 将
SimpleFOC文件夹拷贝到工程路径下,添加SimpleFOC工程组,添加如下工程文件到工程组:



- 引入头文件路径:

- 这些文件开环控制暂不使用,先暂存在这个路径下:

- 加入头文件依赖:
FOCMotor.c
#include "foc_utils.h"
#include "FOCMotor.h"
#include "BLDCmotor.h"
foc_utils.c
#include "foc_utils.h"
#include "FOCMotor.h"
#include "BLDCmotor.h"
BLDCMotor.c
#include "time_open_control.h" //是因为需要包含system.h 需要调用TIM_SetCompareX库函数
#include "foc_utils.h"
#include "FOCMotor.h"
#include "BLDCmotor.h"
6.main.c函数:
大致流程:
-
状态指示灯管脚配置初始化
-
驱动板SimpleFOC Mini 使能管脚配置初始化
-
串口1初始化
-
配置TIM2输出PWM(此时不输出,调用TIM_SetCompareX时输出)
-
配置TIM4定时器中断为1MS
-
配置controller控制模式、voltage_limit、电机极对数pole_pairs等参数
-
驱动板使能
-
SysTick切到CountMode模式
-
while循环(指示灯控制--接收控制命令--控制输出调用move函数)
main.c/* 该代码已经全部调通,led状态闪烁,默认是速度开环模式,高速下电机可能会堵转,低速发热比较严重 */
#include <stdlib.h>
#include "system.h" //已经包含了"stm32f10x.h" 以及定义了位带操作的宏,后续只包含该头文件就可以了
#include "SysTick.h" //使用delay_ms和delay_us#include "led.h" //Led_Init()对8个LED GPIO初始化, 状态指示灯需要使用
#include "time_open_control.h" //三路PWM以及状态指示灯闪烁控制#include "usart.h" //使用printf打印以及接收串口命令
#include "FOCMotor.h" //使用controller全局变量及Type_velocity_openloop等数据结构
#include "BLDCmotor.h" //voltage_power_supply等全局变量/*
1.速度开环模式下(弧度):
T0.01(再小的精度没有测试了) ~ T60(目前测试下再大可能会堵转)
默认T6.28:上电后以6.28rad/s的转速转动(1圈/秒)
2.位置角度开环模式下(float的取值范围):
默认T6.28:上电后顺时针转动1圈后回到起点
T-6.28:逆时针转动1圈后回到起点
*/
float target = 0.0;void commander_run(void)
{
if((USART_RX_STA&0x8000) != 0)
{
switch(USART_RX_BUF[0])
{
case 'H':
printf("Speed Mode:T+-0.01~T+-60\r\n");
printf("ag:T1.23\r\n");
printf("Angle Mode:+-float\r\n");
printf("ag:T-6.28\r\n");
printf("D:Motor disable!\r\n");
printf("E:Motor enable!\r\n");
break;
case 'T': //T6.28
target = atof((const char *)(USART_RX_BUF + 1));
printf("RX = %.4f\r\n", target);
break;
case 'D': //D
MOTOR_DISABLE;
printf("Motor disable!\r\n");
break;
case 'E': //E
MOTOR_ENABLE;
printf("Motor enable!\r\n");
default:
break;
}
USART_RX_STA = 0; //不用清除BUF,清除BUF状态,下次接收复写即可
}
}//开环控制最重要的参数就是voltage_limit
//1、电机抖动转不起来把voltage_limit设置的大一点,
//2、电机发热严重的把voltage_limit设置的小一点,
//3、串口发送指令"T10",后面要有回车换行符
//4、开环不是电机控制的常态,不要纠结太久。
int main(void)
{
SysTick_Init(72); //72为SYSCLK delay_ms延时函数需要使用
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断分组2:2 在使用中断回调函数时都需要调用, 这里虽然没有使用中断,但还是留着Led_Init(); //使用LED1作为状态显示 MOTOR_EN_PIN_Init(); //驱动板SimpleFOC Mini 使能管脚配置 USART1_Init(9600); //使用printf及接收串口控制命令 //中央对齐模式: //频率f = 72M / ((预分频 + 1) * 2 * ARR ) //以25K赫兹 40us为例:f = 72M / ((0+1) * 2 * (1440-1+1)) = 72000000 / (1440 * 2) = 25000 Hz = 25KHz TIM2_CH1_CH3_CH4_PWM_Init(1440 - 1, 0, 0, 0, 0); //25KHz,此时不输出PWM TIM4_1MS_Init(); //interrupt per 1ms delay_ms(1000); //Wait for the system to stabilize /************************参数调整*********************************/ controller = Type_velocity_openloop; //选择开环控制类型为速度模式 //controller = Type_angle_openloop; //选择开环控制类型为角度模式 voltage_power_supply = 12; //V //voltage_limit = 2.5; //V,最大值需小于12/1.732=6.9。大功率航模电机设置的小一点0.5-1;小功率云台电机设置的大一点1-3 voltage_limit = 3.0; //这里2.5V调整为3V velocity_limit = 20; //rad/s angleOpenloop() use it pole_pairs = 7; //电机的极对数(N-S极多少对) target = 6.28; //选择开环控制类型为速度模式为例,上电后以6.28rad/s的转速转动(1圈/秒) /******************************************************************/ MOTOR_ENABLE; //驱动板SimpleFOC Mini 使能管脚 printf("Control Mode(Speed Mode = 3 ,Angle Mode = 4):[%d].\r\n", controller); printf("Motor ready.\r\n"); /* 在BLDCMotor.c中需要读取SysTick->VAL的值,它要求 SysTick 必须一直自由跑! */ SysTick_CountMode(); //后续code不能再调用delay_us()和delay_ms()函数 while(1) { //这个time4_count计数只是为了LED状态指示灯闪烁使用 if(time4_count >= 200) //0.2s闪烁一次 { time4_count = 0; led1 = !led1; } move(target); commander_run(); }}