mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-09-20 14:38:18 +08:00
restucture files
This commit is contained in:
+4
-1
@@ -12,4 +12,7 @@ README.html
|
||||
|
||||
#Eclipse stuff
|
||||
.settings/
|
||||
.project
|
||||
.project
|
||||
|
||||
# STM32CubeMX (in case you put it in this folder, or a symlink)
|
||||
STM32CubeMX
|
||||
@@ -34,6 +34,7 @@ C_SOURCES = \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c \
|
||||
Test/test.c \
|
||||
Src/stm32f4xx_it.c \
|
||||
Src/tim.c \
|
||||
Src/gpio.c \
|
||||
@@ -63,7 +64,7 @@ C_SOURCES = \
|
||||
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c \
|
||||
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c \
|
||||
Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c \
|
||||
MotorControl/test.c
|
||||
MotorControl/low_level.c
|
||||
ASM_SOURCES = \
|
||||
Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f405xx.s
|
||||
|
||||
@@ -89,6 +90,7 @@ AS_INCLUDES =
|
||||
C_INCLUDES = -IMiddlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F
|
||||
C_INCLUDES += -IMiddlewares/Third_Party/FreeRTOS/Source/include
|
||||
C_INCLUDES += -IMiddlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS
|
||||
C_INCLUDES += -ITest
|
||||
C_INCLUDES += -IDrivers/DRV8301
|
||||
C_INCLUDES += -IDrivers/STM32F4xx_HAL_Driver/Inc
|
||||
C_INCLUDES += -IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy
|
||||
|
||||
@@ -1,21 +1,93 @@
|
||||
|
||||
#include "test.h"
|
||||
#include <low_level.h>
|
||||
|
||||
#include "stm32f4xx_hal.h"
|
||||
#include "stm32f405xx.h"
|
||||
#include "assert.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
#include "adc.h"
|
||||
#include "tim.h"
|
||||
#include "spi.h"
|
||||
#include "drv8301.h"
|
||||
|
||||
#include "math.h"
|
||||
#include "stdint.h"
|
||||
|
||||
void start_adc_pwm(){
|
||||
// Global variables
|
||||
Motor_t motor_configs[] = {
|
||||
{ //M0
|
||||
.gate_driver = {
|
||||
.spiHandle = &hspi3,
|
||||
//Note: this board has the EN_Gate pin shared!
|
||||
.EngpioHandle = EN_GATE_GPIO_Port,
|
||||
.EngpioNumber = EN_GATE_Pin,
|
||||
.nCSgpioHandle = M0_nCS_GPIO_Port,
|
||||
.nCSgpioNumber = M0_nCS_Pin,
|
||||
.RxTimeOut = false,
|
||||
.enableTimeOut = false
|
||||
},
|
||||
.shunt_conductance = 1.0f/0.0005f, //[S]
|
||||
.maxcurrent = 75.0f //[A] //Note: consistent with 40v/v gain
|
||||
}
|
||||
};
|
||||
const int num_motors = sizeof(motor_configs)/sizeof(motor_configs[0]);
|
||||
|
||||
// Private variables
|
||||
//Local view of DRV registers
|
||||
static DRV_SPI_8301_Vars_t gate_driver_regs[1/*num_motors*/];
|
||||
|
||||
// current sense queue from ADC to motor control task
|
||||
typedef struct {
|
||||
float current_phB;
|
||||
float current_phC;
|
||||
} Iph_BC_queue_item_t;
|
||||
osMailQDef (Iph_queue_def, 2, Iph_BC_queue_item_t);
|
||||
osMailQId (M0_Iph_queue);
|
||||
|
||||
// Private function prototypes
|
||||
static void DRV8301_setup();
|
||||
static void start_adc_pwm();
|
||||
static float phase_current_from_adcval(uint32_t ADCValue, int motornum);
|
||||
static void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc);
|
||||
|
||||
|
||||
//Special function name for ADC callback.
|
||||
//Automatically registered if defined.
|
||||
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) {
|
||||
pwm_trig_adc_cb(hadc);
|
||||
}
|
||||
|
||||
|
||||
void init_motor_control() {
|
||||
//Allocate the queues
|
||||
M0_Iph_queue = osMailCreate(osMailQ(Iph_queue_def), NULL);
|
||||
|
||||
//Init gate drivers
|
||||
DRV8301_setup();
|
||||
|
||||
osDelay(1000);
|
||||
|
||||
// Start PWM and enable adc interrupts/callbacks
|
||||
start_adc_pwm();
|
||||
}
|
||||
|
||||
// Set up the gate drivers
|
||||
static void DRV8301_setup() {
|
||||
for (int i = 0; i < num_motors; ++i) {
|
||||
DRV8301_enable(&motor_configs[i].gate_driver);
|
||||
DRV8301_setupSpi(&motor_configs[i].gate_driver, &gate_driver_regs[i]);
|
||||
|
||||
//@TODO we can use reporting only if we actually wire up the nOCTW pin
|
||||
gate_driver_regs[i].Ctrl_Reg_1.OC_MODE = DRV8301_OcMode_LatchShutDown;
|
||||
//Overcurrent set to approximately 150A at 100degC. This may need tweaking.
|
||||
gate_driver_regs[i].Ctrl_Reg_1.OC_ADJ_SET = DRV8301_VdsLevel_0p730_V;
|
||||
//20V/V on 500uOhm gives a range of +/- 150A
|
||||
//40V/V on 500uOhm gives a range of +/- 75A
|
||||
gate_driver_regs[i].Ctrl_Reg_2.GAIN = DRV8301_ShuntAmpGain_40VpV;
|
||||
|
||||
gate_driver_regs[i].SndCmd = true;
|
||||
DRV8301_writeData(&motor_configs[i].gate_driver, &gate_driver_regs[i]);
|
||||
gate_driver_regs[i].RcvCmd = true;
|
||||
DRV8301_readData(&motor_configs[i].gate_driver, &gate_driver_regs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void start_adc_pwm(){
|
||||
//Enable ADC and interrupts
|
||||
__HAL_ADC_ENABLE(&hadc2);
|
||||
__HAL_ADC_ENABLE(&hadc3);
|
||||
@@ -24,6 +96,10 @@ void start_adc_pwm(){
|
||||
__HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_JEOC);
|
||||
__HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_JEOC);
|
||||
|
||||
//Ensure that debug halting of the core doesn't leave the motor PWM running
|
||||
__HAL_DBGMCU_FREEZE_TIM1();
|
||||
__HAL_DBGMCU_FREEZE_TIM8();
|
||||
|
||||
//Init PWM
|
||||
int half_load = htim1.Instance->ARR/2;
|
||||
htim1.Instance->CCR1 = half_load;
|
||||
@@ -43,134 +119,9 @@ void start_adc_pwm(){
|
||||
|
||||
//Turn off output
|
||||
//__HAL_TIM_MOE_DISABLE(&htim1);
|
||||
|
||||
}
|
||||
|
||||
typedef struct Motor_s {
|
||||
DRV8301_Obj gate_driver;
|
||||
float shunt_conductance;
|
||||
float maxcurrent;
|
||||
} Motor_t;
|
||||
|
||||
Motor_t motor_configs[] = {
|
||||
{ //M0
|
||||
.gate_driver = {
|
||||
.spiHandle = &hspi3,
|
||||
//Note: this board has the EN_Gate pin shared!
|
||||
.EngpioHandle = EN_GATE_GPIO_Port,
|
||||
.EngpioNumber = EN_GATE_Pin,
|
||||
.nCSgpioHandle = M0_nCS_GPIO_Port,
|
||||
.nCSgpioNumber = M0_nCS_Pin,
|
||||
.RxTimeOut = false,
|
||||
.enableTimeOut = false
|
||||
},
|
||||
.shunt_conductance = 1.0f/0.0005f, //[S]
|
||||
.maxcurrent = 75.0f //[A] //Note: consistent with 40v/v gain
|
||||
}
|
||||
};
|
||||
|
||||
static const int num_motors = sizeof(motor_configs)/sizeof(motor_configs[0]);
|
||||
|
||||
//Local view of DRV registers
|
||||
static DRV_SPI_8301_Vars_t gate_driver_regs[1/*num_motors*/];
|
||||
|
||||
void test_DRV8301_setup() {
|
||||
for (int i = 0; i < num_motors; ++i) {
|
||||
DRV8301_enable(&motor_configs[i].gate_driver);
|
||||
DRV8301_setupSpi(&motor_configs[i].gate_driver, &gate_driver_regs[i]);
|
||||
|
||||
//@TODO we can use reporting only if we actually wire up the nOCTW pin
|
||||
gate_driver_regs[i].Ctrl_Reg_1.OC_MODE = DRV8301_OcMode_LatchShutDown;
|
||||
//Overcurrent set to approximately 150A at 100degC. This may need tweaking.
|
||||
gate_driver_regs[i].Ctrl_Reg_1.OC_ADJ_SET = DRV8301_VdsLevel_0p730_V;
|
||||
//20V/V on 500uOhm gives a range of +/- 150A
|
||||
//40V/V on 500uOhm gives a range of +/- 75A
|
||||
gate_driver_regs[i].Ctrl_Reg_2.GAIN = DRV8301_ShuntAmpGain_40VpV;
|
||||
|
||||
gate_driver_regs[i].SndCmd = true;
|
||||
DRV8301_writeData(&motor_configs[i].gate_driver, &gate_driver_regs[i]);
|
||||
gate_driver_regs[i].RcvCmd = true;
|
||||
DRV8301_readData(&motor_configs[i].gate_driver, &gate_driver_regs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//Test adc conversion latency and triggering
|
||||
|
||||
void test_adc_trigger() {
|
||||
//Set trigger to mid phase to check for trigger polarity
|
||||
htim1.Instance->CCR4 = 2048;
|
||||
|
||||
HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_4);
|
||||
__HAL_ADC_ENABLE(&hadc2);
|
||||
|
||||
//Warp field stabilize.
|
||||
osDelay(2);
|
||||
__HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_JEOC);
|
||||
}
|
||||
|
||||
static int test = 0;
|
||||
static int test2 = 0;
|
||||
static uint32_t testcnt[16];
|
||||
static int tcidx = 0;
|
||||
void test_adc_trigger_cb() {
|
||||
uint32_t cnt = htim1.Instance->CNT;
|
||||
int dir = htim1.Instance->CR1 & TIM_CR1_DIR;
|
||||
if(dir){
|
||||
test++;
|
||||
} else {
|
||||
test2++;
|
||||
testcnt[tcidx] = cnt - 2048;
|
||||
if(++tcidx == 16)
|
||||
tcidx = 0;
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
static int cbcnt = 0;
|
||||
void test_cb_count(){
|
||||
++cbcnt;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//Histogram test
|
||||
static float alpha = 1/(5000.0f);
|
||||
static float avg = 2048.0f;
|
||||
static float var = 0.0f;
|
||||
static uint32_t hist_countdown = 40000;
|
||||
static uint32_t errhist[20];
|
||||
static uint32_t neg_errhist[20];
|
||||
void test_adc_hist_cb(ADC_HandleTypeDef* hadc) {
|
||||
//float unknown_ch_volts = read_ADC_volts(hadc, 1);
|
||||
|
||||
uint32_t ADCValue = HAL_ADCEx_InjectedGetValue(hadc, 1);
|
||||
float val = (float)ADCValue;
|
||||
avg *= (1.0f - alpha);
|
||||
avg += alpha * val;
|
||||
|
||||
float dval = val-avg;
|
||||
var *= (1.0f - alpha);
|
||||
var += alpha * (dval * dval);
|
||||
|
||||
if (hist_countdown) {
|
||||
--hist_countdown;
|
||||
} else {
|
||||
int idval = (int)dval;
|
||||
int pos = (idval >= 0);
|
||||
if (!pos)
|
||||
idval = -idval;
|
||||
if (idval >= 20)
|
||||
idval = 19;
|
||||
if (pos)
|
||||
++errhist[idval];
|
||||
else
|
||||
++neg_errhist[idval];
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
float phase_current_from_adcval(uint32_t ADCValue, int motornum) {
|
||||
static float phase_current_from_adcval(uint32_t ADCValue, int motornum) {
|
||||
float rev_gain;
|
||||
switch (gate_driver_regs[motornum].Ctrl_Reg_2.GAIN) {
|
||||
case DRV8301_ShuntAmpGain_10VpV:
|
||||
@@ -194,21 +145,16 @@ float phase_current_from_adcval(uint32_t ADCValue, int motornum) {
|
||||
return current;
|
||||
}
|
||||
|
||||
void assertt(int arg) {
|
||||
//@TODO implement
|
||||
void safe_assert(int arg) {
|
||||
if(!arg) {
|
||||
for(;;);
|
||||
}
|
||||
}
|
||||
|
||||
// current sense queue from ADC to motor control task
|
||||
typedef struct {
|
||||
float current_phB;
|
||||
float current_phC;
|
||||
} Iph_BC_queue_item_t;
|
||||
osMailQDef (Iph_queue_def, 2, Iph_BC_queue_item_t);
|
||||
osMailQId (M0_Iph_queue);
|
||||
|
||||
void test_pwm_from_adc_cb(ADC_HandleTypeDef* hadc) {
|
||||
// This is the callback from the ADC that we expect after the PWM has triggered an ADC conversion.
|
||||
//@TODO: Document how the phasing is done
|
||||
static void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc) {
|
||||
|
||||
// ADC2 and ADC3 record the phB and phC currents concurrently,
|
||||
// and their interrupts should arrive on the same clock cycle.
|
||||
@@ -230,7 +176,7 @@ void test_pwm_from_adc_cb(ADC_HandleTypeDef* hadc) {
|
||||
phC_current = phase_current_from_adcval(ADCValue, 0);
|
||||
} else {
|
||||
//hadc is something else, not expected
|
||||
assertt(0);
|
||||
safe_assert(0);
|
||||
}
|
||||
|
||||
//Allocate mail queue storage
|
||||
@@ -248,18 +194,9 @@ void test_pwm_from_adc_cb(ADC_HandleTypeDef* hadc) {
|
||||
}
|
||||
|
||||
|
||||
void test_motor_thread(void const * argument) {
|
||||
void motor_thread(void const * argument) {
|
||||
|
||||
//Allocate the queues
|
||||
M0_Iph_queue = osMailCreate(osMailQ(Iph_queue_def), NULL);
|
||||
|
||||
//Init gate drivers
|
||||
test_DRV8301_setup();
|
||||
|
||||
osDelay(1000);
|
||||
|
||||
// Start PWM and enable adc interrupts/callbacks
|
||||
start_adc_pwm();
|
||||
init_motor_control();
|
||||
|
||||
for(;;) {
|
||||
//Current measurements not occurring in a timely manner can be handled by the watchdog
|
||||
@@ -268,7 +205,7 @@ void test_motor_thread(void const * argument) {
|
||||
osEvent evt = osMailGet(M0_Iph_queue, osWaitForever);
|
||||
|
||||
//Since we wait forever, we do not expect timeouts here.
|
||||
assertt(evt.status == osEventMail);
|
||||
safe_assert(evt.status == osEventMail);
|
||||
|
||||
Iph_BC_queue_item_t* mail_ptr = evt.value.p;
|
||||
float M0_phB_current = mail_ptr->current_phB;
|
||||
@@ -283,18 +220,3 @@ void test_motor_thread(void const * argument) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Test setup: Setup tests in main, and set callbacks
|
||||
void test_main(void) {
|
||||
|
||||
//test_adc_trigger();
|
||||
|
||||
}
|
||||
|
||||
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) {
|
||||
//test_adc_trigger_cb(hadc);
|
||||
//test_cb_count();
|
||||
//test_adc_hist_cb(hadc);
|
||||
test_pwm_from_adc_cb(hadc);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
#ifndef __LOW_LEVEL_H
|
||||
#define __LOW_LEVEL_H
|
||||
|
||||
#include "drv8301.h"
|
||||
|
||||
typedef struct Motor_s {
|
||||
DRV8301_Obj gate_driver;
|
||||
float shunt_conductance;
|
||||
float maxcurrent;
|
||||
} Motor_t;
|
||||
|
||||
extern Motor_t motor_configs[];
|
||||
extern const int num_motors;
|
||||
|
||||
void init_motor_control();
|
||||
|
||||
//@TODO move motor thread to high level file
|
||||
void motor_thread(void const * argument);
|
||||
|
||||
#endif //__LOW_LEVEL_H
|
||||
+2
-1
@@ -48,6 +48,7 @@
|
||||
|
||||
/* USER CODE BEGIN Includes */
|
||||
#include "test.h"
|
||||
#include "low_level.h"
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Variables -----------------------------------------------------------------*/
|
||||
@@ -94,7 +95,7 @@ void MX_FREERTOS_Init(void) {
|
||||
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
|
||||
|
||||
/* USER CODE BEGIN RTOS_THREADS */
|
||||
osThreadDef(task_motor_0, test_motor_thread, osPriorityHigh, 0, 512);
|
||||
osThreadDef(task_motor_0, motor_thread, osPriorityHigh, 0, 512);
|
||||
motor0_TaskHandle = osThreadCreate(osThread(task_motor_0), NULL);
|
||||
/* USER CODE END RTOS_THREADS */
|
||||
|
||||
|
||||
@@ -81,10 +81,6 @@ int main(void)
|
||||
|
||||
/* USER CODE BEGIN 1 */
|
||||
|
||||
//Ensure that debug halting of the core doesn't leave the motor PWM running
|
||||
__HAL_DBGMCU_FREEZE_TIM1();
|
||||
__HAL_DBGMCU_FREEZE_TIM8();
|
||||
|
||||
/* USER CODE END 1 */
|
||||
|
||||
/* MCU Configuration----------------------------------------------------------*/
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include "adc.h"
|
||||
#include "tim.h"
|
||||
|
||||
#include "math.h"
|
||||
#include "stdint.h"
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//Test adc conversion latency and triggering
|
||||
|
||||
void test_adc_trigger() {
|
||||
//Set trigger to mid phase to check for trigger polarity
|
||||
htim1.Instance->CCR4 = 2048;
|
||||
|
||||
HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_4);
|
||||
__HAL_ADC_ENABLE(&hadc2);
|
||||
|
||||
//Warp field stabilize.
|
||||
osDelay(2);
|
||||
__HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_JEOC);
|
||||
}
|
||||
|
||||
static int test = 0;
|
||||
static int test2 = 0;
|
||||
static uint32_t testcnt[16];
|
||||
static int tcidx = 0;
|
||||
void test_adc_trigger_cb() {
|
||||
uint32_t cnt = htim1.Instance->CNT;
|
||||
int dir = htim1.Instance->CR1 & TIM_CR1_DIR;
|
||||
if(dir){
|
||||
test++;
|
||||
} else {
|
||||
test2++;
|
||||
testcnt[tcidx] = cnt - 2048;
|
||||
if(++tcidx == 16)
|
||||
tcidx = 0;
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
static int cbcnt = 0;
|
||||
void test_cb_count(){
|
||||
++cbcnt;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//Histogram test
|
||||
static float alpha = 1/(5000.0f);
|
||||
static float avg = 2048.0f;
|
||||
static float var = 0.0f;
|
||||
static uint32_t hist_countdown = 40000;
|
||||
static uint32_t errhist[20];
|
||||
static uint32_t neg_errhist[20];
|
||||
void test_adc_hist_cb(ADC_HandleTypeDef* hadc) {
|
||||
//float unknown_ch_volts = read_ADC_volts(hadc, 1);
|
||||
|
||||
uint32_t ADCValue = HAL_ADCEx_InjectedGetValue(hadc, 1);
|
||||
float val = (float)ADCValue;
|
||||
avg *= (1.0f - alpha);
|
||||
avg += alpha * val;
|
||||
|
||||
float dval = val-avg;
|
||||
var *= (1.0f - alpha);
|
||||
var += alpha * (dval * dval);
|
||||
|
||||
if (hist_countdown) {
|
||||
--hist_countdown;
|
||||
} else {
|
||||
int idval = (int)dval;
|
||||
int pos = (idval >= 0);
|
||||
if (!pos)
|
||||
idval = -idval;
|
||||
if (idval >= 20)
|
||||
idval = 19;
|
||||
if (pos)
|
||||
++errhist[idval];
|
||||
else
|
||||
++neg_errhist[idval];
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
|
||||
//Test setup: Setup tests in main, and set callbacks
|
||||
void test_main(void) {
|
||||
|
||||
//test_adc_trigger();
|
||||
|
||||
}
|
||||
|
||||
//Uncomment to define the ADC callback. Ordinarily it is used by motor control
|
||||
// void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) {
|
||||
// //test_adc_trigger_cb(hadc);
|
||||
// //test_cb_count();
|
||||
// //test_adc_hist_cb(hadc);
|
||||
// test_pwm_from_adc_cb(hadc);
|
||||
// }
|
||||
|
||||
@@ -3,6 +3,5 @@
|
||||
#define __TEST_H
|
||||
|
||||
void test_main(void);
|
||||
void test_motor_thread(void const * argument);
|
||||
|
||||
#endif //__TEST_H
|
||||
Reference in New Issue
Block a user