[Embedded Software] 경희대 임베디드 소프트웨어 과제7 - Inter-Task Communication in uC/OS-II
on Study
개발 환경
- 개발보드 : JKIT-128-1
- MUC : ATmega128
- OS : uC/OS-II
- Editor : WinAVR Progrmmers Notepad
- Compiler : WinAVR
uC/OS-II
C 언어 기반으로 작성된 마이크로프로세서를 위한 저비용 우선순위 기반 선점형 실시간 운영체제 커널 위키백과
uC/OS-II File Structure
Task state Diagram in uC/OS-II
실습 내용
온도 센서로부터 1초에 한번씩 데이터를 읽는 Temperature Task, Temperature Task로부터 온도를 전달받는 FndTask, 온도를 FND에 출력하는 FndDisplay Task를 두 가지 ITC 방법으로 구현
두 가지 ITC 방법 : Message Passing, Shared Memory
// lab8.c 코드
#include "includes.h"
#define F_CPU 16000000UL // CPU frequency = 16 Mhz
#include <avr/io.h>
#include <util/delay.h>
#define TASK_STK_SIZE OS_TASK_DEF_STK_SIZE
#define N_TASKS 3
#define MBOX 0
#define GVAR 1
#define METHOD MBOX
#define DEBUG 1
OS_STK TaskStk[N_TASKS][TASK_STK_SIZE];
// METHOD가 MBOX일 경우, Mbox를 통해 값을 교환하고
// MBOX가 아닐 경우, semaphore를 이용해 값을 교환한다.
#if METHOD == MBOX
OS_EVENT *Mbox;
#else
OS_EVENT *Sem;
volatile INT8U TemperatureValue;
#endif
OS_EVENT *Mutex;
volatile INT8U FndNum;
void TemperatureTask(void *data);
void FndTask(void *data);
void FndDisplayTask(void *data);
int main (void)
{
OSInit();
OS_ENTER_CRITICAL();
TCCR0=0x07;
TIMSK=_BV(TOIE0);
TCNT0=256-(CPU_CLOCK_HZ/OS_TICKS_PER_SEC/ 1024 );
OS_EXIT_CRITICAL();
OSTaskCreate(TemperatureTask, (void *)0, (void *)&TaskStk[0][TASK_STK_SIZE - 1], 1);
OSTaskCreate(FndTask, (void *)0, (void *)&TaskStk[1][TASK_STK_SIZE - 1], 2);
OSTaskCreate(FndDisplayTask, (void *)0, (void *)&TaskStk[2][TASK_STK_SIZE - 1], 3);
OSStart();
return 0;
}
void InitI2C()
{
PORTD = 3; // For Pull-up override value
SFIOR &= ~(1 << PUD); // PUD
TWSR = 0; // TWPS0 = 0, TWPS1 = 0
TWBR = 32; // for 100 K Hz bus clock
TWCR = _BV(TWEA) | _BV(TWEN); // TWEA = Ack pulse is generated
// TWEN = TWI 동작을 가능하게 한다
}
int ReadTemperature(void)
{
int value;
OS_ENTER_CRITICAL();
TWCR = _BV(TWSTA) | _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
TWDR = 0x98 + 1; //TEMP_I2C_ADDR + 1
TWCR = _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
while(!(TWCR & _BV(TWINT)));
//온도센서는 16bit 기준으로 값을 가져오므로
//8비트씩 2번을 받아야 한다.
value = TWDR << 8;
TWCR = _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
value |= TWDR;
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
value >>= 8;
TIMSK = (value >= 33) ? TIMSK | _BV(TOIE2): TIMSK & ~_BV(TOIE2);
OS_EXIT_CRITICAL();
return value;
}
void TemperatureTask (void *data)
{
int value;
INT8U err;
data = data;
#if METHOD == MBOX
Mbox = OSMboxCreate(0);
#else
Sem = OSSemCreate(0);
#endif
Mutex = OSMutexCreate(0, &err);
InitI2C();
DDRA = 0xFF;
while (1) {
value = ReadTemperature();
#if METHOD == MBOX
OSMboxPost(Mbox, &value);
#else
OSMutexPend(Mutex, 0, &err);
TemperatureValue = value;
OSMutexPost(Mutex);
OSSemPost(Sem);
#endif
OSTimeDlyHMSM(0, 0, 1, 0);
}
}
void FndTask (void *data)
{
INT8U err;
data = data;
while (1) {
#if METHOD == MBOX
FndNum = *(int *)OSMboxPend(Mbox, 0, &err);
#else
OSSemPend(Sem, 0, &err);
OSMutexPend(Mutex, 0, &err);
FndNum = TemperatureValue;
OSMutexPost(Mutex);
#endif
#ifdef DEBUG
PORTA = FndNum;
#endif
}
}
void FndDisplayTask (void *data)
{
unsigned char FND_DATA[ ]= {
0x3f, // 0
0x06, // 1
0x5b, // 2
0x4f, // 3
0x66, // 4
0x6d, // 5
0x7d, // 6
0x27, // 7
0x7f, // 8
0x6f, // 9
0x77, // A
0x7c, // B
0x39, // C
0x5e, // D
0x79, // E
0x71, // F
0x80, // .
0x40, // -
0x08 // _
};
unsigned int num0, num1, num2, num3;
INT8U err;
data = data;
DDRC = 0xff;
DDRG = 0x0f;
while(1) {
OSMutexPend(Mutex, 0, &err);
num3 = (FndNum / 1000) % 10;
num2 = (FndNum / 100) % 10;
num1 = (FndNum / 10) % 10;
num0 = FndNum % 10;
OSMutexPost(Mutex);
PORTC = FND_DATA[num3];
PORTG = 0x08;
_delay_ms(2);
PORTC = FND_DATA[num2];
PORTG = 0x04;
_delay_ms(3);
PORTC = FND_DATA[num1];
PORTG = 0x02;
_delay_ms(2);
PORTC = FND_DATA[num0];
PORTG = 0x01;
_delay_ms(3);
}
}
실습 응용 1
온도가 특정 값 이상이 되면 buzzer를 통해 싸이렌을 울림 추가
// lab8.c
#include "includes.h"
#define F_CPU 16000000UL // CPU frequency = 16 Mhz
#include <avr/io.h>
#include <util/delay.h>
#define TASK_STK_SIZE OS_TASK_DEF_STK_SIZE
#define N_TASKS 3
#define MBOX 0
#define GVAR 1
#define METHOD MBOX
#define DEBUG 1
OS_STK TaskStk[N_TASKS][TASK_STK_SIZE];
#if METHOD == MBOX
OS_EVENT *Mbox;
#else
OS_EVENT *Sem;
volatile INT8U TemperatureValue;
#endif
OS_EVENT *Mutex;
volatile INT8U FndNum;
void TemperatureTask(void *data);
void FndTask(void *data);
void FndDisplayTask(void *data);
int main (void)
{
OSInit();
OS_ENTER_CRITICAL();
TCCR0=0x07;
TIMSK=_BV(TOIE0);
TCNT0=256-(CPU_CLOCK_HZ/OS_TICKS_PER_SEC/ 1024 );
OS_EXIT_CRITICAL();
OSTaskCreate(TemperatureTask, (void *)0, (void *)&TaskStk[0][TASK_STK_SIZE - 1], 1);
OSTaskCreate(FndTask, (void *)0, (void *)&TaskStk[1][TASK_STK_SIZE - 1], 2);
OSTaskCreate(FndDisplayTask, (void *)0, (void *)&TaskStk[2][TASK_STK_SIZE - 1], 3);
OSStart();
return 0;
}
void InitI2C()
{
PORTD = 3; // For Pull-up override value
SFIOR &= ~(1 << PUD); // PUD
TWSR = 0; // TWPS0 = 0, TWPS1 = 0
TWBR = 32; // for 100 K Hz bus clock
TWCR = _BV(TWEA) | _BV(TWEN); // TWEA = Ack pulse is generated
// TWEN = TWI 동작을 가능하게 한다
}
int ReadTemperature(void)
{
int value;
OS_ENTER_CRITICAL();
TWCR = _BV(TWSTA) | _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
TWDR = 0x98 + 1; //TEMP_I2C_ADDR + 1
TWCR = _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
while(!(TWCR & _BV(TWINT)));
//온도센서는 16bit 기준으로 값을 가져오므로
//8비트씩 2번을 받아야 한다.
value = TWDR << 8;
TWCR = _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
value |= TWDR;
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
value >>= 8;
TIMSK = (value >= 33) ? TIMSK | _BV(TOIE2): TIMSK & ~_BV(TOIE2);
OS_EXIT_CRITICAL();
return value;
}
void TemperatureTask (void *data)
{
int value;
INT8U err;
data = data;
#if METHOD == MBOX
Mbox = OSMboxCreate(0);
#else
Sem = OSSemCreate(0);
#endif
Mutex = OSMutexCreate(0, &err);
InitI2C();
DDRA = 0xFF;
while (1) {
value = ReadTemperature();
#if METHOD == MBOX
OSMboxPost(Mbox, &value);
#else
OSMutexPend(Mutex, 0, &err);
TemperatureValue = value;
OSMutexPost(Mutex);
OSSemPost(Sem);
#endif
// Buzzer는 포트 B에 연결되어 있다.
DDRB = 0x10;
int i;
// 온도센서를 통해 측정한 값이 29도 이상이면
// PORTB에 연결된 Buzzer를 On하여 싸이렌이 울린다.
if(value >= 29){
for(i = 0; i < 10; i++)
{
PORTB = 0x10;
_delay_ms(1);
PORTB = 0x00;
_delay_ms(1);
}
}
OSTimeDlyHMSM(0, 0, 1, 0);
}
}
void FndTask (void *data)
{
INT8U err;
data = data;
while (1) {
#if METHOD == MBOX
FndNum = *(int *)OSMboxPend(Mbox, 0, &err);
#else
OSSemPend(Sem, 0, &err);
OSMutexPend(Mutex, 0, &err);
FndNum = TemperatureValue;
OSMutexPost(Mutex);
#endif
#ifdef DEBUG
PORTA = FndNum;
#endif
}
}
void FndDisplayTask (void *data)
{
unsigned char FND_DATA[ ]= {
0x3f, // 0
0x06, // 1
0x5b, // 2
0x4f, // 3
0x66, // 4
0x6d, // 5
0x7d, // 6
0x27, // 7
0x7f, // 8
0x6f, // 9
0x77, // A
0x7c, // B
0x39, // C
0x5e, // D
0x79, // E
0x71, // F
0x80, // .
0x40, // -
0x08 // _
};
unsigned int num0, num1, num2, num3;
INT8U err;
data = data;
DDRC = 0xff;
DDRG = 0x0f;
while(1) {
OSMutexPend(Mutex, 0, &err);
num3 = (FndNum / 1000) % 10;
num2 = (FndNum / 100) % 10;
num1 = (FndNum / 10) % 10;
num0 = FndNum % 10;
OSMutexPost(Mutex);
PORTC = FND_DATA[num3];
PORTG = 0x08;
_delay_ms(2);
PORTC = FND_DATA[num2];
PORTG = 0x04;
_delay_ms(3);
PORTC = FND_DATA[num1];
PORTG = 0x02;
_delay_ms(2);
PORTC = FND_DATA[num0];
PORTG = 0x01;
_delay_ms(3);
}
}
실습 응용 2
실습 응용 1을 OS 없이 구현
// lab8.c
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
volatile int FndNum;
volatile int TemperatureValue;
void InitI2C()
{
PORTD = 3;
SFIOR &= ~(1<<PUD);
TWSR = 0;
TWBR = 32;
TWCR = _BV(TWEA) | _BV(TWEN);
}
int ReadTemperature(void)
{
int value;
TWCR = _BV(TWSTA) | _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
TWDR = 0x98 + 1;
TWCR = _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
while(!(TWCR & _BV(TWINT)));
value = TWDR << 8;
TWCR = _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
value |= TWDR;
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
value >>= 8;
TIMSK = (value >= 33) ? TIMSK | _BV(TOIE2): TIMSK & ~_BV(TOIE2);
return value;
}
void TemperatureTask()
{
int value;
InitI2C();
DDRA = 0xFF;
value = ReadTemperature();
TemperatureValue = value;
DDRB = 0x10;
int i;
if(value >= 29) {
for(i = 0; i<10; i++){
PORTB = 0x10;
_delay_ms(1);
PORTB = 0x00;
_delay_ms(1);
}
}
}
void FndTask()
{
FndNum = TemperatureValue;
}
// 운영체제는 멀티태스크 환경에서 Waiting 상태의 태스크를 Ready 상태로
// Context Switching하며 스케줄링을 하고 태스크 간
// Critical Section의 동기화를 한다.
// 이러한 운영체제가 없기 때문에 main()에서 태스크의 순서를 맞춰야 하며,
// TemperatureTask -> FndTask + FndDisplayTask 순으로
// 호출하도록 하였다.
int main() {
int num0, num1, num2, num3;
static unsigned char NUM[ ]= {
0x3f, // 0
0x06, // 1
0x5b, // 2
0x4f, // 3
0x66, // 4
0x6d, // 5
0x7d, // 6
0x27, // 7
0x7f, // 8
0x6f, // 9
0x77, // A
0x7c, // B
0x39, // C
0x5e, // D
0x79, // E
0x71, // F
0x80, // .
0x40, // -
0x08 // _
};
DDRC = 0xff;
DDRG = 0x0f;
DDRA = 0xff;
PORTA = 0x00;
while(1) {
TemperatureTask();
FndTask();
num3 = (FndNum / 1000) % 10;
num2 = (FndNum / 100) % 10;
num1 = (FndNum / 10) % 10;
num0 = FndNum % 10;
PORTC = NUM[num3];
PORTG = 0x08;
_delay_ms(2);
PORTC = NUM[num2];
PORTG = 0x04;
_delay_ms(3);
PORTC = NUM[num1];
PORTG = 0x02;
_delay_ms(2);
PORTC = NUM[num0];
PORTG = 0x01;
_delay_ms(3);
}
}
실습 응용 3
조도 센서로부터 1초에 한번씩 데이터를 읽는 LighTask 추가, FndDisplayTask는 Temperature sensor 값과 Light sensor 값을 1초마다 번갈아 출력하도록 추가
// lab8.c
#include "includes.h"
#define F_CPU 16000000UL // CPU frequency = 16 Mhz
#include <avr/io.h>
#include <util/delay.h>
#define TASK_STK_SIZE OS_TASK_DEF_STK_SIZE
#define N_TASKS 4
#define MBOX 0
#define GVAR 1
#define METHOD MBOX
#define DEBUG 1
OS_STK TaskStk[N_TASKS][TASK_STK_SIZE];
// TemperatureTask와 LightTask에서 사용할
// MobxTemper, MoxLight, SemTemper, SemLight 생성
#if METHOD == MBOX
OS_EVENT *MboxTemper;
OS_EVENT *MboxLight;
#else
OS_EVENT *SemTemper;
OS-EVENT *SemLight;
volatile INT8U TemperatureValue;
volatile INT8U LightValue;
#endif
OS_EVENT *Mutex;
volatile INT8U FndNum;
void TemperatureTask(void *data);
void FndTask(void *data);
void FndDisplayTask(void *data);
void LightTask(void *data);
int main (void)
{
OSInit();
OS_ENTER_CRITICAL();
TCCR0=0x07;
TIMSK=_BV(TOIE0);
TCNT0=256-(CPU_CLOCK_HZ/OS_TICKS_PER_SEC/ 1024 );
OS_EXIT_CRITICAL();
OSTaskCreate(TemperatureTask, (void *)0, (void *)&TaskStk[0][TASK_STK_SIZE - 1], 1);
OSTaskCreate(LightTask, (void *)0, (void *)&TaskStk[1][TASK_STK_SIZE - 1], 2);
OSTaskCreate(FndTask, (void *)0, (void *)&TaskStk[2][TASK_STK_SIZE - 1], 3);
OSTaskCreate(FndDisplayTask, (void *)0, (void *)&TaskStk[3][TASK_STK_SIZE - 1], 4);
OSStart();
return 0;
}
void InitI2C()
{
PORTD = 3; // For Pull-up override value
SFIOR &= ~(1 << PUD); // PUD
TWSR = 0; // TWPS0 = 0, TWPS1 = 0
TWBR = 32; // for 100 K Hz bus clock
TWCR = _BV(TWEA) | _BV(TWEN); // TWEA = Ack pulse is generated
// TWEN = TWI 동작을 가능하게 한다
}
int ReadTemperature(void)
{
int value;
OS_ENTER_CRITICAL();
TWCR = _BV(TWSTA) | _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
TWDR = 0x98 + 1; //TEMP_I2C_ADDR + 1
TWCR = _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
while(!(TWCR & _BV(TWINT)));
//온도센서는 16bit 기준으로 값을 가져오므로
//8비트씩 2번을 받아야 한다.
value = TWDR << 8;
TWCR = _BV(TWINT) | _BV(TWEN);
while(!(TWCR & _BV(TWINT)));
value |= TWDR;
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
value >>= 8;
TIMSK = (value >= 33) ? TIMSK | _BV(TOIE2): TIMSK & ~_BV(TOIE2);
OS_EXIT_CRITICAL();
return value;
}
void TemperatureTask (void *data)
{
int value;
INT8U err;
data = data;
#if METHOD == MBOX
MboxTemper = OSMboxCreate(0);
#else
SemTemper = OSSemCreate(0);
#endif
Mutex = OSMutexCreate(0, &err);
InitI2C();
DDRA = 0xFF;
while (1) {
value = ReadTemperature();
#if METHOD == MBOX
OSMboxPost(MboxTemper, &value);
#else
OSMutexPend(Mutex, 0, &err);
TemperatureValue = value;
OSMutexPost(Mutex);
OSSemPost(SemTemper);
#endif
DDRB = 0x10;
int i;
if(value >= 34){
for(i = 0; i < 10; i++)
{
PORTB = 0x10;
_delay_ms(1);
PORTB = 0x00;
_delay_ms(1);
}
}
OSTimeDlyHMSM(0, 0, 1, 0);
}
}
void FndTask (void *data)
{
INT8U err;
data = data;
// TemperatureTask의 값을 받은 후,
// OSTimeDlyHMSM()을 통해 1초 딜레이를 발생하고
// LightTask의 값을 받도록 수행
while (1) {
#if METHOD == MBOX
FndNum = *(int *)OSMboxPend(MboxTemper, 0, &err);
#else
OSSemPend(SemTemper, 0, &err);
OSMutexPend(Mutex, 0, &err);
FndNum = TemperatureValue;
OSMutexPost(Mutex);
#endif
OSTimeDlyHMSM(0, 0, 1, 0);
#if METHOD == MBOX
FndNum = *(int *)OSMboxPend(MboxLight, 0, &err);
#else
OSSemPend(SemLight, 0, &err);
OSMutexPend(Mutex, 0, &err);
FndNum = LightValue;
OSMutexPost(Mutex);
#endif
OSTimeDlyHMSM(0, 0, 1, 0);
#ifdef DEBUG
PORTA = FndNum;
#endif
}
}
void FndDisplayTask (void *data)
{
unsigned char FND_DATA[ ]= {
0x3f, // 0
0x06, // 1
0x5b, // 2
0x4f, // 3
0x66, // 4
0x6d, // 5
0x7d, // 6
0x27, // 7
0x7f, // 8
0x6f, // 9
0x77, // A
0x7c, // B
0x39, // C
0x5e, // D
0x79, // E
0x71, // F
0x80, // .
0x40, // -
0x08 // _
};
unsigned int num0, num1, num2, num3;
INT8U err;
data = data;
DDRC = 0xff;
DDRG = 0x0f;
while(1) {
OSMutexPend(Mutex, 0, &err);
num3 = (FndNum / 1000) % 10;
num2 = (FndNum / 100) % 10;
num1 = (FndNum / 10) % 10;
num0 = FndNum % 10;
OSMutexPost(Mutex);
PORTC = FND_DATA[num3];
PORTG = 0x08;
_delay_ms(2);
PORTC = FND_DATA[num2];
PORTG = 0x04;
_delay_ms(3);
PORTC = FND_DATA[num1];
PORTG = 0x02;
_delay_ms(2);
PORTC = FND_DATA[num0];
PORTG = 0x01;
_delay_ms(3);
}
}
void init_adc()
{
DDRF = 0x01;
ADMUX = 0x00;
ADCSRA = 0x87;
}
unsigned short read_adc()
{
unsigned char adc_low, adc_high;
unsigned short value;
ADCSRA |= 0x40;
while((ADCSRA & 0x10) != 0x10)
;
adc_low = ADCL;
adc_high = ADCH;
value = ((unsigned short)adc_high << 8) | (unsigned short)adc_low;
return value;
}
void LightTask(void *data)
{
data = data;
unsigned short value;
init_adc();
#if METHOD == MBOX
MboxLight = OSMboxCreate(0);
#else
SemLight = OSSemCreate(0);
#endif
while(1)
{
value = read_adc();
FndNum = value;
#if METHOD == MBOX
OSMboxPost(MboxLight, &value);
#else
OSMutexPend(Mutex, 0, &err);
PrintValue = value;
OSMutexPost(Mutex);
OSSemPost(SemLight);
#endif
OSTimeDlyHMSM(0, 0, 1, 0);
}
}