[아두이노] 동기화(Semaphore), 키패드&모터 제어
아두이노를 사용해 태스크 간 동기화 실습을 진행해본다.
요구사항
- Keypad ISR과 KeyPad Task의 동기화
- Keypad Task에서는 Keypad ISR로부터의 버튼 전달을 기다림
- Keypad ISR에서 눌린 버튼을 전역변수를 통해 키패드에 전달
- 동기화를 위해 바이너리 세마포어 사용
- Keypad Task와 FND Task
- FND Task에서는 키패드 Task로부터의 버튼 전달을 기다림
- KeyPad Task는 눌린 버튼을 전역 변수를 통해 FND Task에 전달
- FND Task는 전달 받은 버튼을 FND에 왼쪽으로 shift하며 표시
실습 1
#include <FreeRTOS_AVR.h>
#define MS2TICKS(ms) (ms/portTICK_PERIOD_MS)
#define FND_SIZE 6
const int Keypad[4] = {2,3,21,20};
const int FndSelectPin[6] = {22,23,24,25,26,27};
const int FndPin[8] = {30,31,32,33,34,35,36,37};
const int FndFont[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x60, 0x7D, 0x07, 0x7F, 0x67};
SemaphoreHandle_t Sem;
int SendValue = 0;
int Fnd[FND_SIZE] = {0,};
//FND의 값을 왼쪽으로 shift
//파라미터로 들어온 새로운 값(data)을 0번지에 넣는 함수
void ShiftInsert(int data){
int i;
for(i=1; i<FND_SIZE; i++){
Fnd[FND_SIZE - i] = Fnd[FND_SIZE - i -1];
}
Fnd[0] = data;
}
void KeypadControl1(){
delay(50);
SendValue = 1;
xSemaphoreGive(Sem);
}
void KeypadControl2(){
delay(50);
SendValue = 2;
xSemaphoreGive(Sem);
}
void KeypadControl3(){
delay(50);
SendValue = 3;
xSemaphoreGive(Sem);
}
void KeypadControl4(){
delay(50);
SendValue = 4;
xSemaphoreGive(Sem);
}
void KeypadTask(void* arg){
int i;
int keypad;
while(1){
//세마포어를 통해 키패드가 눌렸음을 Keypad ISR로부터 전달 받음
//portMAX_DELAY: 세마포어를 받기 전까지 대기
//입력된 번호는 ShiftInsert를 통해 Fnd[]에 삽입
//세마포어를 받을 때까지 대기
if(xSemaphoreTake(Sem,portMAX_DELAY)){
keypad = SendValue;
ShiftInsert(keypad);
}
}
}
void FndSelect(int pos){
int i;
for(i=0; i<6; i++){
if(i==pos){
digitalWrite(FndSelectPin[i], LOW);
}
else{
digitalWrite(FndSelectPin[i], HIGH);
}
}
}
void FndDisplay(int pos, int num){
int i;
int flag = 0;
int shift = 0x01;
FndSelect(pos);
for(i=0; i<8; i++){
flag = (FndFont[num] & shift);
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(Keypad[i], INPUT);
}
attachInterrupt(0,KeypadControl1, RISING);
attachInterrupt(1,KeypadControl2, RISING);
attachInterrupt(2,KeypadControl3, RISING);
attachInterrupt(3, KeypadControl4, RISING);
vSemaphoreCreateBinary(Sem);
xTaskCreate(KeypadTask, NULL, 200, NULL, 2, NULL);
xTaskCreate(FndTask, NULL, 200, NULL, 1, NULL);
vTaskStartScheduler();
}
void loop(){
}
요구사항
- Keypad를 통해 Motor 제어
- 왼쪽 회전, 정지, 오른족 회전 3개 키에 의해 모터 동작
- Keypad ISR과 Motor Task간 전역변수와 세마포어를 이용해 데이터 전달
실습2
#include <FreeRTOS_AVR.h>
const int MT_P = 10;
const int MT_N = 9;
const int LeftKey = 2;
const int StopKey = 3;
const int RightKey = 21;
QueueHandle_t xQueue;
SemaphoreHandle_t Sem;
int SendValue = 0;
//SendValue로 값 전달, 세마포어로 동기화
void LeftKeyControl() {
SendValue = 1;
xSemaphoreGive(Sem);
}
void StopKeyControl() {
SendValue = 2;
xSemaphoreGive(Sem);
}
void RightKeyControl() {
SendValue = 3;
xSemaphoreGive(Sem);
}
void MotorTask(void * arg) {
while(1) {
//세마포어를 통해 키패드가 눌렸음을 KeypadISR로부터 전달 받았다면, 아래 구문 실행
if(xSemaphoreTake(Sem, portMAX_DELAY)){
// Rotate left
if(SendValue == 1) {
digitalWrite(MT_P, LOW);
digitalWrite(MT_N, HIGH);
}
// Stop
else if(SendValue == 2) {
digitalWrite(MT_P, LOW);
digitalWrite(MT_N, LOW);
}
// Rotate right
else if(SendValue == 3) {
digitalWrite(MT_P, HIGH);
digitalWrite(MT_N, LOW);
}
}
}
}
void setup() {
pinMode(MT_P, OUTPUT);
pinMode(MT_N, OUTPUT);
pinMode(LeftKey, INPUT);
pinMode(StopKey, INPUT);
pinMode(RightKey, INPUT);
attachInterrupt(0, LeftKeyControl, RISING);
attachInterrupt(1, StopKeyControl, RISING);
attachInterrupt(2, RightKeyControl, RISING);
vSemaphoreCreateBinary(Sem);
xTaskCreate(MotorTask,NULL,200,NULL,1,NULL);
vTaskStartScheduler();
}
void loop() {
}
요구사항
- Keypad를 통해 Motor 제어
- KeyPad ISR과 KeyPad Task간 전역변수와 semaphore를 이용해 데이터 전달
- KeyPad Task에서는 Circular Queue에 버튼을 입력
- 이때 debounce 해결
- Motor Task에서는 Circular Queue로부터 버튼을 입력받아 모터 제어
- KeyPad Task(producer)와 Motor Task(consumer)간critical section 보호 및 동기화를 위해 semaphore를 사용
- 운영체제 Producer & Consumer의 bounded buffer problem 참조
실습3
#include <FreeRTOS_AVR.h>
const int MT_P = 10;
const int MT_N = 9;
const int LeftKey = 2;
const int StopKey = 3;
const int RightKey = 21;
int Queue[8] = {0,};
int in = 0;
int out = 0;
int cur = 0; // 디바운싱을 위한 변수 선언
SemaphoreHandle_t Sem;
SemaphoreHandle_t Sem2;
int SendValue = 0;
//SendValue로 값 전달, 세마포어로 동기화
void LeftKeyControl() {
SendValue = 1;
xSemaphoreGive(Sem);
}
void StopKeyControl() {
SendValue = 2;
xSemaphoreGive(Sem);
}
void RightKeyControl() {
SendValue = 3;
xSemaphoreGive(Sem);
}
void KeypadTask(void* arg){
while(1){
vTaskDelay(50);
if(SendValue == cur){
continue;
}
if(xSemaphoreTake(Sem,portMAX_DELAY)){
//큐에 데이터를 넣음(Producer)
//Serial.print(SendValue);
Queue[in] = SendValue;
in = (in+1) % 8;
cur = SendValue;
xSemaphoreGive(Sem2);
Serial.print("SendValue");
Serial.println(SendValue);
Serial.print("cur");
Serial.print(cur);
}
}
}
void MotorTask(void * arg) {
while(1) {
vTaskDelay(10);
if(xSemaphoreTake(Sem2,portMAX_DELAY)){
// Rotate left
if(Queue[out] == 1) {
digitalWrite(MT_P, LOW);
digitalWrite(MT_N, HIGH);
}
// Stop
else if(Queue[out] == 2) {
digitalWrite(MT_P, LOW);
digitalWrite(MT_N, LOW);
}
// Rotate right
else if(Queue[out] == 3) {
digitalWrite(MT_P, HIGH);
digitalWrite(MT_N, LOW);
}
out = (out+1) % 8;
}
}
}
void setup() {
Serial.begin(9600);
pinMode(MT_P, OUTPUT);
pinMode(MT_N, OUTPUT);
pinMode(LeftKey, INPUT);
pinMode(StopKey, INPUT);
pinMode(RightKey, INPUT);
attachInterrupt(0, LeftKeyControl, RISING);
attachInterrupt(1, StopKeyControl, RISING);
attachInterrupt(2, RightKeyControl, RISING);
//vSemaphoreCreateBinary(Sem);
//vSemaphoreCreateBinary(Sem2);
Sem = xSemaphoreCreateCounting(8,0);
Sem2 = xSemaphoreCreateCounting(8,0);
if(Queue != NULL){
xTaskCreate(MotorTask,NULL,200,NULL,1,NULL);
xTaskCreate(KeypadTask,NULL,200,NULL,2,NULL);
vTaskStartScheduler();
}
}
void loop() {
}
Leave a comment