[아두이노] Synchronization

HEEJOON MOON·2022년 6월 6일
0

키패드 버튼 4개를 누르면 FND에 왼쪽으로 shift되며 표시

  • 키패드 ISR과 키패드 TASK의 동기화 : 키패드 task는 키패드 ISR로부터의 버튼 전달을 기다림. Keypad ISR에서 눌린 버튼을 전역변수(SendValue)를 통해 키패드 Task에 전달. 동기화를 위해 binary semaphore 사용.
  • FND task는 Keypad Task로부터의 버튼 전달을 기다림. Keypad Task는 눌린 버튼을 전역 변수통해 FND task에 전달. FND task는 전달받은 버튼을 FND에 왼쪽으로 shift하며 표시
#include <FreeRTOS_AVR.h>

#define MS2TICKS(ms) (ms/portTICK_PERIOD_MS)
#define FND_SIZE 6

const int Keypad[4] = { 2, 3, 21, 20 }; // Keypad 핀 정의
const int FndSelectPin[6] = { 22, 23, 24, 25, 26, 27 }; // FND Sel
const int FndPin[8] = { 30, 31, 32, 33, 34, 35, 36, 37 }; // A~H
const int FndFont[10] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x60, 0x7D, 0x07, 0x7F, 0x67 };  // 숫자 0 ~ 9

// 세마포어 전역 변수로 선언
SemaphoreHandle_t Sem;
int SendValue = 0;
int Fnd[FND_SIZE] = {0,};

// FND 값을 왼쪽으로 shift하고 파라미터로 들어온 새로운 값을 0번지에 넣는 함수
void ShiftInsert(int data){
	int i;
    
    for(i=1; i<FND_SIZE; i++){
    	Fnd[FND_SIZE - i] = Fnd[FND_SIZE - i - 1]; // 인덱스를 오른쪽으로 하나씩 비워줌 => 0번 인덱스가 비어있다
    }
    Fnd[0] = data;
}

// Keypad ISR -> 전역변수로 값을 전달하기 때문에 여러 task가 접근 가능. 따라서 세마포어로 보호
void KeypadControl1(){
	delay(50);
    SendValue = 1;
    xSemaphoreGive(sem); // 입력이 끝났으므로, signal()을 통해 리소스 반환
}

void KeypadControl2(){
	delay(50);
    SendValue = 2;
    xSemaphoreGive(sem); // 입력이 끝났으므로, signal()을 통해 리소스 반환
}

void KeypadControl3(){
	delay(50);
    SendValue = 3;
    xSemaphoreGive(sem); // 입력이 끝났으므로, signal()을 통해 리소스 반환
}

void KeypadControl4(){
	delay(50);
    SendValue = 4;
    xSemaphoreGive(sem); // 입력이 끝났으므로, signal()을 통해 리소스 반환
}

void KeypadTask(){
	int i, keypad;
  	while(1){
    	// 세마포어를 통해 키패트가 눌렸음을 keypad ISR로 부터 전달받음.
        // portMAX_DELAY : 세마포어를 받기 전까지 block. 입력된 번호는 Shiftinsert 통해 전역 변수 배열인 Fnd[]에 삽입.
        if(xSemaphoreTake(Sem, portMAX_DELAY)){
        	keypad = SendValue; // 키패드 ISR로 전달받은 값
            ShiftInsert(keypad): // 전역 FND[]에 전달받은 값을 삽입
        }
    }
}

void FndSelect(int pos){
	int i;
    
    for(i=0; i<6; i++){
    	if(i==pos){
        	digitalWrite(FndSelectPin[i], LOW); // FNDSEL은 LOW이면 select
        }
        else{
        	digitalWrite(FndSelectPin[i], HIGH); // FNDSEL은 HIGH면 not select
        }
    }
}

void FndDisplay(int pos, int num){
	int i;
    int flag = 0;
    int shift = 0x01; 
    
    FndSelect(pos); // position에 해당하는 FND_SEL 선택
    
    for(i=0; i<8; i++){
    	flag = (FndFont[num] & shift); // and 비트 연산을 통해 해당하는 Segment에 bit 1을 준다
        digitalWrite(FndPin[i], flag);
        shift << 1;
    }
}

// 지속적으로 Fnd[] 데이터를 FND에 출력
void FndTask(void* arg){
	int i;
    while(1){
    	for(i=0; i<FND_SIZE; i++){
        	delay(3);
            FndDisplay(i, Fnd[i]);
        }
    }
}

void setup(){
	int i;
    
    for(i=0; i<6; i++){
    	pinMode(FndSelectPin[i], OUTPUT);
    }
    for(i=0; i<8; i++){
    	pinMode(FndPin[i], OUTPUT);
    }
    for(i=0; i<4; i++){
    	pinMode(FndPin[i], INPUT);
    }
    
    // Interrupt setting
    attachInterrupt( 0, KeypadControl1, RISING );
    attachInterrupt( 1, KeypadControl2, RISING );
    attachInterrupt( 2, KeypadControl3, RISING );
    attachInterrupt( 3, KeypadControl4, RISING );
    
    // 세마포어 생성
    vSemaphoreCreateBinary(sem1); 
    
    // Task 생성
    xTaskCreate(KeypadTask, NULL, 200, NULL, 2, NULL);
    xTaskCreate(FndTask, NULL, 200, NULL, 1, NULL);
    
    // 스케쥴러 시작
    vTaskStartScheduler();
}

void loop(){}

세마포어 기반 Keypad를 이용한 모터 제어

  • 왼쪽 회전, 정지, 오른쪽 회전 3개 키에 의해 모터 동작
  • Keypad ISR과 Motor Task간 전역변수와 semaphore를 이용해 데이터 전달
#include <FreeRTOS_AVR.h>

#define MS2TICKS(ms) (ms / portTICK_PERIOD_MS)
#define FND_SIZE 6
#define MAX_BUF 2

const int Keypad[3] = { 2, 3, 21 };
const int MT_P = 10;
const int MT_N = 9;

SemaphoreHandle_t emptySem;
SemaphoreHandle_t fullSem;
SemaphoreHandle_t mutexSem;

typedef struct {
  int SendValue[MAX_BUF];
  int in;
  int out;
  int counter;
} BufferType;

BufferType buf;

int MotorValue = 0;

void SetMotor(int value) {
  Serial.print("SET Motor");
  Serial.println(value);
  MotorValue = value;
}

// Keypad ISR
// 전역 변수(SendValue)로 값을 전달하고 세마포어로 동기화
void KeypadControl1() {
  xSemaphoreTake(emptySem, portMAX_DELAY);
  xSemaphoreTake(mutexSem, portMAX_DELAY);
  
  buf.SendValue[buf.in] = 1;
  buf.in = (buf.in + 1) % MAX_BUF;
  buf.counter++;
  
  xSemaphoreGive(mutexSem);
  xSemaphoreGive(fullSem);
}

void KeypadControl2() {
  xSemaphoreTake(emptySem, portMAX_DELAY);
  xSemaphoreTake(mutexSem, portMAX_DELAY);
  
  buf.SendValue[buf.in] = 2;
  buf.in = (buf.in + 1) % MAX_BUF;
  buf.counter++;
  
  xSemaphoreGive(mutexSem);
  xSemaphoreGive(fullSem);
}

void KeypadControl3() {
  xSemaphoreTake(emptySem, portMAX_DELAY);
  xSemaphoreTake(mutexSem, portMAX_DELAY);
  
  buf.SendValue[buf.in] = 3;
  buf.in = (buf.in + 1) % MAX_BUF;
  buf.counter++;
  
  xSemaphoreGive(mutexSem);
  xSemaphoreGive(fullSem);
}

void KeypadTask( void* arg ) {
  int keypad;
  
  while(1) {
    xSemaphoreTake(fullSem, portMAX_DELAY);
    xSemaphoreTake(mutexSem, portMAX_DELAY);
    //Serial.println("KEY");

    keypad = buf.SendValue[buf.out];
    buf.out = (buf.out + 1) % MAX_BUF;
    buf.counter--;
    
    SetMotor(keypad);

    xSemaphoreGive(mutexSem);
    xSemaphoreGive(emptySem);
  }
}

void MotorTask( void* arg) {
  while(1) {
    //Serial.println(MotorValue);
  
    if(MotorValue == 1) {
      digitalWrite(MT_P, LOW);
      digitalWrite(MT_N, HIGH);
    }
  
    // Stop
    else if(MotorValue == 2) {
      digitalWrite(MT_P, LOW);
      digitalWrite(MT_N, LOW);
    }
        
    // Rotate right
    else if(MotorValue == 3) {
      digitalWrite(MT_P, HIGH);
      digitalWrite(MT_N, LOW);
    }
  }
}

void setup() {
  int i;

  Serial.begin(9600);

  pinMode(MT_P, OUTPUT);
  pinMode(MT_N, OUTPUT);
  
  for( i = 0; i < 3; i++ ) {
    pinMode( Keypad[ i ], INPUT );
  }
  
  attachInterrupt( 0, KeypadControl1, RISING );
  attachInterrupt( 1, KeypadControl2, RISING );
  attachInterrupt( 2, KeypadControl3, RISING );
  
  vSemaphoreCreateBinary(emptySem);
  vSemaphoreCreateBinary(fullSem);
  vSemaphoreCreateBinary(mutexSem);
  
  xTaskCreate( KeypadTask, NULL, 200, NULL, 2, NULL );
  xTaskCreate( MotorTask, NULL, 200, NULL, 1, NULL );
  vTaskStartScheduler();
}

void loop() { 
  
}
profile
Robotics, 3D-Vision, Deep-Learning에 관심이 있습니다

0개의 댓글