From 07090d14e0dafc3b5b564a339cd615c2d9864125 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Wed, 31 Aug 2016 16:30:35 +0200 Subject: o A couple of stepper motor driver applications. --- apps/CMakeLists.txt | 2 + apps/stepper1/CMakeLists.txt | 19 ++++ apps/stepper1/stepper1.cpp | 174 +++++++++++++++++++++++++++++++ apps/stepper2/CMakeLists.txt | 20 ++++ apps/stepper2/stepper2.cpp | 241 +++++++++++++++++++++++++++++++++++++++++++ gdb-start | 31 ++++++ 6 files changed, 487 insertions(+) create mode 100644 apps/stepper1/CMakeLists.txt create mode 100644 apps/stepper1/stepper1.cpp create mode 100644 apps/stepper2/CMakeLists.txt create mode 100644 apps/stepper2/stepper2.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index cbbbc65..f5fe450 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -4,4 +4,6 @@ add_subdirectory(os1) add_subdirectory(os2) add_subdirectory(serial1) add_subdirectory(serial2) +add_subdirectory(stepper1) +add_subdirectory(stepper2) add_subdirectory(test1) diff --git a/apps/stepper1/CMakeLists.txt b/apps/stepper1/CMakeLists.txt new file mode 100644 index 0000000..e09a093 --- /dev/null +++ b/apps/stepper1/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(stepper1.elf stepper1.cpp + ${STM32F10X_STDPERIPH_LIB}/Libraries/CMSIS/CM3/CoreSupport/core_cm3.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/misc.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_spi.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_dma.c + $ + ) + +target_include_directories(stepper1.elf PUBLIC + $ + ) +target_compile_definitions(stepper1.elf PUBLIC ${STM32F10X_STDPERIPH_DEFINES}) +target_link_libraries(stepper1.elf tinyprintf) + +set_target_properties(stepper1.elf PROPERTIES LINK_FLAGS "-nostartfiles -T${CMAKE_SOURCE_DIR}/cmake/stm32.ld") +add_extra_commands(stepper1.elf) diff --git a/apps/stepper1/stepper1.cpp b/apps/stepper1/stepper1.cpp new file mode 100644 index 0000000..4ee8d88 --- /dev/null +++ b/apps/stepper1/stepper1.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "tinyprintf.h" +#include "playground.h" + +extern "C" +__attribute__((naked, used)) +void HardFault_Handler_C(uint32_t *hardfault_args) { + dbg_printf("r0 = 0x%08lx (%lu)\n", hardfault_args[0], hardfault_args[0]); + dbg_printf("r1 = 0x%08lx (%lu)\n", hardfault_args[1], hardfault_args[1]); + dbg_printf("r2 = 0x%08lx (%lu)\n", hardfault_args[2], hardfault_args[2]); + dbg_printf("r3 = 0x%08lx (%lu)\n", hardfault_args[3], hardfault_args[3]); + dbg_printf("r12 = 0x%08lx (%lu)\n", hardfault_args[4], hardfault_args[4]); + dbg_printf("lr = 0x%08lx (%lu)\n", hardfault_args[5], hardfault_args[5]); + dbg_printf("pc = 0x%08lx (%lu)\n", hardfault_args[6], hardfault_args[6]); + dbg_printf("psr = 0x%08lx (%lu)\n", hardfault_args[7], hardfault_args[7]); + dbg_printf("\n"); + + halt(); +} + +volatile bool dma_busy; + +static uint32_t pwm_buffer[128]; + +static uint16_t pwm_gpio_pin_a = GPIO_Pin_2; +static uint16_t pwm_gpio_pin_b = GPIO_Pin_3; + +static +void pwm_init() { + int on = true; + for (size_t i = 0; i < SizeOfArray(pwm_buffer); i++) { + pwm_buffer[i] = on ? 0xffffffff : 0; + on=!on; + } + +// for (size_t i = 0; i < SizeOfArray(pwm_buffer) / 2; i++) { +// pwm_buffer[i] = 0xffffffff; +// } +// +// for (size_t i = SizeOfArray(pwm_buffer) / 2; i < SizeOfArray(pwm_buffer); i++) { +// pwm_buffer[i] = 0; +// } +} + +static +void pwm_transfer_setup() { + DMA_Cmd(DMA1_Channel3, DISABLE); + DMA_DeInit(DMA1_Channel3); + + DMA_InitTypeDef dmaInit; + DMA_StructInit(&dmaInit); + + dmaInit.DMA_M2M = DMA_M2M_Enable; + dmaInit.DMA_Mode = DMA_Mode_Normal; + dmaInit.DMA_Priority = DMA_Priority_VeryHigh; + + dmaInit.DMA_DIR = DMA_DIR_PeripheralDST; + + dmaInit.DMA_PeripheralBaseAddr = reinterpret_cast(&GPIOA->ODR); + dmaInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; + dmaInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + + dmaInit.DMA_MemoryBaseAddr = reinterpret_cast(pwm_buffer); + dmaInit.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; + dmaInit.DMA_MemoryInc = DMA_MemoryInc_Enable; + + dmaInit.DMA_BufferSize = SizeOfArray(pwm_buffer); + DMA_Init(DMA1_Channel3, &dmaInit); + DMA_ITConfig(DMA1_Channel3, DMA_IT_TC | DMA_IT_TE, ENABLE); + + dma_busy = true; + + DMA_Cmd(DMA1_Channel3, ENABLE); +} + +void gpio_a_setup() { + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, ENABLE); + RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, DISABLE); + + GPIO_InitTypeDef init; + GPIO_StructInit(&init); + init.GPIO_Mode = GPIO_Mode_Out_PP; + init.GPIO_Pin = pwm_gpio_pin_a | pwm_gpio_pin_b; + GPIO_Init(GPIOA, &init); +} + +void gpio_a_loop() { + static bool on = false; + if (on) { + GPIO_SetBits(GPIOA, pwm_gpio_pin_a); + GPIO_SetBits(GPIOA, pwm_gpio_pin_b); + } else { + GPIO_ResetBits(GPIOA, pwm_gpio_pin_a); + GPIO_ResetBits(GPIOA, pwm_gpio_pin_b); + } + on = !on; +} + +volatile bool run = true; + +bool use_dma = true; + +int main() { + SystemInit(); + + init_printf(nullptr, dbg_putc); + + dbg_printf("stepper1\n"); + + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); + + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); + NVIC_InitTypeDef NVIC_InitStruct = { + NVIC_IRQChannel: DMA1_Channel3_IRQn, + NVIC_IRQChannelPreemptionPriority: 0, + NVIC_IRQChannelSubPriority: 0, + NVIC_IRQChannelCmd: ENABLE, + }; + NVIC_Init(&NVIC_InitStruct); + NVIC_EnableIRQ(DMA1_Channel3_IRQn); + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO + | RCC_APB2Periph_GPIOA +// | RCC_APB2Periph_GPIOB +// | RCC_APB2Periph_GPIOC +// | RCC_APB2Periph_ADC1 +// | RCC_APB2Periph_ADC2 +// | RCC_APB2Periph_ADC3, + , ENABLE); + + /* ***************************************** */ + + gpio_a_setup(); + + pwm_init(); + + while (run) { + if (use_dma) { +// dbg_printf("dma setup..."); + pwm_transfer_setup(); +// dbg_printf("done\n"); + + int count = 0; + while (dma_busy) { + count++; + } + +// dbg_printf("dma tx done: count=%d\n", count); + } else { + gpio_a_loop(); + } + } + + return 0; +} + +extern "C" +void DMA1_Channel3_IRQHandler() { + ITStatus tc = DMA_GetITStatus(DMA1_IT_TC3); + ITStatus te = DMA_GetITStatus(DMA1_IT_TE3); + ITStatus ht = DMA_GetITStatus(DMA1_IT_HT3); + + if (tc || te || ht) { + DMA_ClearITPendingBit(DMA1_IT_GL3); + dma_busy = false; + } +} diff --git a/apps/stepper2/CMakeLists.txt b/apps/stepper2/CMakeLists.txt new file mode 100644 index 0000000..67d76b0 --- /dev/null +++ b/apps/stepper2/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(stepper2.elf stepper2.cpp + ${STM32F10X_STDPERIPH_LIB}/Libraries/CMSIS/CM3/CoreSupport/core_cm3.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/misc.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_spi.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_tim.c + ${STM32F10X_STDPERIPH_LIB}/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_dma.c + $ + ) + +target_include_directories(stepper2.elf PUBLIC + $ + ) +target_compile_definitions(stepper2.elf PUBLIC ${STM32F10X_STDPERIPH_DEFINES}) +target_link_libraries(stepper2.elf tinyprintf) + +set_target_properties(stepper2.elf PROPERTIES LINK_FLAGS "-nostartfiles -T${CMAKE_SOURCE_DIR}/cmake/stm32.ld") +add_extra_commands(stepper2.elf) diff --git a/apps/stepper2/stepper2.cpp b/apps/stepper2/stepper2.cpp new file mode 100644 index 0000000..75603ac --- /dev/null +++ b/apps/stepper2/stepper2.cpp @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "tinyprintf.h" +#include "playground.h" + +extern "C" +__attribute__((naked, used)) +void HardFault_Handler_C(uint32_t *hardfault_args) { + dbg_printf("r0 = 0x%08lx (%lu)\n", hardfault_args[0], hardfault_args[0]); + dbg_printf("r1 = 0x%08lx (%lu)\n", hardfault_args[1], hardfault_args[1]); + dbg_printf("r2 = 0x%08lx (%lu)\n", hardfault_args[2], hardfault_args[2]); + dbg_printf("r3 = 0x%08lx (%lu)\n", hardfault_args[3], hardfault_args[3]); + dbg_printf("r12 = 0x%08lx (%lu)\n", hardfault_args[4], hardfault_args[4]); + dbg_printf("lr = 0x%08lx (%lu)\n", hardfault_args[5], hardfault_args[5]); + dbg_printf("pc = 0x%08lx (%lu)\n", hardfault_args[6], hardfault_args[6]); + dbg_printf("psr = 0x%08lx (%lu)\n", hardfault_args[7], hardfault_args[7]); + dbg_printf("\n"); + + halt(); +} + +volatile bool dma_busy; + +struct motor_t { + GPIO_TypeDef *gpio_port; + const uint16_t ah; + const uint16_t al; +}; + +static struct motor_t motor_a = { + gpio_port: GPIOA, + ah : GPIO_Pin_0, + al : GPIO_Pin_1, +}; + +TIM_TypeDef *pwm_timer = TIM2; + +struct apb_bus_1 { +}; +struct apb_bus_2 { +}; + +template +class apb_ctrl { +public: + static void RCC_PeriphClockCmd(uint32_t peripherals, FunctionalState functionalState) { + RCC_PeriphClockCmd(apb_bus{}, peripherals, functionalState); + } + +private: + static void RCC_PeriphClockCmd(apb_bus_1, uint32_t peripherals, FunctionalState functionalState) { + RCC_APB1PeriphClockCmd(peripherals, functionalState); + } + + static void RCC_PeriphClockCmd(apb_bus_2, uint32_t peripherals, FunctionalState functionalState) { + RCC_APB2PeriphClockCmd(functionalState, functionalState); + } + +public: + static void RCC_PeriphResetCmd(uint32_t peripherals, FunctionalState functionalState) { + RCC_PeriphResetCmd(apb_bus{}, peripherals, functionalState); + } + +private: + static void RCC_PeriphResetCmd(apb_bus_1, uint32_t peripherals, FunctionalState functionalState) { + RCC_APB1PeriphResetCmd(peripherals, functionalState); + } + + static void RCC_PeriphResetCmd(apb_bus_2, uint32_t peripherals, FunctionalState functionalState) { + RCC_APB2PeriphResetCmd(functionalState, functionalState); + } +}; + +template +class apb_device { +public: + static void reset() { + apb_ctrl::RCC_PeriphResetCmd(peripheral, ENABLE); + apb_ctrl::RCC_PeriphResetCmd(peripheral, DISABLE); + } + + static void enable() { + apb_ctrl::RCC_PeriphClockCmd(peripheral, ENABLE); + } + + static void disable() { + apb_ctrl::RCC_PeriphClockCmd(peripheral, DISABLE); + } +}; + +template +class gpio_port : public apb_device { +public: +}; + +class gpio_port_a : public gpio_port { +}; + +class gpio_port_b : public gpio_port { +}; + +class gpio_port_c : public gpio_port { +}; + +class tim2 : public apb_device { +}; + +void timer_init(TIM_TypeDef *tim, uint16_t period) { + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); +// tim2::reset(); +// tim2::enable(); + + TIM_TimeBaseInitTypeDef timerInitStructure; + TIM_TimeBaseStructInit(&timerInitStructure); + timerInitStructure.TIM_Prescaler = 0; + timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up; + timerInitStructure.TIM_Period = period; + timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; + timerInitStructure.TIM_RepetitionCounter = 0; + TIM_TimeBaseInit(tim, &timerInitStructure); +} + +void pwm_control(TIM_TypeDef *tim, uint16_t pulse) { + TIM_OCInitTypeDef outputChannelInit; + outputChannelInit.TIM_OCMode = TIM_OCMode_PWM1; + outputChannelInit.TIM_Pulse = pulse; + outputChannelInit.TIM_OutputState = TIM_OutputState_Enable; + outputChannelInit.TIM_OCPolarity = TIM_OCPolarity_High; + + TIM_OC1Init(tim, &outputChannelInit); + TIM_OC1PreloadConfig(tim, TIM_OCPreload_Enable); + + TIM_OC2Init(tim, &outputChannelInit); + TIM_OC2PreloadConfig(tim, TIM_OCPreload_Enable); +} + +void gpio_init(GPIO_TypeDef* gpio_port, uint16_t pins, bool use_af) { + GPIO_InitTypeDef init; + GPIO_StructInit(&init); + init.GPIO_Mode = use_af ? GPIO_Mode_AF_PP : GPIO_Mode_Out_PP; + init.GPIO_Pin = pins; + init.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(gpio_port, &init); +} + +volatile bool run = true; + +bool use_gpio = false; + +int main() { + SystemInit(); + + init_printf(nullptr, dbg_putc); + + dbg_printf("stepper2\n"); + + NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); + NVIC_InitTypeDef NVIC_InitStruct = { + NVIC_IRQChannel: DMA1_Channel3_IRQn, + NVIC_IRQChannelPreemptionPriority: 0, + NVIC_IRQChannelSubPriority: 0, + NVIC_IRQChannelCmd: ENABLE, + }; + NVIC_Init(&NVIC_InitStruct); + NVIC_EnableIRQ(DMA1_Channel3_IRQn); + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO + | RCC_APB2Periph_GPIOA + | RCC_APB2Periph_GPIOB + | RCC_APB2Periph_GPIOC + | RCC_APB2Periph_ADC1 + | RCC_APB2Periph_ADC2 + | RCC_APB2Periph_ADC3 + | RCC_APB2Periph_TIM1, + ENABLE); + + const uint16_t period = 1000; + + const int count_max = 10000; + int count = count_max - 1; + + uint16_t pulse = 500; + + gpio_init(motor_a.gpio_port, motor_a.ah | motor_a.al, !use_gpio); + timer_init(pwm_timer, period); + pwm_control(pwm_timer, pulse); + + TIM_ARRPreloadConfig(pwm_timer, ENABLE); + TIM_Cmd(pwm_timer, ENABLE); + + while (run) { + static bool on = false; + + if (use_gpio) { + if (on) { + GPIO_SetBits(motor_a.gpio_port, motor_a.ah); + GPIO_SetBits(motor_a.gpio_port, motor_a.al); + } else { + GPIO_ResetBits(motor_a.gpio_port, motor_a.ah); + GPIO_ResetBits(motor_a.gpio_port, motor_a.al); + } + on = !on; + } else { + count++; + + if (count == count_max) { + count = 0; + + pulse++; + + if (pulse > period) { + pulse = 0; + } + + pwm_timer->CCR1 = pulse; + pwm_timer->CCR2 = period - pulse; + } + } + } + + return 0; +} + +extern "C" +void DMA1_Channel3_IRQHandler() { + ITStatus tc = DMA_GetITStatus(DMA1_IT_TC3); + ITStatus te = DMA_GetITStatus(DMA1_IT_TE3); + ITStatus ht = DMA_GetITStatus(DMA1_IT_HT3); + + if (tc || te || ht) { + DMA_ClearITPendingBit(DMA1_IT_GL3); + dma_busy = false; + } +} diff --git a/gdb-start b/gdb-start index de85932..fbd8789 100644 --- a/gdb-start +++ b/gdb-start @@ -1,5 +1,6 @@ target remote tcp:localhost:3333 monitor reset halt +set remotetimeout 10 define flash_test1 shell cd build && make test1.elf @@ -46,6 +47,36 @@ define flash_serial2 hbreak halt end +define flash_stepper1 + shell cd build && make stepper1.elf + + monitor arm semihosting enable + monitor reset halt + + set confirm off + file build/apps/stepper1/stepper1.elf + load build/apps/stepper1/stepper1.elf + set confirm on + + monitor stm32f1x.cpu mwb 0x20000000 0x5a 20480 + hbreak halt +end + +define flash_stepper2 + shell cd build && make stepper2.elf + + monitor arm semihosting enable + monitor reset halt + + set confirm off + file build/apps/stepper2/stepper2.elf + load build/apps/stepper2/stepper2.elf + set confirm on + + monitor stm32f1x.cpu mwb 0x20000000 0x5a 20480 + hbreak halt +end + define flash_os1 shell cd build && make os1.elf -- cgit v1.2.3