본문 바로가기

코딩/잡다한 코딩

[하드웨어] STM32를 이용한 방공시스템 제작


울학교에서 이번 학기에 내가 진행한 프로젝트이다.


통틀어서 1등을 먹었는데, 기록해두면 나중에 면접이든 뭐든 쓸모가 있지 않을까 싶어서 이렇게 기록으로 남겨둔다.





또 혹시 아는가


내 후배들이 내 글을 보고 도움을 받을지...











일단 전체 코드부터 올린다.



↓클릭시 다운


main.c


#include "misc.h"
#include "stm32f10x.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_adc.h"
#include "lcd.h"
#include "Touch.h"
#include "stm32f10x_tim.h"
uint16_t pos_x,pos_y;
uint16_t pix_x,pix_y;

int motorTime = 0;
int motorTimePhrase = 100;
int sensorDegree[6] = {64,54,44,34,21,5};

int launchDegree = 140;
int launchInitDegree = 30;

vu32 ADCvalue[6];

int time = 0;

int timerStartFlag = 0;

int radarRotateFlag = 0;

int delayCounter = 0;
int delayFlag = 0;
int delayStart = 0;

NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

void launcherDegreeSet(int sensorNum);
void Sencing();
void Launch();

int degreeToPWM(int degree){
	degree -= 15;
	return (2300 - 700) / 180 * degree + 700;
}
void delay(int i){
	int j;
	for(j=0; j<=i * 100000; j++);
}

void changeLED(int num){
	if(num == 1) GPIOD->ODR ^= GPIO_Pin_2;
	else if(num == 2) GPIOD->ODR ^= GPIO_Pin_3;
	else if(num == 3) GPIOD->ODR ^= GPIO_Pin_4;
	else if(num == 4) GPIOD->ODR ^= GPIO_Pin_7;
}

void USART1_Init(void)
{
  USART_InitTypeDef usart1_init_struct;
  GPIO_InitTypeDef gpioa_init_struct;
  NVIC_InitTypeDef NVIC_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO |
		  RCC_APB2Periph_GPIOA, ENABLE);

  gpioa_init_struct.GPIO_Pin = GPIO_Pin_9;
  gpioa_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
  gpioa_init_struct.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &gpioa_init_struct);
  gpioa_init_struct.GPIO_Pin = GPIO_Pin_10;
  gpioa_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
  gpioa_init_struct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &gpioa_init_struct);

  usart1_init_struct.USART_BaudRate = 57600;
  usart1_init_struct.USART_WordLength = USART_WordLength_8b;
  usart1_init_struct.USART_StopBits = USART_StopBits_1;
  usart1_init_struct.USART_Parity = USART_Parity_No;
  usart1_init_struct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  usart1_init_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_Init(USART1, &usart1_init_struct);
  USART_Cmd(USART1, ENABLE);
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void RCC_Config(void) {
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
}

void GPIO_Config(void) {
	GPIO_InitTypeDef GPIO_init;
	GPIO_init.GPIO_Pin = GPIO_Pin_5;
	GPIO_init.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_init);

	GPIO_init.GPIO_Pin = GPIO_Pin_6;
	GPIO_init.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_init);

	GPIO_init.GPIO_Pin = GPIO_Pin_7;
	GPIO_init.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_init);

	GPIO_init.GPIO_Pin = GPIO_Pin_1;
	GPIO_init.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_init);

	GPIO_init.GPIO_Pin = GPIO_Pin_1;
	GPIO_init.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_init);

	GPIO_init.GPIO_Pin = GPIO_Pin_1;
	GPIO_init.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_init);

	/*
	GPIO_init.GPIO_Pin = GPIO_Pin_3;
	GPIO_init.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_init);
	 */
	GPIO_init.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_init.GPIO_Pin = (GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_7);
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOD, &GPIO_init);
}

void NVIC_Config(void) {
	NVIC_InitTypeDef NVIC_init;
	NVIC_init.NVIC_IRQChannel = ADC1_2_IRQn;
	NVIC_init.NVIC_IRQChannelCmd = ENABLE;
	NVIC_init.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_init.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_init);
}

void ADC_Config(void) {
	ADC_InitTypeDef ADC_init;

	// ADC1 parameter
	ADC_init.ADC_ContinuousConvMode = ENABLE;
	ADC_init.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_init.ADC_Mode = ADC_Mode_Independent;
	ADC_init.ADC_NbrOfChannel = 6;
	ADC_init.ADC_ScanConvMode = ENABLE;
	ADC_Init(ADC1, &ADC_init);

	// ADC Channel Config
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 2, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 3, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 4, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 5, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 6, ADC_SampleTime_239Cycles5);
	//ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_239Cycles5);


	ADC_DMACmd(ADC1, ENABLE);
	ADC_Cmd(ADC1, ENABLE);
	//ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

	// ADC Start
	ADC_ResetCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}


void DMA_Channel_Config(void){
	DMA_InitTypeDef DMA_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

	DMA_DeInit(DMA1_Channel1);
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &ADC1->DR;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) ADCvalue;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = 6;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);

	DMA_Cmd(DMA1_Channel1, ENABLE);
}


void init_Timer2() {
  NVIC_InitTypeDef NVIC_InitStructure; // for interreupt
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // timerbase...

  /* TIM2 Clock Enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  /* Enable TIM2 Global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  /* TIM2 Initialize */
  TIM_TimeBaseStructure.TIM_Period = 1200;
  TIM_TimeBaseStructure.TIM_Prescaler = 600;
  //계산방법 : 1/72mhz * 1200 * 60000
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  /* TIM2 Enale */
  TIM_ARRPreloadConfig(TIM2, ENABLE);
  TIM_Cmd(TIM2, ENABLE);
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // interrupt enable
}


void printADCvalue(int size){
	int i = 0;
	float a = 0.0;
	for(i = 0 ; i < size ; i ++){
		a = ADCvalue[i];
		a = (67870.0 / (ADCvalue[i]-3.0));
		LCD_ShowNum(50, 100+i*20, a , 4, BLACK, WHITE);
	}
}

/*
void TIM1_UP_IRQHandler(void){ // 10Hz호출

	if(TIM_GetITStatus(TIM1,TIM_IT_Update) != RESET ){
		TIM_ClearFlag(TIM1,TIM_FLAG_Update);
		TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
		motorTime ++;
		if(1){
			GPIOD->ODR ^= GPIO_Pin_4;
			GPIOD->ODR ^= GPIO_Pin_7;
			motorTime = 0;
		}
	}
}
*/
void TIM2_IRQHandler(void) {
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	//Clears the TIMx's interrupt pending bits.

	if(timerStartFlag == 1){

		//LCD_ShowNum(100, 200, timerStartFlag , 4, BLACK, WHITE);

		//printADCvalue(6);

	  	motorTime ++;
	  	if(motorTime % 20 == 0){
	  		Sencing();
	  	}
		if(motorTime >= motorTimePhrase){
			motorTime = 0;
			changeLED(4);
		}
	}

	if(delayStart == 1){
		delayCounter++;
		if(delayCounter >= 300){
			delayFlag = 1;
			delayStart = 0;
		}
	}
}


void lightOn(){
	int i = 0;
	float a = 0.0;
	for(i = 0 ; i < 6 ; i ++){
		a = (67870.0 / (ADCvalue[i]-3.0));
		if( a < 30){
			int j = 0;
			for(j = 0 ; j < 4 ; j ++){
				changeLED(2);
				delay(10);
			}
			break;
		}
	}
}

void timerStart(){
	timerStartFlag = 1;

}

void RotateRadar(){
	if(radarRotateFlag == 0){
		radarRotateFlag = 1;
		changeLED(3);
	}
}
void StopRader(){
	if(radarRotateFlag == 1){
		radarRotateFlag = 0;
		changeLED(3);
	}
}
void Sencing(){
	int sensorNum = 0;
	int a = 0;
	for(sensorNum = 0 ; sensorNum < 6 ; sensorNum ++){
		a = (67870.0 / (ADCvalue[sensorNum]-3.0));
		if(a < 30){
			StopRader();
			launcherDegreeSet(sensorNum);
			Launch();
			changeLED(2);

			LCD_ShowNum(40, 150, a, 4, BLACK, WHITE);
			LCD_ShowString(90, 150, "cm", BLACK, WHITE);

			LCD_ShowNum(40, 180, sensorDegree[sensorNum], 4, BLACK, WHITE);
			LCD_ShowString(90, 180, "Degree!", BLACK, WHITE);

			break;

		}
	}
	return;
}


void ButtonInputToRotate(){

	GPIO_InitTypeDef GPIO_init;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	GPIO_init.GPIO_Pin = GPIO_Pin_11;
	GPIO_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOD, &GPIO_init);

	while(1){
		if((GPIOD->IDR & GPIO_Pin_11)){

			delayStart = 1;

			while(1){
				if(delayFlag == 1){
					break;
				}
			}
			RotateRadar();

			launcherDegreeSet(2);

			timerStart();

			break;
		}
	}


}
void luncherDegreeInit(){
	launcherDegreeSet(6);
}
void PWM_Timer_Config(){
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	uint16_t PrescalerValue;


    /* TIM3 clock enable */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    /* GPIOA and GPIOB clock enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);

    /*GPIOB Configuration: TIM3 channel1, 2, 3 and 4 */
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);



    TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM3, ENABLE);
    TIM_Cmd(TIM3, ENABLE);
    PrescalerValue = (uint16_t) (SystemCoreClock / 1000000) - 1;
    TIM_TimeBaseStructure.TIM_Period = 20000-1;
    TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

}

void launcherDegreeSet(int sensorNum){
	int degree = sensorDegree[sensorNum - 1]-10;
    TIM_OCInitTypeDef  TIM_OCInitStructure;

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = degreeToPWM(degree);
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);

}
void luncherControlerDegreeInit(){
    TIM_OCInitTypeDef  TIM_OCInitStructure;

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = degreeToPWM(launchInitDegree);

    TIM_OC4Init(TIM3, &TIM_OCInitStructure);

}
void Launch(){
    TIM_OCInitTypeDef  TIM_OCInitStructure;

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse =  degreeToPWM(launchDegree);

    TIM_OC4Init(TIM3, &TIM_OCInitStructure);

    timerStartFlag=0;
    delay(100);
    luncherControlerDegreeInit();


}
int color[12]={WHITE,CYAN,BLUE,RED,MAGENTA,LGRAY,GREEN,YELLOW,BROWN,BRRED,GRAY};
int main(){
	timerStartFlag=0;

	SystemInit();
	LCD_Init();
	//Touch_Configuration();
	//Touch_Adjust();
	//TIM_Init();
	//init_MoterTimer();
	LCD_Clear(WHITE);
	RCC_Config();
	GPIO_Config();
	NVIC_Config();
	ADC_Config();
	DMA_Channel_Config();

	PWM_Timer_Config();

	init_Timer2();


	changeLED(1);
	luncherDegreeInit();
	luncherControlerDegreeInit();

	ButtonInputToRotate();



	while(1){
		delay(10);
		LCD_ShowString(50, 50, "!TURRET ENGAGE!", BLACK, WHITE);
		//lightOn();
	}
}









STM32라는 보드가 있다.


거기에 이 코드를 얹고, 센서 좀 달고 모터 좀 달아서 만들었다.




결과적으로 아래와 같은 완성품이 나왔다.





사담으로, 지금 동영상올리면서 느낀건데 동영상 카테고리에 학문이 없다. 






여튼, 이렇게 적외선 센서로 만들어진 레이더가 뱅글뱅글 왔다갔다하면서 돌다가


적 비행기가 나타나면 미사일(빨대)를 발사한다!


단순하지만, 멋있다!





외형적인 구성은 크게 다음과 같다.



1. 레이더는 크게 두가지로 구성되어 있다. 적외선 센서, 그리고 DC모터.


각각 각도가 다른 적외선 센서 6개가 배치되어 적 비행기를 탐지하고,


DC모터의 '왔다 갔다 회전'은 릴레이로 +극과 -극을 바꾸어주는 것으로 구현했다.



2. 발사대의 각도조정과 발사는 둘다 서보모터가 수행한다.


빨대를 서보모터 A가 막고있다가 위의 레이더가 적비행기를 탐지하면 이 서보모터 A가 90도 회전하면서 발사대 마개를 치운다.


그러면 빨대가 날라간다!


또한 발사대 하단의 서보모터B에 펄스 신호를 줌으로서 발사대의 각도를 조절한다.






내부적인 구성 중 중요한 부분은 다음과 같다.



1. 타이머를 통해서 적외선 센서의 입력값을 ADC로 감지하고, 그 값이 일정 값 미만으로 떨어지면 (= 일정 거리 이하의 물체를 감지하면),


그 탐지한 적외선 센서의 위치에 따라 서보모터 B의 각도를 조정하고, 발사대를 막고있는 서보모터A를 작동시켜 발사한다!



2. DC모터의 '왔다갔다회전'을 구현하기 위해서 릴레이를 작동시키는데, 회로는 아래와 같다.


릴레이는 NO가 노말 오픈, NC가 노말 클로즈. 



이렇게 회로를 구성하면 3번 LED가 켜지면 시계방향, 꺼지면 반시계 방향으로 회전한다.









아래는 플로우 차트다.







솔직히, 센서도 한종류만 썼고, 모터도 DC모터와 서보모터라는 두 종류의 모터만 썼다. 즉, 단순하다.



하지만 아름답다!



내가 만든 것 중 가장 잘 만들었다. 


완성하고 디게 뿌듯했다.








어려웠던 점이라...


레이더가 연약해서 잘 부서진다는 것과


서보모터를 처음 써봐서 펄스 신호를 주는 방식이 생소했다는 점 정도.




느낀 점이라.....


임베디드 하시는 선배님들이 존경스러워졌다는 것과


구글링하는 실력이 향상되었고


아두이노 한번 만져보고 싶다는 생각이 들었다.







끝!