系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
提示:以下是本篇文章正文内容,下面案例可供参考
一、代码解析
1.FreeRTOS配置
代码如下(示例):
#define configUSE_PREEMPTION 1 #define configSUPPORT_STATIC_ALLOCATION 0 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configUSE_IDLE_HOOK 1 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( SystemCoreClock ) #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES ( 7 ) #define configMINIMAL_STACK_SIZE ((uint16_t)128) #define configTOTAL_HEAP_SIZE ((size_t)65536) #define configMAX_TASK_NAME_LEN ( 16 ) #define configUSE_16_BIT_TICKS 0 #define configUSE_MUTEXES 1 #define configQUEUE_REGISTRY_SIZE 8 #define configCHECK_FOR_STACK_OVERFLOW 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
这里其实跟KEIL或者C版本的代码时一样的,找到SystemCoreClock 的定义,是160MHz,也就是系统的主频。然后总的堆栈大小是64K,并且注意一下这段堆栈的分配。
#if defined(STM32F405xx) // Place FreeRTOS heap in core coupled memory for better performance __attribute__((section(".ccmram"))) uint8_t ucHeap[configTOTAL_HEAP_SIZE]; #endif /* Specify the memory areas */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 768K NVM (r) : ORIGIN = 0x80C0000, LENGTH = 256K }
405的手册没仔细看,但是RAM也是分了两个区,RAM1和RAM2。其中RAM2也就是CCMRAM。main.cpp中通过一段声明将ucHeap分配到了这段RAM空间,目的应该是有更快的访问速度,但是印象中这段RAM不能用于DMA传输。
另外没有在配置里面看到软件定时器的设置,可能不需要吧。
2.配置操作
main.cpp的前半部分都是配置的一些操作接口。比如读取、擦除、写入、生效等操作。不用太细看。
4.进DFU模式
uint32_t _reboot_cookie __attribute__ ((section (".noinit"))); //进入DFU模式,禁止中断 写入升级标志 并且调用NVIC Reset void ODrive::enter_dfu_mode() { if ((hw_version_major_ == 3) && (hw_version_minor_ >= 5)) { __asm volatile ("CPSID I\n\t":::"memory"); // disable interrupts _reboot_cookie = 0xDEADBEEF; NVIC_SystemReset(); } else { /* * DFU mode is only allowed on board version >= 3.5 because it can burn * the brake resistor FETs on older boards. * If you really want to use it on an older board, add 3.3k pull-down resistors * to the AUX_L and AUX_H signals and _only then_ uncomment these lines. */ //__asm volatile ("CPSID I\n\t":::"memory"); // disable interrupts //_reboot_cookie = 0xDEADFE75; //NVIC_SystemReset(); } }
也比较简单,就是对_reboot_cookie这个变量进行赋值
5.错误操作
包含错误检测/错误清除/获取驱动异常等接口。
bool ODrive::any_error() { uint64_t ODrive::get_drv_fault() { void ODrive::clear_errors() {
6.钩子函数
包含错误检测/错误清除/获取驱动异常等接口。
这里的stackoverflow 还做了一些保护动作 对刹车电阻有一些措施
void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed portCHAR *pcTaskName) { for(auto& axis: axes){ axis.motor_.disarm(); } safety_critical_disarm_brake_resistor(); for (;;); // TODO: safe action } void vApplicationIdleHook(void) { if (odrv.system_stats_.fully_booted) { odrv.system_stats_.uptime = xTaskGetTickCount(); odrv.system_stats_.min_heap_space = xPortGetMinimumEverFreeHeapSize(); uint32_t min_stack_space[AXIS_COUNT]; std::transform(axes.begin(), axes.end(), std::begin(min_stack_space), [](auto& axis) { return uxTaskGetStackHighWaterMark(axis.thread_id_) * sizeof(StackType_t); }); odrv.system_stats_.max_stack_usage_axis = axes[0].stack_size_ - *std::min_element(std::begin(min_stack_space), std::end(min_stack_space)); odrv.system_stats_.max_stack_usage_usb = stack_size_usb_thread - uxTaskGetStackHighWaterMark(usb_thread) * sizeof(StackType_t); odrv.system_stats_.max_stack_usage_uart = stack_size_uart_thread - uxTaskGetStackHighWaterMark(uart_thread) * sizeof(StackType_t); odrv.system_stats_.max_stack_usage_startup = stack_size_default_task - uxTaskGetStackHighWaterMark(defaultTaskHandle) * sizeof(StackType_t); odrv.system_stats_.max_stack_usage_can = odrv.can_.stack_size_ - uxTaskGetStackHighWaterMark(odrv.can_.thread_id_) * sizeof(StackType_t); odrv.system_stats_.max_stack_usage_analog = stack_size_analog_thread - uxTaskGetStackHighWaterMark(analog_thread) * sizeof(StackType_t); odrv.system_stats_.stack_size_axis = axes[0].stack_size_; odrv.system_stats_.stack_size_usb = stack_size_usb_thread; odrv.system_stats_.stack_size_uart = stack_size_uart_thread; odrv.system_stats_.stack_size_startup = stack_size_default_task; odrv.system_stats_.stack_size_can = odrv.can_.stack_size_; odrv.system_stats_.stack_size_analog = stack_size_analog_thread; odrv.system_stats_.prio_axis = osThreadGetPriority(axes[0].thread_id_); odrv.system_stats_.prio_usb = osThreadGetPriority(usb_thread); odrv.system_stats_.prio_uart = osThreadGetPriority(uart_thread); odrv.system_stats_.prio_startup = osThreadGetPriority(defaultTaskHandle); odrv.system_stats_.prio_can = osThreadGetPriority(odrv.can_.thread_id_); odrv.system_stats_.prio_analog = osThreadGetPriority(analog_thread); status_led_controller.update(); } } }
7.回调函数
void ODrive::sampling_cb() {
sampling_cb应该就是用来更新编码器数值的。
void ODrive::control_loop_cb(uint32_t timestamp) {
control_loop_cb里内容比较多一点
MEASURE_TIME(task_times_.control_loop_misc) { // Reset all output ports so that we are certain about the freshness of // all values that we use. // If we forget to reset a value here the worst that can happen is that // this safety check doesn't work. // TODO: maybe we should add a check to output ports that prevents // double-setting the value. for (auto& axis: axes) { axis.acim_estimator_.slip_vel_.reset(); axis.acim_estimator_.stator_phase_vel_.reset(); axis.acim_estimator_.stator_phase_.reset(); axis.controller_.torque_output_.reset(); axis.encoder_.phase_.reset(); axis.encoder_.phase_vel_.reset(); axis.encoder_.pos_estimate_.reset(); axis.encoder_.vel_estimate_.reset(); axis.encoder_.pos_circular_.reset(); axis.motor_.Vdq_setpoint_.reset(); axis.motor_.Idq_setpoint_.reset(); axis.open_loop_controller_.Idq_setpoint_.reset(); axis.open_loop_controller_.Vdq_setpoint_.reset(); axis.open_loop_controller_.phase_.reset(); axis.open_loop_controller_.phase_vel_.reset(); axis.open_loop_controller_.total_distance_.reset(); axis.sensorless_estimator_.phase_.reset(); axis.sensorless_estimator_.phase_vel_.reset(); axis.sensorless_estimator_.vel_estimate_.reset(); } uart_poll(); odrv.oscilloscope_.update(); }
这里会定期让系统各个输出复位。包括串口的操作,刷新缓存 启动接收。
8.获取外设状态
uint32_t ODrive::get_interrupt_status(int32_t irqn) {
uint32_t ODrive::get_dma_status(uint8_t stream_num) {
uint32_t ODrive::get_gpio_states() {
9.rtos_main
接口进来之后先对外设进行初始化
// Init USB device MX_USB_DEVICE_Init(); // Start ADC for temperature measurements and user measurements start_general_purpose_adc(); //osDelay(100); // Init communications (this requires the axis objects to be constructed) init_communication(); // Start pwm-in compare modules // must happen after communication is initialized pwm0_input.init();
USB/ADC/COMM(串口 I2C CAN)/PWM。这些系统级的。
for(auto& axis : axes){
之后对每一路电机进行初始化
初始化完成之后会用2S时间对电机电流进行采集校准。
之后会启动各电机的各类任务。之后启动Freertos 完成。
9.main函数
uint32_t uuid0 = *(uint32_t *)(UID_BASE + 0); uint32_t uuid1 = *(uint32_t *)(UID_BASE + 4); uint32_t uuid2 = *(uint32_t *)(UID_BASE + 8); uint32_t uuid_mixed_part = uuid0 + uuid2; serial_number = ((uint64_t)uuid_mixed_part << 16) | (uint64_t)(uuid1 >> 16)
- 首先获取MCU的UID。
- 然后系统初始化,时钟之类。
- 再就是获取参数配置并应用。
- 板载初始化 外设的初始化,硬件驱动层
- GPIO 初始化,好像引用的也是HAL库。。。
- 信号量 队列的初始化
7、创建 rtos_main进程
8、启动Freertos
总结
相对来说main.cpp内容还好,大部分是一些系统需要的功能模块和完成初始化的相关的工作。