Hi,
Lâu rồi không tham gia diễn đàn, nay trở lại góp vui với bà con MSP430 chương trình nháp điều khiển từ xa dùng Remote của TV, DVD, vv... của Sony, Samsung, GL, Panasonic, vv... trừ mấy loại của Trung Quốc. Đây là chương trình nháp, muốn ứng dụng thì phải code bổ sung vào, mục đích là để cho các bác vọc MSP430 với KIT MSP430 LaunchPad. Trong ví dụ này, các bác học cách sử dụng ADC (single conversion), sử dụng Timer, các ngắt và chế độ tiết kiệm năng lượng.
Mô tả sơ hoạt động của chương trình như sau (nhìn ảnh dưới nhé):
Để điều khiển được trước tiên phải dạy cho mạch các lệnh điều khiển. Vì bộ nhớ có hạn nên mạch chỉ thực hiện được 6 lệnh điều khiển. Việc dạy lệnh điều khiển cho mạch được thực hiện bằng cách vặn biến trở về bên trái đến khi màn hình hiển thị chữ SET. Tiếp theo vặn biến trở bên phải để chọn lệnh (từ 0 đến 5). Và bấm nút trên remote 4 lần để mạch ghi nhớ lệnh. Khi lệnh được ghi nhớ thì đèn báo sẽ sáng lên giúp người dùng nhận biết và chuyển qua dạy cho lệnh mới. Vặn biến trở bên phải qua mã lệnh mới và thực hiện dạy lệnh mới cho mạch. Sau khi dạy xong lệnh cho mạch thì vặn biến trở bên phải qua chế độ hoạt động, màn hình sẽ hiển thị chữ RUN. Lúc này bạn bấm lệnh nào thì màn hình sẽ hiển thị lệnh đó và thực hiện lệnh đó (trong phạm vi ví dụ chỉ thực hiện việc Bật đèn). Nếu bấm sai lệnh thì sẽ thực hiện Tắt đèn.
Có thể mở rộng thêm các tính năng khác như hẹn giờ, đồng hồ vv...
Một vài ảnh:

KIT MSP430 LaunchPad

Mạch test

Chế độ dạy lệnh

Chế độ hoạt động

Các biến trở dùng để dạy và điều khiển

Và cuối cùng là code
Lâu rồi không tham gia diễn đàn, nay trở lại góp vui với bà con MSP430 chương trình nháp điều khiển từ xa dùng Remote của TV, DVD, vv... của Sony, Samsung, GL, Panasonic, vv... trừ mấy loại của Trung Quốc. Đây là chương trình nháp, muốn ứng dụng thì phải code bổ sung vào, mục đích là để cho các bác vọc MSP430 với KIT MSP430 LaunchPad. Trong ví dụ này, các bác học cách sử dụng ADC (single conversion), sử dụng Timer, các ngắt và chế độ tiết kiệm năng lượng.
Mô tả sơ hoạt động của chương trình như sau (nhìn ảnh dưới nhé):
Để điều khiển được trước tiên phải dạy cho mạch các lệnh điều khiển. Vì bộ nhớ có hạn nên mạch chỉ thực hiện được 6 lệnh điều khiển. Việc dạy lệnh điều khiển cho mạch được thực hiện bằng cách vặn biến trở về bên trái đến khi màn hình hiển thị chữ SET. Tiếp theo vặn biến trở bên phải để chọn lệnh (từ 0 đến 5). Và bấm nút trên remote 4 lần để mạch ghi nhớ lệnh. Khi lệnh được ghi nhớ thì đèn báo sẽ sáng lên giúp người dùng nhận biết và chuyển qua dạy cho lệnh mới. Vặn biến trở bên phải qua mã lệnh mới và thực hiện dạy lệnh mới cho mạch. Sau khi dạy xong lệnh cho mạch thì vặn biến trở bên phải qua chế độ hoạt động, màn hình sẽ hiển thị chữ RUN. Lúc này bạn bấm lệnh nào thì màn hình sẽ hiển thị lệnh đó và thực hiện lệnh đó (trong phạm vi ví dụ chỉ thực hiện việc Bật đèn). Nếu bấm sai lệnh thì sẽ thực hiện Tắt đèn.
Có thể mở rộng thêm các tính năng khác như hẹn giờ, đồng hồ vv...
Một vài ảnh:

KIT MSP430 LaunchPad

Mạch test

Chế độ dạy lệnh

Chế độ hoạt động

Các biến trở dùng để dạy và điều khiển

Và cuối cùng là code
Code:
#include <msp430g2231.h>
#define _S 11
#define _E 12
#define _T 13
#define _R 14
#define _U 15
#define _N 16
#define SETTING 0
#define RUNNING 1
const unsigned char LedCode[17] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0xFF, 0x92, 0x86, 0x87, 0xCE, 0xE3, 0xAB}; //codes of led7 from 0 to 9
const unsigned char LedID[4] = {0x01, 0x02, 0x04, 0x08}; // active led7
const unsigned int AdcChanel[2] = {INCH_1, INCH_2};
volatile unsigned char Number, Dot, Blink;
volatile unsigned char LedBuffer[4] = {0, 0, 0, 1}; // test data
volatile unsigned char Scene;
volatile unsigned char Buffer[24];
volatile unsigned long Command[6];
volatile unsigned long IrCode;
volatile unsigned char BitCounter, StartBit;
void update(void);
/*
* main.c
*/
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
P1SEL = BIT1 + BIT2;
P1DIR = 0xF1;
P1REN = 0x00;
ADC10CTL0 = ADC10SHT_0 + SREF_0 + ADC10ON; // + ADC10IE; // ADC10ON, interrupt enabled
ADC10CTL1 = ADC10SSEL_0 + AdcChanel[0]; // input A1
ADC10AE0 |= BIT1 + BIT2;
ADC10CTL0 |= ENC + ADC10SC;
TACTL |= TASSEL_1 + MC_1;// + TAIE; // Timer A source from ACLK, up mode, Timer A interrupt enable
CCR0 = 655; // period approximately 20ms
CCR1 = 131;
CCTL1 = CCIE;
P1IE = BIT3; // enable interrupt P1.3
P1IES = BIT3; // H2L trigger interrupt
StartBit = 1; // indicate start of frame
P1OUT &= ~BIT0;
_bis_SR_register(LPM3_bits + GIE);
}
// interrup port 1.3
#pragma vector = PORT1_VECTOR
void __interrupt port1_isr(void)
{
if(StartBit)
{
TAR = 0; // reset Timer A counter
CCTL0 = CCIE; // enable CC0 interrupt
BitCounter = 0; // reset BitCounter
StartBit = 0; // clear start of frame status
}
else if(BitCounter < 23)
{
Buffer[BitCounter] = TAR; // get Timer A counter
TAR = 0; // reset Timer A counter
BitCounter++;
}
P1IFG &= ~BIT3; // clear interrupt flag
}
// interrupt capture/compare 0
#pragma vector = TIMERA0_VECTOR
void __interrupt cc0_isr(void)
{
unsigned char min, max, average, i;
static unsigned char count;
static unsigned long temp;
CCTL0 &= ~CCIE; // disable CC0 interrupt
StartBit = 1; // set start of frame
if(BitCounter < 5) return; // ignore if interference
// decode
min = 0xFF;
max = 0x00;
for(i = 1; i < BitCounter; i++)
{
if(Buffer[i] < min) min = Buffer[i];
if(Buffer[i] > max) max = Buffer[i];
}
average = (max + min) / 2;
IrCode = 0;
for(i = 1; i < BitCounter; i++)
{
IrCode <<= 1;
if(Buffer[i] > average) IrCode++;
}
if(Scene == SETTING)
{
if(IrCode != temp)
{
temp = IrCode;
count = 0;
P1OUT &= ~BIT0;
}
else if(++count >= 3)
{
Command[LedBuffer[0]] = IrCode;
count = 0;
P1OUT |= BIT0;
}
}
else if(Scene == RUNNING)
{
for(i = 0; i < 6; i++)
{
if(IrCode == Command[i])
{
LedBuffer[0] = i;
P1OUT |= BIT0;
break;
}
else P1OUT &= ~BIT0;
}
}
}
#pragma vector = TIMERA1_VECTOR
void __interrupt taif_isr(void)
{
static volatile unsigned char i = 0, count, blink_counter, last_id = BIT0;
unsigned char k;
unsigned char temp;
if(TAIV & TAIV_TACCR1)
{
CCR1 += 100;
if(CCR1 > 655) CCR1 = 100;
// scan led7
if(i >= 3) // set led7 id
i = 0;
else
i++;
if(++blink_counter >= 50)
{
blink_counter = 0;
if(Blink < 4) last_id ^= BIT0;
else last_id = 0;
}
temp = LedID[i] << 4;
for(k = 0; k < 4; k++)
{
//if(last_id && ((k + Blink) == 3)) P1OUT |= BIT5;
if(last_id && ((Blink + k) == 3)) P1OUT |= BIT5;
else
{
if(temp & BIT7) P1OUT &= ~BIT5; // Apply Led ID data
else P1OUT |= BIT5;
}
P1OUT |= BIT6; // Clock Shift pulse
temp <<= 1;
P1OUT &= ~BIT6;
}
temp = LedCode[LedBuffer[i]];
//if(i == 2) temp &= ~BIT7; // display dot sign at led 2
for(k = 0; k < 8; k++)
{
if(temp & BIT7) P1OUT |= BIT5;
else P1OUT &= ~BIT5;
P1OUT |= BIT6;
temp <<= 1;
P1OUT &= ~BIT6;
}
P1OUT |= BIT7;
_NOP();
P1OUT &= ~BIT7;
if(++count >= 40)
{
count = 0;
update();
}
}
}
void update(void)
{
unsigned long adc_value;
unsigned char now;
static unsigned char channel, last = 0xFF;//, last_blink;
if(ADC10CTL0 & ADC10BUSY)
{
return;
}
adc_value = ADC10MEM;
ADC10CTL0 &= ~ENC; // Disable conversion
if(channel == 0)
{
Scene = (unsigned char)(adc_value * 2 / 1023);
if(Scene == SETTING)
{
LedBuffer[3] = _S;
LedBuffer[2] = _E;
LedBuffer[1] = _T;
Blink = 0;
}
else if(Scene == RUNNING)
{
LedBuffer[3] = _R;
LedBuffer[2] = _U;
LedBuffer[1] = _N;
Blink = 0xFF;
}
channel = 1;
}
else
{
//LedBuffer[0] = (unsigned char)(adc_value * 6 / 1023);
now = (unsigned char)(adc_value * 6 / 1023);
if(last ^ 0xFF) // Ignore the first value
{
if(now < last)
{
if((LedBuffer[0] + now) <= last) LedBuffer[0] = 0;
else LedBuffer[0] = LedBuffer[0] + now - last;
}
else if(now > last)
{
LedBuffer[0] = LedBuffer[0] + now - last;
if(LedBuffer[0] > 5) LedBuffer[0] = 5;
}
}
last = now;
channel = 0;
if(Scene == RUNNING)
{
// do command of LedBuffer[0] value
}
}
ADC10CTL1 = AdcChanel[channel];
ADC10CTL0 |= ENC + ADC10SC; // Enable, start conversion
}
, google >NEC Protocol> rất nhiều, tôi chọn đại cái này vì có vẻ dễ nhìn:
thì ra vậy, giống ý của Hoangdai. "Học" tức là nhớ một sample nhị phân lặp đi lặp lại trong khoảng thời gian lấy mẫu? Mấy con led7 seg dùng để hiển thị cái mã đó cho dễ xem ah?

Comment