一、任务目标
利用STMF103RCT6双安卓口的开发板,发信号给MP6540H控制无刷直流6对级12V的2804的云台电机,通过SPI读取MA600A磁编码角度传感器,通过I2C 显示0.96的OLED。
二、硬件部分
引脚接线
| 功能模块 | 信号名称 | STM32F103引脚 | 外设/模式 | 备注 |
|---|---|---|---|---|
| 电机驱动 MP6540H | PWMA | PB6 | TIM4_CH1 (PWM) | A相PWM |
| PWMB | PB7 | TIM4_CH2 (PWM) | B相PWM | |
| PWMC | PB8 | TIM4_CH3 (PWM) | C相PWM | |
| ENA | PB12 | GPIO_Output | 使能A | |
| ENB | PB13 | GPIO_Output | 使能B | |
| ENC | PB14 | GPIO_Output | 使能C | |
| nSleep | PB15 | GPIO_Output | 睡眠控制 | |
| nFault | PB0 | GPIO_Input | 故障检测 | |
| 磁编码器 MA600A | SCLK | PA5 | SPI1_SCK | SPI时钟 |
| CIPO | PA6 | SPI1_MISO | 数据输入 | |
| COPI | PA7 | SPI1_MOSI | 数据输出 | |
| CS | PA4 | GPIO_Output | 片选 | |
| OLED 显示屏 | SCL | PB10 | I2C1_SCL | I2C时钟 |
| SDA | PB11 | I2C1_SDA | I2C数据 | |
| 电流检测 | I_U | PA0 | ADC1_IN0 | U相电流 |
| I_V | PA1 | ADC1_IN1 | V相电流 | |
| 调试串口 | TX | PA2 | USART2_TX | 串口输出 |
| RX | PA3 | USART2_RX | 串口输入 | |
| 板载资源 | LED2 | PA8 | GPIO_Output | 板载LED |
| SW3 | PC9 | GPIO_Input | 按键3 | |
| SW4 | PC8 | GPIO_Input | 按键4 | |
| USB1 | RX | PA9 | USB_DM | USB1接收 |
| TX | PA10 | USB_DP | USB1发送 | |
| USB2 | DM | PA11 | USB_DM | USB差分负 |
| DP | PA12 | USB_DP | USB差分正 | |
| 调试接口 | SWDIO | PA13 | SWD | ST-Link下载 |
| SWCLK | PA14 | SWD | ST-Link时钟 |
三、软件部分
STM32F103RCT6 FOC控制系统 - CubeMX配置指南
STM32F103RCT6 FOC控制系统 - CubeMX配置指南
一、新建工程
- 打开STM32CubeMX → ACCESS TO MCU SELECTOR
- 搜索 STM32F103RCT6 → 选择型号 → Start Project
时钟配置 (Clock Configuration) 配置项
设置值 HSE Crystal/Ceramic Resonator (8MHz) LSE Crystal Oscillator (32.768KHz) PLL Source HSE PLL Multiplier ×9 SYSCLK 72MHz AHB Prescaler /1 APB1 Prescaler /2 (36MHz) APB2 Prescaler /1 (72MHz) LSE = 32.768KHz (可选,用于RTC)
配置TIM1(三相PWM输出)
第一步:打开TIM1配置界面
-
在左侧 Timers 下找到 TIM1
-
点击进入配置界面
第二步:基础配置
参数 设置值 Clock Source Internal Clock Channel 1 PWM Generation CH1 Channel 2 PWM Generation CH2 Channel 3 PWM Generation CH3
第四步:配置PWM模式
在 PWM Generation Channel 1/2/3 中:
参数 设置值 Mode PWM Mode 1 Pulse 0 Output Compare Polarity High Fast Mode Disable CHxN Polarity High (如果使用互补输出
第五步:配置GPIO引脚
配置完成后,CubeMX会自动分配引脚:
-
PA8 → TIM1_CH1 (PWMA)
-
PA9 → TIM1_CH2 (PWMB)
-
PA10 → TIM1_CH3 (PWMC)
第六步:启用PWM输出
在 NVIC Settings 中:
- 可选:启用 TIM1 更新中断(用于FOC定时)
下一步:配置SPI1(MA600A磁编码器)
第一步:打开SPI1配置界面
-
在左侧 Connectivity 下找到 SPI1
-
点击进入配置界面
第二步:基础配置
参数 设置值 Mode Full Duplex Master Hardware NSS Signal Disable
第三步:配置SPI参数
在 Parameter Settings 中:
Data Size: 16 Bits
First Bit: MSB First
Clock Polarity (CPOL): Low
Clock Phase (CPHA): 1 Edge
Baud Rate Prescaler: 16
NSS Signal Type: Software (GPIO)
第四步:配置GPIO引脚
配置完成后,CubeMX会自动分配引脚:
- PA5 → SPI1_SCK
- PA6 → SPI1_MISO (CIPO)
- PA7 → SPI1_MOSI (COPI)
- 在左侧 GPIO 中找到 PA4
- 设置为 GPIO_Output
- 默认电平设置为 High (CS高电平表示未选中)
下一步:配置I2C1(OLED显示屏)
第一步:打开I2C1配置界面
-
在左侧 Connectivity 下找到 I2C1
-
点击进入配置界面
参数 设置值 Mode I2C Slave Address 0x78(OLED默认地址)
第三步:配置I2C参数
在 Parameter Settings 中:
I2C Speed Mode: Fast Mode
Fast Mode Speed (kHz): 400
Addressing Mode: 7-bit
Own Address 1: 0
Dual Address Mode: Disable
General Call Mode: Disable
No Stretch Mode: Disable
第四步:配置GPIO引脚
配置完成后,CubeMX会自动分配引脚:
下一步:配置ADC1(三相电流检测)
第一步:打开ADC1配置界面
-
在左侧 Analog 下找到 ADC1
-
点击进入配置界面
第二步:基础配置
在 Mode Settings 中:
-
Scan Conversion Mode : Enable
-
Continuous Conversion Mode : Disable
-
Discontinuous Conversion Mode : Disable
参数 设置值 Channel ADC_CHANNEL_0 (PA0) Sampling Time 55.5 Cycles
然后添加 Rank 2 :
参数 设置值 Channel ADC_CHANNEL_1 (PA1) Sampling Time 55.5 Cycles
然后添加 Rank 3 :
参数 设置值 Channel ADC_CHANNEL_2 (PA2) Sampling Time 55.5 Cycles
下一步:配置GPIO控制引脚
第一步:打开GPIO配置界面
-
在左侧 System Core 下找到 GPIO
-
点击进入配置界面
第二步:配置控制引脚
需要配置以下引脚:
引脚 功能 Mode Default Level PB12 ENA GPIO_Output Low PB13 ENB GPIO_Output Low PB14 ENC GPIO_Output Low PB15 nSleep GPIO_Output High PB0 nFault GPIO_Input -
操作步骤:
- 找到 PB12 :
- Mode: GPIO_Output
- Pull-up/Pull-down: No pull-up and no pull-down
- Speed: Low
- Default Level: Low
- 找到 PB13 :
- Mode: GPIO_Output
- 其他同PB12
- 找到 PB14 :
- Mode: GPIO_Output
- 其他同PB12
- 找到 PB15 :
- Mode: GPIO_Output
- Default Level: High (nSleep高电平表示唤醒状态)
- 找到 PB0 :
-
Mode: GPIO_Input
-
Pull-up/Pull-down: No pull-up and no pull-down (或根据电路选择)
-
找到板载LED引脚(通常是PC13)
-
Mode: GPIO_Output
下一步:配置USART1(调试串口)
第一步:打开USART1配置界面
-
在左侧 Connectivity 下找到 USART1
-
点击进入配置界面
第二步:基础配置
参数 设置值 Mode Asynchronous Baud Rate 115200 Word Length 8 Bits Stop Bits 1 Parity None Hardware Flow Control None
第三步:配置GPIO引脚
配置完成后,CubeMX会自动分配引脚:
-
PA9 → USART1_TX
-
PA10 → USART1_RX
第四步:配置中断(可选)
在 NVIC Settings 中:
-
勾选 USART1 global interrupt
-
设置优先级(例如:Preemption Priority: 0, Sub Priority: 1)
修改程序
/**
******************************************************************************
* @file : main.c
* @brief : FOC + Serial CLI + OLED
******************************************************************************
*/
#include "main.h"
volatile uint32_t tick_count = 0;
/* ---- Simple string helpers (no libc) ---- */
static int strequal(const char *a, const char *b) { while(*a&&*b){if(*a!=*b)return 0;a++;b++;} return *a==*b; }
static int strprefix(const char *a, const char *b, int n) { for(int i=0;i<n;i++){if(a[i]!=b[i])return 0;} return 1; }
static float myatof(const char *s) { float r=0,d=1;int sign=1; if(*s=='-'){sign=-1;s++;} while(*s>='0'&&*s<='9'){r=r*10+(*s-'0');s++;} if(*s=='.'){s++;while(*s>='0'&&*s<='9'){d*=0.1f;r+=(*s-'0')*d;s++;}} return r*sign; }
static int myatoi(const char *s) { int r=0; while(*s>='0'&&*s<='9'){r=r*10+(*s-'0');s++;} return r; }
/* ---- UART TX (register-direct, no HAL) ---- */
#define U1_BASE 0x40013800 // USART1 base address (APB2)
static void uart_tx(uint8_t c) { while(!(*(volatile uint32_t*)(U1_BASE+0x00) & (1<<7))); *(volatile uint32_t*)(U1_BASE+0x04) = c; }
static void uart_puts(const char *s) { while(*s) uart_tx((uint8_t)*s++); }
static void uart_puti(int32_t n) {
char buf[12]; int i=0, neg=0;
if(n<0){neg=1; n=-n;} if(n==0) buf[i++]='0';
while(n>0){buf[i++]='0'+(n%10); n/=10;}
if(neg) buf[i++]='-';
while(i>0) uart_tx((uint8_t)buf[--i]);
}
static void uart_putx(uint32_t n) {
char h[]="0123456789ABCDEF"; uart_tx('0'); uart_tx('x');
int s=28; while(s>0 && ((n>>s)&0xF)==0) s-=4;
while(s>=0){uart_tx(h[(n>>s)&0xF]); s-=4;}
}
/* ---- FOC state ---- */
static float foc_vq = 0.3f; // 默认300 (x1000),提高默认电压以便电机能启动
static float foc_speed = 50.0f; // 默认500 (x10)
static float foc_angle = 0.0f;
static int closed_loop = 0; // 0=open, 1=closed
static float pid_kp = 0.005f, pid_ki = 0.001f; // Kp=5(x1000), Ki=1(x1000)
static int motor_running = 0;
static int iq_ref = 100; // 默认电流参考值ADC
static int pole_pairs = 6; // 默认极对数
/* ---- sinf/cosf (simple Taylor) ---- */
static float sinf(float x) {
while(x>3.1415926f) x-=6.2831853f;
while(x<-3.1415926f) x+=6.2831853f;
float r=0, p=x, f=1; for(int i=0;i<10;i++){r+=p/f; p*=-x*x; f*=(2*i+2)*(2*i+3);} return r;
}
static float cosf(float x) {
while(x>3.1415926f) x-=6.2831853f;
while(x<-3.1415926f) x+=6.2831853f;
float r=0, p=1, f=1; for(int i=0;i<10;i++){r+=p/f; p*=-x*x; f*=(2*i+1)*(2*i+2);} return r;
}
/* ---- SVPWM ---- */
#define M_SQRT3_DIV2 0.8660254037844386f
#define PWM_MAX 1600
static void svpwm_calc(float va, float vb, float vc, uint16_t *a, uint16_t *b, uint16_t *c) {
float vmax=va, vmin=va;
if(vb>vmax) vmax=vb;
if(vc>vmax) vmax=vc;
if(vb<vmin) vmin=vb;
if(vc<vmin) vmin=vc;
float vo = -0.5f*(vmax+vmin);
va+=vo; vb+=vo; vc+=vo;
*a=(uint16_t)((va+0.5f)*(float)PWM_MAX);
*b=(uint16_t)((vb+0.5f)*(float)PWM_MAX);
*c=(uint16_t)((vc+0.5f)*(float)PWM_MAX);
}
static void foc_run(void) {
if(!motor_running) { TIM4->CCR1=0; TIM4->CCR2=0; TIM4->CCR3=0; return; }
// 更新角度
foc_angle += foc_speed * 0.001f;
while(foc_angle >= 6.2831853f) foc_angle -= 6.2831853f;
// FOC计算
float vd=0, vq=foc_vq;
float va = vd*cosf(foc_angle) - vq*sinf(foc_angle);
float vb = -0.5f*va + M_SQRT3_DIV2*(vd*sinf(foc_angle)+vq*cosf(foc_angle));
float vc = -va - vb;
uint16_t pa,pb,pc;
svpwm_calc(va,vb,vc,&pa,&pb,&pc);
// 限制范围
if(pa>PWM_MAX-1) pa=PWM_MAX-1;
if(pb>PWM_MAX-1) pb=PWM_MAX-1;
if(pc>PWM_MAX-1) pc=PWM_MAX-1;
// 输出PWM
TIM4->CCR1=pa;
TIM4->CCR2=pb;
TIM4->CCR3=pc;
}
/* ---- Serial CLI ---- */
static char cmd_buf[64]; static int cmd_idx=0; static int echo_on=0;
static void cmd_help(void) {
uart_puts("\r\n=== FOC 命令 (默认值) ===\r\n"
"start - 启动电机\r\n"
"stop - 停止电机\r\n"
"mode open - 开环模式 (默认)\r\n"
"mode closed - 电流闭环模式\r\n"
"vq <0~1000> - 开环电压 x1000 (默认300, 最大1000)\r\n"
"iq <0~4095> - 闭环电流ADC参考值 (默认100, 0~4095)\r\n"
"sp <1~9999> - 电角速度 x10 rad/s (默认500, 最大9999)\r\n"
"kp <0~999> - 电流环 Kp x1000 (默认5, 最大999)\r\n"
"ki <0~999> - 电流环 Ki x1000 (默认1, 最大999)\r\n"
"pp <1~50> - 极对数 (默认6, 1~50)\r\n"
"stat - 显示当前状态\r\n"
"angle - 读取MA600角度\r\n"
"spi XXXX - 16bit帧SPI: 发XXXX, 收rx\r\n"
"spi2 X Y - 32bit帧SPI: 发X,Y, 收rx0 rx1\r\n"
"echo on - 自动打印 (默认OFF)\r\n"
"echo off - 停止自动打印\r\n"
"help - 显示帮助\r\n"
"========================\r\n\r\n");
}
static void cmd_stat(void) {
uart_puts(closed_loop?"CLOSED":"OPEN");
uart_puts(" vq="); uart_puti((int)(foc_vq*1000));
uart_puts(" iq="); uart_puti(iq_ref);
uart_puts(" sp="); uart_puti((int)(foc_speed*10));
uart_puts(" pp="); uart_puti(pole_pairs);
uart_puts(" kp="); uart_puti((int)(pid_kp*1000));
uart_puts(" ki="); uart_puti((int)(pid_ki*1000));
uart_puts("\r\nRUN="); uart_puts(motor_running?"YES":"NO");
uart_puts(" PWM="); uart_puti(TIM4->CCR1); uart_tx(','); uart_puti(TIM4->CCR2); uart_tx(','); uart_puti(TIM4->CCR3);
uart_puts(" MA="); uart_puti((int)(MA600A_ReadRaw()));
uart_puts("\r\n");
}
static void cmd_parse(char *cmd) {
if(strequal(cmd,"help")) cmd_help();
else if(strequal(cmd,"start")) {
if(!motor_running){motor_running=1; GPIOB->ODR|=(1<<12)|(1<<13)|(1<<14)|(1<<15); uart_puts("STARTED\r\n");}
else uart_puts("Already running\r\n");
}
else if(strequal(cmd,"stop")) {
motor_running=0; TIM4->CCR1=0;TIM4->CCR2=0;TIM4->CCR3=0;
GPIOB->ODR&=~((1<<12)|(1<<13)|(1<<14)|(1<<15)); uart_puts("STOPPED\r\n");
}
else if(strprefix(cmd,"mode ",5)) {
if(strequal(cmd+5,"open")) { closed_loop=0; uart_puts("Mode: OPEN\r\n"); }
else if(strequal(cmd+5,"closed")) { closed_loop=1; uart_puts("Mode: CLOSED\r\n"); }
else uart_puts("? mode open/closed\r\n");
}
else if(strprefix(cmd,"vq ",3)) {
int v=myatoi(cmd+3);
if(v<0)v=0;if(v>1000)v=1000;
foc_vq=(float)v/1000.0f;
uart_puts("vq="); uart_puti(v); uart_puts("\r\n");
}
else if(strprefix(cmd,"iq ",3)) {
int i=myatoi(cmd+3);
if(i<0)i=0;if(i>4095)i=4095;
iq_ref=(uint16_t)i;
uart_puts("iq="); uart_puti(i); uart_puts("\r\n");
}
else if(strprefix(cmd,"sp ",3)) {
int s=myatoi(cmd+3);
if(s<1)s=1;if(s>9999)s=9999;
foc_speed=(float)s/10.0f;
uart_puts("sp="); uart_puti(s); uart_puts("\r\n");
}
else if(strprefix(cmd,"kp ",3)) {
int k=myatoi(cmd+3);
if(k<0)k=0;if(k>999)k=999;
pid_kp=(float)k/1000.0f;
uart_puts("kp="); uart_puti(k); uart_puts("\r\n");
}
else if(strprefix(cmd,"ki ",3)) {
int k=myatoi(cmd+3);
if(k<0)k=0;if(k>999)k=999;
pid_ki=(float)k/1000.0f;
uart_puts("ki="); uart_puti(k); uart_puts("\r\n");
}
else if(strprefix(cmd,"pp ",3)) {
int p=myatoi(cmd+3);
if(p<1)p=1;if(p>50)p=50;
pole_pairs=(uint8_t)p;
uart_puts("pp="); uart_puti(p); uart_puts("\r\n");
}
else if(strequal(cmd,"stat")) cmd_stat();
else if(strequal(cmd,"echo on")) { echo_on=1; uart_puts("Echo ON\r\n"); }
else if(strequal(cmd,"echo off")) { echo_on=0; uart_puts("Echo OFF\r\n"); }
else if(strequal(cmd,"angle")) {
uint16_t raw=MA600A_ReadRaw(); float deg=(float)raw*360.0f/65536.0f;
uart_puts("raw="); uart_putx(raw); uart_puts(" deg="); uart_puti((int)deg); uart_puts("\r\n");
}
else if(strprefix(cmd,"spi ",4)) {
uint16_t tx=(uint16_t)myatoi(cmd+4);
uint16_t rx=MA600A_SPI_Exchange(tx);
uart_puts("tx="); uart_putx(tx); uart_puts(" rx="); uart_putx(rx); uart_puts("\r\n");
}
else if(strprefix(cmd,"spi2 ",5)) {
char *p = cmd+5;
while(*p==' ') p++;
uint16_t tx0=(uint16_t)myatoi(p);
while(*p>='0'&&*p<='9') p++;
while(*p==' ') p++;
uint16_t tx1=(uint16_t)myatoi(p);
uint16_t rx0=MA600A_SPI_Exchange(tx0);
uint16_t rx1=MA600A_SPI_Exchange(tx1);
uart_puts("tx="); uart_putx(tx0); uart_tx(' '); uart_putx(tx1);
uart_puts(" rx="); uart_putx(rx0); uart_tx(' '); uart_putx(rx1); uart_puts("\r\n");
}
else { uart_puts("? '"); uart_puts(cmd); uart_puts("' help?\r\n"); }
}
void USART1_IRQHandler(void) {
if(*(volatile uint32_t*)(U1_BASE+0x00) & (1<<5)) {
uint8_t c = *(volatile uint32_t*)(U1_BASE+0x04);
if(c=='\r'||c=='\n') { cmd_buf[cmd_idx]=0; if(cmd_idx>0) cmd_parse(cmd_buf); cmd_idx=0; }
else if(cmd_idx<63) cmd_buf[cmd_idx++]=c;
}
}
/* ---- Main ---- */
int main(void) {
/* HSE->PLLx9->72MHz */
RCC->CR |= (1<<16); while(!(RCC->CR&(1<<17)));
*(volatile uint32_t*)0x40022000 |= (1<<1);
RCC->CFGR = (RCC->CFGR & ~(0xF<<18)) | (0x7<<18) | (1<<16) | (0x4<<8);
RCC->CR |= (1<<24); while(!(RCC->CR&(1<<25)));
RCC->CFGR = (RCC->CFGR & ~0x3) | 0x2;
while((RCC->CFGR & (0x3<<2)) != (0x2<<2));
*(volatile uint32_t*)0xE000E014 = 72000-1; *(volatile uint32_t*)0xE000E018=0; *(volatile uint32_t*)0xE000E010=7;
/* GPIO clocks */
RCC->APB2ENR |= (1<<2)|(1<<3)|(1<<4);
/* PA8 LED */
GPIOA->CRH = (GPIOA->CRH & ~0xF) | 0x3;
/* PA4 CS for MA600A */
GPIOA->CRL = (GPIOA->CRL & ~(0xF<<16)) | (0x3<<16);
GPIOA->ODR |= (1<<4);
/* PC8 key */
GPIOC->CRH = (GPIOC->CRH & ~(0xF<<0)) | 0x8;
GPIOC->ODR |= (1<<8);
/* PB12-15 motor ctrl: output PP */
GPIOB->CRH = (GPIOB->CRH & ~(0xFFFF<<16)) | (0x3333<<16);
GPIOB->ODR &= ~((1<<12)|(1<<13)|(1<<14));
GPIOB->ODR |= (1<<15);
/* PB0 nFault: input pull-up */
GPIOB->CRL = (GPIOB->CRL & ~0xF) | 0x8;
/* TIM4 PWM: PB6/7/8 AF_PP */
RCC->APB1ENR |= (1<<2);
GPIOB->CRL = (GPIOB->CRL & ~(0xFF<<24)) | (0xBB<<24); // PB6,PB7 = AF push-pull 50MHz
GPIOB->CRH = (GPIOB->CRH & ~0xF) | 0xB; // PB8 = AF push-pull 50MHz
TIM4->PSC = 0; // 不分频,72MHz时钟
TIM4->ARR = PWM_MAX-1; // 自动重装载值
TIM4->CCR1 = 0; TIM4->CCR2 = 0; TIM4->CCR3 = 0; // 初始占空比为0
// CCMR1: CH1(PB6)和CH2(PB7)配置为PWM模式1,使能预装载
TIM4->CCMR1 = (6<<4)|(1<<3) | (6<<12)|(1<<11); // OC1M=110(PWM1), OC1PE=1, OC2M=110, OC2PE=1
// CCMR2: CH3(PB8)配置为PWM模式1,使能预装载
TIM4->CCMR2 = (6<<4)|(1<<3); // OC3M=110, OC3PE=1
TIM4->CCER = (1<<0)|(1<<4)|(1<<8); // 使能CH1,CH2,CH3输出
TIM4->CR1 |= (1<<7); // ARPE=1 自动重装载预装载使能
TIM4->CR1 |= (1<<0); // CEN=1 计数器使能
TIM4->EGR |= (1<<0); // UG=1 产生更新事件
/* SPI1 */
RCC->APB2ENR |= (1<<12);
GPIOA->CRL = (GPIOA->CRL & ~(0xFFF<<20)) | (0xB4B<<20);
SPI1->CR1 = (1<<11)|(0x5<<3)|(1<<2)|(1<<1)|(1<<9)|(1<<8)|(1<<6);
volatile uint16_t d=SPI1->DR; (void)d;
/* I2C2 */
RCC->APB1ENR |= (1<<22);
GPIOB->CRH = (GPIOB->CRH & ~(0xFF<<8)) | (0xFF<<8);
I2C2->CR2=36; I2C2->CCR=180; I2C2->TRISE=37; I2C2->CR1|=(1<<0);
/* USART1: PA9=TX AF_PP, PA10=RX floating input, 115200 */
RCC->APB2ENR |= (1<<14); // Enable USART1 clock (APB2)
GPIOA->CRH = (GPIOA->CRH & ~(0xFF<<4)) | (0x4B<<4); // PA9=AF output 50MHz (TX), PA10=floating input (RX)
/* BRR = fCLK/(16*baudrate) = 72000000/(16*115200) = 39.0625 = 0x271 (integer part in bits 15-4, fraction in bits 3-0) */
*(volatile uint32_t*)(U1_BASE+0x08) = 0x271; // 39.0625 -> mantissa=39(0x27), fraction=1
*(volatile uint32_t*)(U1_BASE+0x0C) = (1<<13)|(1<<5)|(1<<3)|(1<<2); // UE+RXNEIE+TE+RE
/* Enable USART1 interrupt in NVIC (IRQ 37) - ISER[1] bit 5 */
*(volatile uint32_t*)0xE000E104 |= (1<<(37-32)); // ISER[1] for IRQ 37
int oled_ok = OLED_Init();
MA600A_Init();
if(oled_ok==0) OLED_Clear();
uart_puts("\r\nF103 FOC Controller\r\n");
cmd_help();
uint32_t last=0, counter=0;
uint8_t btn_last=1;
while(1) {
uint32_t t = tick_count;
if(t-last==0) continue;
last = t;
/* PC8 button */
uint8_t btn = !(GPIOC->IDR&(1<<8));
if(btn_last==1 && btn==0) {
motor_running = !motor_running;
if(motor_running) { GPIOB->ODR|=(1<<12)|(1<<13)|(1<<14)|(1<<15); }
else { TIM4->CCR1=0;TIM4->CCR2=0;TIM4->CCR3=0; GPIOB->ODR&=~((1<<12)|(1<<13)|(1<<14)|(1<<15)); }
}
btn_last=btn;
/* FOC update at 1kHz (from 1ms tick) */
if(motor_running) foc_run();
/* OLED + LED every 200ms */
counter++;
if(counter>=200) {
counter=0;
GPIOA->ODR ^= (1<<8);
uint16_t raw = MA600A_ReadRaw();
float ang = (float)raw/65536.0f*360.0f;
if(oled_ok==0) {
OLED_ShowString(0,0,"RAW:"); OLED_ShowNum(24,0,raw,5);
OLED_ShowString(60,0,"H:"); OLED_ShowHex(72,0,raw);
OLED_ShowString(0,1,"ANG:"); OLED_ShowNum(24,1,(uint32_t)ang,3);
OLED_ShowString(0,2,"RUN:"); OLED_ShowString(24,2,motor_running?"YES":"NO ");
OLED_ShowString(0,3,"VQ:"); OLED_ShowNum(24,3,(int)(foc_vq*1000),4);
OLED_ShowString(0,4,"PMW:"); OLED_ShowNum(24,4,TIM4->CCR1,4);
OLED_ShowString(0,5,motor_running?"MOTOR:ON ":"MOTOR:OFF");
}
}
/* Echo */
if(echo_on && t%200==0) cmd_stat();
}
}
void SysTick_Handler(void) { tick_count++; }
编译程序
cd g:\U3C5\STM32F103RCT6; make clean; make all
下载程序
总结
发现几个现象,每次我下载程序前先清除芯片,然后再下载,成功后按RESET启动,但是也出现很多次程序没有下载成功,让AI改半天也不行,然后无意中发现,重新给板子断电再上电就好了,而且遇见好几次,比如程序改完,灯不闪了,OLED屏不亮了,按RESET不行,AI改几次也不行,这时候可以试试重新断电













