Продолжаю, и этой статьей пожалуй закончу описание конструкции самодельного 3D сканера, которая была описана в Эта статья .
Вообще, почти два года назад, когда мы только начинали этот проект, подобных сканеров было не так много, как сегодня.
Так что механику описывать нет смысла (она одинакова для всех подобных сканеров), а программную часть я не писал.
И эта статья еще может помочь тем, кто все же решил собрать сканер самостоятельно.
Эта статья была написана для них.
О чем
Никаких картинок, схем и даже котиков не будет. Хотя нет. Они будут.А все потому, что дальше будет только код. Здесь я приведу программу только для четвертого сканера, так как прошивка третьего сканера утеряна (именно после этого случая я начал делать бэкапы).
Как вы, наверное, помните Плата четвертого сканера содержит микроконтроллер STM32F401RE, поэтому код будет написан для семейства F4.
Библиотеки
Сначала мы должны подключить все необходимые библиотеки: Библиотеки #include "stm32f4xx.h" #include "stm32f4xx_exti.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_tim.h" #include "stm32f4xx_usart.h" #include "разное.h" Из названия библиотек понятно, что они одинаковы для всего семейства STM32F4 (а значит, код можно легко перенести на любой другой микроконтроллер этого семейства.
Например, на STM32F407VB).
Второе, что понятно из названий, это название периферии, для которой нужны эти библиотеки.
Особняком стоит библиотека misc.h. Эта библиотека необходима для обработки прерываний.
Инициализация периферийных устройств и переменных
Начнем с объявления глобальных переменных: Переменные интервал Шаг; интервал DelayTime=100000; символ ConfigState; uint8_t StepsPerComand=1; uint8_t LaserPower = 0; uint8_t LightPower = 0; Переменная Step показывает, на каком шаге находится двигатель в данный момент. DelayTime определяет время между шагами.Это необходимо для того, чтобы мотор успевал вращать вал.
Устанавливается в количестве тактов процессора.
О ConfigState и StepsPerComand я расскажу позже.
LaserPower и LightPower задают мощность лазера и подсветку соответственно (подсветка не реализована, но ШИМ все равно выводится).
Глобальные переменные находятся здесь.
Перейдем к функциям.
Для начала давайте напишем эту простую, но полезную функцию: Задерживать void Задержка (uint32_t n) { uint32_t я; для (я=0;я Штифты двигателя недействительный InitMotorGPIO (недействительный) { GPIO_InitTypeDef MotorGPIO; RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE); RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOB, ENABLE); MotorGPIO.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; MotorGPIO.GPIO_Mode = GPIO_Mode_OUT; MotorGPIO.GPIO_OType = GPIO_OType_PP; MotorGPIO.GPIO_PuPd = GPIO_PuPd_DOWN; MotorGPIO.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOA, &MotorGPIO); MotorGPIO.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOB, &MotorGPIO); } Далее мотором необходимо управлять.
Для этого существуют следующие функции: Блок управления двигателем недействительный MotorResetGPIO (недействительный) { GPIO_ResetBits(GPIOA, GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); GPIO_ResetBits(GPIOB, GPIO_Pin_6); } void MotorCoil (int катушка) { МоторСбросGPIO(); переключатель (катушка) { случай 1: GPIO_SetBits(GPIOA, GPIO_Pin_5); перерыв; случай 2: GPIO_SetBits(GPIOA, GPIO_Pin_6); перерыв; случай 3: GPIO_SetBits(GPIOA, GPIO_Pin_7); перерыв; случай 4: GPIO_SetBits(GPIOB, GPIO_Pin_6); перерыв; } } void MotorStepUP(int n) { интервал я; для (я=0;я 4) {Шаг=1;} МоторКойл (Шаг); Задержка (Время задержки); } } void MotorStepDOWN(int n) { интервал я; для (я=0;я Название спойлера недействительный InitUsartGPIO (недействительный) { GPIO_InitTypeDef UsartGPIO; RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE); GPIO_PinAFConfig (GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig (GPIOA, GPIO_PinSource3, GPIO_AF_USART2); UsartGPIO.GPIO_OType = GPIO_OType_PP; UsartGPIO.GPIO_PuPd = GPIO_PuPd_UP; UsartGPIO.GPIO_Mode = GPIO_Mode_AF; UsartGPIO.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3; UsartGPIO.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &UsartGPIO); } недействительный InitUsart (недействительный) { ИнитУсартGPIO(); USART_InitTypeDef USART_InitStructure; RCC_APB1PeriphClockCmd (RCC_APB1Periph_USART2, ENABLE); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USART_InitStructure); USART_Cmd (USART2, ВКЛЮЧИТЬ); NVIC_InitTypeDef UsartNVIC; UsartNVIC.NVIC_IRQChannel = USART2_IRQn; UsartNVIC.NVIC_IRQChannelPreemptionPriority = 2; UsartNVIC.NVIC_IRQChannelSubPriority = 2; UsartNVIC.NVIC_IRQChannelCmd = ВКЛЮЧИТЬ; NVIC_Init(&UsartNVIC); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); } Здесь тоже особо нечего комментировать.
А2 – Прием, А3 – Передача.
Скорость – 9600 бод. Если только не обратить внимание на последние 9 строк.
Там мы инициализируем прерывание.
Он появится при получении данных через USART. Последний блок функций — это функции управления лазером и подсветкой.
Для этого необходимо настроить таймер и вывести ШИМ на интересующие нас ножки (микроконтроллер): Инициализация таймера недействительный InitLaserGPIO (недействительный) { GPIO_InitTypeDef LaserGPIO; RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOC, ENABLE); GPIO_PinAFConfig (GPIOC, GPIO_PinSource7, GPIO_AF_TIM3); LaserGPIO.GPIO_Mode = GPIO_Mode_AF; LaserGPIO.GPIO_OType = GPIO_OType_PP; LaserGPIO.GPIO_Pin = GPIO_Pin_7; LaserGPIO.GPIO_PuPd = GPIO_PuPd_UP; LaserGPIO.GPIO_Speed = GPIO_Speed_100МГц; GPIO_Init(GPIOC, &LaserGPIO); } недействительный InitLightGPIO (недействительный) { GPIO_InitTypeDef LightGPIO; GPIO_PinAFConfig (GPIOB, GPIO_PinSource4, GPIO_AF_TIM3); RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOB, ENABLE); LightGPIO.GPIO_Mode = GPIO_Mode_AF; LightGPIO.GPIO_OType = GPIO_OType_PP; LightGPIO.GPIO_Pin = GPIO_Pin_4; LightGPIO.GPIO_PuPd = GPIO_PuPd_NOPULL; LightGPIO.GPIO_Speed = GPIO_Speed_100МГц; GPIO_Init(GPIOB, &LightGPIO); } недействительный InitLaserAndLight (недействительный) { InitLaserGPIO(); InitLightGPIO(); TIM_TimeBaseInitTypeDef BaseTIM; RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3, ENABLE); BaseTIM.TIM_Period=0xFF; BaseTIM.TIM_Prescaler=3; BaseTIM.TIM_CounterMode=TIM_CounterMode_Up; BaseTIM.TIM_ClockDivision=0; TIM_TimeBaseInit(TIM3,&BaseTIM); TIM_OCInitTypeDef TimOC; TimOC.TIM_OCMode=TIM_OCMode_PWM1; TimOC.TIM_OutputState=TIM_OutputState_Enable; ТимOC.TIM_Pulse=0; TimOC.TIM_OCPolarity=TIM_OCPolarity_High; TIM_OC1Init(TIM3,&TimOC); TIM_OC1PreloadConfig (TIM3, TIM_OCPreload_Enable); TimOC.TIM_OutputState = TIM_OutputState_Enable; ТимOC.TIM_Pulse=0; TIM_OC2Init(TIM3, &TimOC); TIM_OC2PreloadConfig (TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig (TIM3, ENABLE); TIM_Cmd(TIM3, ВКЛЮЧИТЬ); } void SetLaserPower(uint8_t p) { TIM3-> CCR2 = р; } void SetLightPower(uint8_t p) { TIM3-> CCR1 = р; } Сначала мы инициализируем GPIO (C7 для лазера и B4 для подсветки).
Затем ставим таймер.
И последние две функции называются очень понятно.
На этом периферия заканчивается.
Осталось сделать функцию полного отключения всего (чтобы не грелось, когда оно не нужно): Полное отключение недействительный FullReset (недействительный) { МоторСбросGPIO(); SetLaserPower(0); SetLightPower(0); } И конечно главное: основной int main (недействительный) { ИнитМоторГПИО(); ИнитЛазерИСвет(); ИнитУсарт(); пока(1) { } } Просто небольшой код в main, вам не кажется? Это все из-за того, что вся обработка происходит в перерывах.
Скорее всего, в одном, который мы инициализировали при настройке USSART. Давайте посмотрим на обработчик этого прерывания: Нужно больше дел!!! недействительный USART2_IRQHandler (недействительный) { данные символов; данные = USART_ReceiveData (USART2); если (данные<10) {MotorStepUP(data);} если (ConfigState){ переключатель (ConfigState) { случай «d»: DelayTime = данные; перерыв; случай 'p': LaserPower = данные; SetLaserPower(LaserPower); перерыв; случай 'i': StepsPerComand = данные; перерыв; случай «l»: LightPower = данные; перерыв; } КонфигСтате = 0; USART_SendData(USART2, 'R'); } еще { переключатель (данные){ случай 's': MotorStepUP(StepsPerComand); перерыв; регистр 'n': SetLaserPower(LaserPower); перерыв; случай 'f': SetLaserPower(0); перерыв; случай 'r': FullReset(); перерыв; случай «b»: MotorStepDOWN(StepsPerComand); перерыв; случай «h»: SetLightPower(LightPower); перерыв; случай 'u': SetLightPower(0); перерыв; по умолчанию: ConfigState=данные; } if (ConfigState) {USART_SendData(USART2, '#');} еще {USART_SendData(USART2, 'R');} } USART_ReceiveData(USART2); данные = 0; USART_ClearITPendingBit(USART2, USART_IT_RXNE); } Давайте посмотрим на это построчно.
Сначала мы создали переменную данных.
Затем мы записали в него данные от USART. Эм.
Что это за магическое число 10? И вот маленькая хитрость.
Если число (код символа) меньше 10, то двигатель выполнит количество шагов, равное коду символа.
Далее идет проверка ConfigState. И во второй раз давайте пропустим эту переменную и посмотрим, что написано в else. Вот набор команд: s — сделать шаги StepsPerComand вперед (это та переменная, которую мы пропустили в начале статьи).
n — включить лазер (в LaserPower указана мощность).
е - выключить лазер r - ПОЛНОЕ ОТКЛЮЧЕНИЕ (а, звучит хорошо).
б — сделать шаги StepsPerComand назад. h — включить подсветку (в LightPower указана мощность).
у — выключить подсветку.
Если команда не соответствует ни одному из вышеперечисленных условий, то символ записывается в переменную ConfigState. Теперь вернемся снова к условию в 4-й строке функции.
Если ConfigState не равен 0, то настраиваем параметр.
Установите новое значение.
Если приходит d, то устанавливаем новый DelayTime, если p, то новую мощность лазера (и эта мощность сразу задается на лазере, т.е.
он включается), если приходит i, то меняем переменную StepsPerComand, а если l , затем меняем мощность подсветки.
Более того, когда что-то записывается в ConfigState, микроконтроллер отправляет на терминал символ «#», указывающий, что он ожидает второго символа.
Если он просто выполнил команду, то приходит ответ «R».
В конце мы просто очищаем буфер приема и сбрасываем флаг прерывания.
На этом код заканчивается.
Финал
Вот и все.Надеюсь, этот материал поможет тем, кто решит повторить мой опыт (или кого вынудят преподаватели).
Я не претендую на идеальное решение или на то, что все сделано правильно.
Конечно, на каждом этапе есть что-то, что можно сделать лучше.
Так что лучше не просто повторять, но и дополнять/дополнять самостоятельно.
Удачи всем в их разработках и проектах! Надеюсь, это кому-то хоть немного поможет. Теги: #stm32 #Сделай сам или Сделай сам #3d сканер #сделай сам #шаговый двигатель
-
Обзор: Cloudmark Spamnet
19 Oct, 24 -
Введение В Смарт-Карты
19 Oct, 24 -
Дам, Хенрик Карл Питер
19 Oct, 24