網頁

2011年7月3日 星期日

無線模組 (RF 315MHz) 發射接收,使用8051與ATtiny2313

相信大多數的人都有玩過遙控類的玩具,甚至在日常生活中也常常用到,例如家裡的電動門、汽車中控鎖......等,都使用到這種 RF (Radio Frequency) 通訊的技術。

本文中所要探討的是 RF 315MHz 長距離發射/接收模組 (TG-11A 與 TG-11B) 的控制方法,不使用額外的編碼與解碼晶片,而是使用 MCU (Micro-Controller Unit) 微控制器來負責發射與接收:發射端送出自訂字元,接收端接收自訂字元產生相對應的動作 (這裡是控制 LED 明滅)。


網路上關於照片中的模組所找到的資料都是基於八支腳位的,不過由於大部份接腳都是共用的,所以簡化之後的接腳只有 Vcc、GND和 DATA IN/OUT用的到。

文中所使用的模組都是 4-pin,有些接腳在使用時必須接在一起 (照片中可清楚看出)。

參考網站:
WinAVR AVR-GCC Tutorial - Running TX433 and RX433 RF modules with AVR microcontrollers


硬體線路:
發射線路:從左而右的四支接腳分別接 Vcc (pin-1, pin-2)、Data-In (pin-3)、GND (pin-4),其中比較重要的是 Data-In 接腳,這支接腳必須接到 MCU 的 TxD 接腳;另外 Vcc 接腳在本文中是接 +5V,我讓它跟 MCU 共用電源(你可以試試接 +3 ~ 12V)。

接收線路:從左而右的四支接腳分別接 Vcc (pin-1)、Data-Out (pin-2,pin-3)、GND (pin-4),其中比較重要的是 Data-Out 接腳,這支接腳必須接到 MCU 的 RxD 接腳;另外 Vcc 接腳在本文中是接 +5V,我讓它跟 MCU 共用電源,但是實際測試過可以用兩顆電池(+3V)來驅動,+3V 的電源剛好可以用在 ATtiny2313,這在下面測試時會講到。


電源是用 +9V 乾電池經穩壓晶片 7805 降壓到 +5V,輸出電流最大為 1A,剛好可以給本文中發射與接收的電路作測試用;一開始我是這樣想,以 ATtiny2313 做發射 AT89S51 做接收都正常運作,直到 AT89S51 做發射 ATtiny2313 做接收時,LED 閃爍的反應變的超慢,也不能說不正常,畢竟也正常運作,那時還沒想到這樣反過來做測試會吃這麼多電,因為電壓不足造成 MCU 一直 reset 重新啟動,而造成這種情形。

在使用上,發射與接收線路通常不會共用電源而是分開使用的,我是為了方便測試所以才接在一起,最後是在接收程式中加入 "hello word" 的開機 LED 閃爍,才得知這個問題,然後另外使用一個+3V的乾電池電源給 ATtiny2313 用,希望你不要發生像我一樣的情形。

下面是使用 ATtiny2313 與 AT89S51 時,發射與接收模組的接法。AT89S51 的震盪器(12 MHz)與 Reset 電路並沒有畫出你必須自行接上;ATtiny2313 直接接上電源即可,熔絲位請選擇 "Internal RC Osc. 8MHz; Start-up time: 14 CK + 65 ms",CLK/8 不使用。


軟體撰寫:
"參考網站" 中使用 ATmega8 作為 RF 發射與接收的韌體開發晶片。為了無線傳輸時,發射與接收訊號能夠同步化,使用晶片中通用同步與非同步串列接收發射器 ( USART,  Universal Synchronous and Asynchronous serial Receiver and Transmitter) 功能;為了避免無端干擾,設置了編碼與解碼封包的自定控制字元,原始程式的說明請上 "參考網站"。在本文中只針對修改及增加的部份多做說明。

基於 "參考網站" 中的資料,本文中所要實做的部分為:
ATtiny2313 (C語言) 發射 <<<<>>>> AT89S51 (C語言) 接收
ATtiny2313 (C語言) 發射 <<<<>>>> AT89S51(組合語言) 接收

AT89S51 (C語言) 接收     <<<<>>>> ATtiny2313 (C語言) 接收
AT89S51(組合語言) 接收 <<<<>>>> ATtiny2313 (C語言) 接收

因為比較熟 Keil C/assembly 和 AVR GCC,所以這六個程式除了AVR GCC 的程式之外,Keil C/Assembly 就依 "參考網站" 中的程式來重寫以達到同樣的功能,順便複習 Keil 組合語言。

ATtiny2313 RF發射程式需要修改的部份如下,主要是修改暫存器裡的設定
void USART_Init(void)
{
//Set baud rate
UBRRL = (uint8_t)UBRRVAL;  //low byte
UBRRH = (UBRRVAL >> 8); //high byte
//Enable Transmitter and Receiver and Interrupt on receive complete
UCSRB = (1 << TXEN);
//Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size
UCSRC = (0 << UMSEL) | (0 << UPM1) | (0 << UPM0) |
 (0 << USBS) | (0 << UCSZ2) | (1 << UCSZ1) | (1 << UCSZ0); 
ATtiny2313 RF發射程式整理在此,按此下載

ATtiny2313 RF接收程式修改的部份如下,除了暫存器修改之外,在 "Main_Init()" 裡加入了 LED 閃爍的效果,當 MCU 插上電源 LED 會明滅閃爍 5 次,以及修改中斷副程式 LED 所連接的位置,LED 是低態動作。
void USART_Init(void)
{
//Set baud rate
UBRRL = (uint8_t)UBRRVAL;  //low byte
UBRRH = (UBRRVAL >> 8); //high byte
//Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size
UCSRC = (0 << UMSEL) | (0 << UPM1) | (0 << UPM0) |
 (0 << USBS) | (0 << UCSZ2) | (1 << UCSZ1) | (1 << UCSZ0); 
//Enable Transmitter and Receiver and Interrupt on receive complete
UCSRB = (1 << RXEN) | (1 << RXCIE);//|(1<<TXEN);
}

ISR(USART_RX_vect)
{
//define variables
uint8_t raddress, data, chk;//transmitter address
//receive destination address
raddress = USART_vReceiveByte();
//receive data
data = USART_vReceiveByte();
//receive checksum
chk = USART_vReceiveByte();
//compare received checksum with calculated
if(chk == (raddress+data))//if match perform operations
{
  //if transmitter address match  
  if(raddress == RADDR)
  {
 if(data == LEDON)
 {
 PORTD &= ~(1<<PD6);//LED ON
 }
 else if(data == LEDOFF)
 {
 PORTD |= (1<<PD6);//LED OFF
 }
 else
 {
 //blink led as error
 PORTD |= (1 << PD6);//LED OFF
 _delay_ms(10);
 PORTD &= ~(1 << PD6);//LED ON 
 }
  }
}
}

void Main_Init(void)
{ 
 uint8_t i = 0;

 DDRD = 0x40;//define port C pin 0 as output;
 // hello word
 for(;i < 5;i++)
  {
  PORTD &= ~(1 << PD6); // LED ON
  _delay_ms(150);
  PORTD |= (1 << PD6); //LED OFF
  _delay_ms(150);  
  }
 //enable global interrupts
 sei();
}
ATtiny2313 RF接收程式整理在此,按此下載

AT89S51(Keil C) RF發射程式,結構上與原始程式一樣,主要增加 "delay_100ms()" 副程式做延遲的工作,因為 Keil 沒內建延遲程式可以呼叫,所以只好自己建一個;"ioinit()" 包含 UART 的設定與設定使用 Timer1 作為 baud rate 產生器,不需設定全局中斷(EA)。
#include <REGX51.H>
 
typedef unsigned char tByte;

//define receive parameters
#define SYNC 0XAA// synchro signal
#define RADDR 0x44
#define LEDON 0x11//switch led on command
#define LEDOFF 0x22//switch led off command

// delay 100ms
void delay_100ms()
 {
  #pragma asm
     MOV  R6, #250
    delay_1: MOV  R7, #200
    DJNZ R7, $
    DJNZ R6, delay_1
  #pragma endasm
 }

void ioinit(void)
 {
 // mode 1, 8-bit UART
 PCON |= 0x00; // SMOD = 0, double baud rate speed disable 
 // SCON = SM0 SM1 SM2 REN TB8 RB8 TI RI = 01000000;
 // SM0 = 0, SM1 = 1 meas UART mode 1
 SCON = 0x40;
 // baun rate, Timer1, mode 2 auto reload, 1200bps
 TMOD = 0x20;
 TL1 = 0xe6;
 TH1 = 0xe6;
 TR1 = 1; // start timer 1  
 }

void send_byte(tByte const b)
 {
  SBUF = b;
  while(!TI);
  TI = 0;   
 }

void send_packs(tByte const radd, tByte const rcmd)
 {
  send_byte(SYNC);
  send_byte(radd);
  send_byte(rcmd);
  send_byte((radd+rcmd)); // check sum
 }

void main(void)
 {
 ioinit();
 for(;;)
  {
  send_packs(RADDR, LEDON);
  delay_100ms();
  send_packs(RADDR, LEDOFF);
  delay_100ms();
  }
 }

相對應的 Keil Assembly 的 RF 發射程式,請對照上一個程式。
;------------------------------------------------------------------------------
;   VARIABLES
;------------------------------------------------------------------------------
  ; CONST DEFINES
  SYNC EQU  0xAA
  RADDR EQU  0x44
  LEDON EQU  0x11
  LEDOFF EQU  0x22
  ; VARIABLE DEFINES
  CMD  DATA 0x30  
;------------------------------------------------------------------------------
;   INTERRUPT VECTOR DEFINITIONS
;------------------------------------------------------------------------------
  ORG  0000H   ; STARTUP MEMORY ADDRESS
  JMP  RESET
;------------------------------------------------------------------------------
;   MAIN PROGRAM
;------------------------------------------------------------------------------
RESET: MOV  SP, #5FH
IOINIT: ORL  PCON, #00H ; SMOD = 0, DOUBLE BAUD RATE SPEED DISABLE
  MOV  SCON, #01000000B ; UART MODE 1, 8-BIT
  MOV  TMOD, #20H ; TIMER1, MODE 2(8-BIT AUTOLOAD), 1200bps
  MOV  TL1, #0E6H
  MOV  TH1, #0E6H
  SETB TR1   ; START TIMER 1
MAIN: ; send led on command
  MOV  CMD, #LEDON
  LCALL SEND_PACKS  
  ; delay 100 ms
  LCALL  DELAY_100MS
  ; send led off command
  MOV  CMD, #LEDOFF
  LCALL SEND_PACKS
  ; delay 100 ms
  LCALL DELAY_100MS
  JMP  MAIN
;------------------------------------------------------------------------------
;   SUBROUTINE
;------------------------------------------------------------------------------
;    DELAY 100 ms
;------------------------------------------------
DELAY_100MS:
   MOV  R6, #250
delay_1: MOV  R7, #200
   DJNZ R7, $
   DJNZ R6, delay_1
   RET
;------------------------------------------------
;  SEND PACKS FROM UART
;  1. synchronous signal
;  2. wireless device ID
;  3. command
;  4. check sum
;------------------------------------------------
SEND_PACKS:
  MOV  SBUF, #SYNC  ; synchronous signal
  JNB  TI, $
  CLR  TI
  MOV  SBUF, #RADDR  ; wireless devive ID
  JNB  TI, $
  CLR  TI
  MOV  SBUF, CMD  ; command
  JNB  TI, $
  CLR  TI
  MOV  A, CMD
  ADD  A, #RADDR
  MOV  SBUF, A   ; check sum
  JNB  TI, $
  CLR  TI
  RET
;------------------------------------------------------------------------------
  END

AT89S51(Keil C) RF接收程式
#include <REGX51.H>

#define INTERRUPT_UART 4
#define ON 0
#define OFF 1

// ASSIGN LED CONTROL BIT P0.0, ACTIVE LOW
sbit LED = P0^0;

// delay 10ms
void delay_10ms()
 {
 #pragma asm
    MOV R6, #25
 DEL: MOV  R7, #200
   DJNZ  R7, $
   DJNZ R6, DEL 
 #pragma endasm
 }

typedef unsigned char tByte;

//define receive parameters
#define SYNC 0XAA// synchro signal
#define RADDR 0x44
#define LEDON 0x11//switch led on command
#define LEDOFF 0x22//switch led off command

void ioinit(void) // wait modify
{
 // turn off LED
 LED = OFF;

 // mode 1, 8-bit UART
 PCON |= 0x00; // SMOD = 0, double baud rate speed disable 
 // SCON = SM0 SM1 SM2 REN TB8 RB8 TI RI = 01010000;
 // SM0 = 0, SM1 = 1 meas UART mode 1
 SCON = 0x50;
 // baun rate, Timer1, mode 2 auto reload, 1200bps
 TMOD = 0x20;
 TL1 = 0xe6;
 TH1 = 0xe6;  
 TR1 = 1; // start timer 1  
 // interrupt
 ES = 1; // enable serial port interrupt
 EA = 1; // enable global interrupt
}

tByte USART_vReceiveByte(void)
{
    // Wait until a byte has been received
    while(!RI);
 RI = 0;
    // Return received data
    return SBUF;
}

void UART_ISR() interrupt INTERRUPT_UART
{
 // a-lu remark: 2011.02.21
 //  No implemented SYNC check before receive the byte of RADDR
 //  Let's to wait and see the test result

 //define variables
 tByte raddress, cmd, chk;//transmitter address

 // find TX or RX caused interrupt
 if(TI) 
  { 
  // clear TI
  TI = 0;
  }
 else
  { 
  //receive destination address
  raddress = USART_vReceiveByte();
  //receive cmd
  cmd = USART_vReceiveByte();
  //receive checksum
  chk = USART_vReceiveByte();
  //compare received checksum with calculated
  if(chk == (raddress+cmd))//if match perform operations
   {
   //if transmitter address match
   if(raddress == RADDR)
    {
     if(cmd == LEDON)
      {
      LED = ON; //LED ON
      }
     else if(cmd == LEDOFF)
      {
      LED = OFF; //LED OFF
      }
     else
      {
      //blink led as error
      LED = OFF; //LED OFF
      delay_10ms();
      LED = ON; //LED ON 
      }
     }
    }
   }
}

int main(void)
{
 ioinit();
 for(;;)
 {
 PCON |= 0x01; // IDLE MODE
 }
 //nothing here interrupts are working
 return 0;
}

相對應的 Keil Assembly 的 RF 發射程式,請對照上一個程式。
;------------------------------------------------------------------------------
;   VARIABLES
;------------------------------------------------------------------------------
  ; CONST DEFINES
  SYNC EQU  0xAA
  RADDR EQU  0x44
  LEDON EQU  0x11
  LEDOFF EQU  0x22
  ; VARIABLE DEFINES
  CMD  DATA 0x30  
;------------------------------------------------------------------------------
;   INTERRUPT VECTOR DEFINITIONS
;------------------------------------------------------------------------------
  ORG  0000H   ; STARTUP MEMORY ADDRESS
  JMP  RESET
;------------------------------------------------------------------------------
;   MAIN PROGRAM
;------------------------------------------------------------------------------
RESET: MOV  SP, #5FH
IOINIT: ORL  PCON, #00H ; SMOD = 0, DOUBLE BAUD RATE SPEED DISABLE
  MOV  SCON, #01000000B ; UART MODE 1, 8-BIT
  MOV  TMOD, #20H ; TIMER1, MODE 2(8-BIT AUTOLOAD), 1200bps
  MOV  TL1, #0E6H
  MOV  TH1, #0E6H
  SETB TR1   ; START TIMER 1
MAIN: ; send led on command
  MOV  CMD, #LEDON
  LCALL SEND_PACKS  
  ; delay 100 ms
  LCALL  DELAY_100MS
  ; send led off command
  MOV  CMD, #LEDOFF
  LCALL SEND_PACKS
  ; delay 100 ms
  LCALL DELAY_100MS
  JMP  MAIN
;------------------------------------------------------------------------------
;   SUBROUTINE
;------------------------------------------------------------------------------
;    DELAY R5 x 100 ms
;  before call it should set R5 value
;------------------------------------------------
DELAY_100MS:
;delay:  MOV  R6, #250
   MOV  R6, #250
delay_1: MOV  R7, #200
   DJNZ R7, $
   DJNZ R6, delay_1
;   DJNZ R5, delay
   RET
;------------------------------------------------
;  SEND PACKS FROM UART
;  1. synchronous signal
;  2. wireless device ID
;  3. command
;  4. check sum
;------------------------------------------------
SEND_PACKS:
  MOV  SBUF, #SYNC  ; synchronous signal
  JNB  TI, $
  CLR  TI
  MOV  SBUF, #RADDR  ; wireless devive ID
  JNB  TI, $
  CLR  TI
  MOV  SBUF, CMD  ; command
  JNB  TI, $
  CLR  TI
  MOV  A, CMD
  ADD  A, #RADDR
  MOV  SBUF, A   ; check sum
  JNB  TI, $
  CLR  TI
  RET
;------------------------------------------------------------------------------
  END

AT89S51 與 ATtiny2313 接收程式中,AT89S51 的中斷副程式同時處理 UART 傳送 (TxD) 與接收 (RxD) 觸發的中斷,而 ATtiny2313 (應該 AVR都是一樣的) 是分開處理的。因此在處理 AT89S51 的 UART 中斷時也要同時注意中斷是由哪一個所引起,雖然在接收程式可以不去管 TxD 所造成的中斷,但為了避免錯誤,所以只要是由 TxD 所引起的 UART 中斷,都直接將 TI設置為 0並直接返回,這是在撰寫 AT89S51 時要注意到的。

AT89S51 發射與接收的 Keil 專案檔整理在此,按此下載

編譯後產生 .HEX 然後燒錄進你的晶片,開始接下來的測試。

上機測試:
上面所提供的六個韌體程式都以實際測試過。

下面影片中,RF發射端使用 AT89S51,電源供給 +5V;RF 接收端使用 ATtiny2313 ,電源供給 +3V。

不管你發射與接收是使用哪一個晶片,正常的動作情形就像影片一樣!

17 則留言:

  1. 請問一下

    接收和發射能不能只靠 89s51 ?

    還是一定要靠 ATtiny2313

    回覆刪除
  2. 都可以!
    89s51與Attiny2313都有附發送與接收的程式在上面,是可以互相混用以及使用同一種晶片做發送與接收的功能

    回覆刪除
  3. 問一下
    發射器跟接收器,如要接天線的話,是要接在哪裡??

    回覆刪除
  4. 想請問神手大大一個問題!!!
    我的訊號頻率約50Hz ,結果tg-11 來不及變換.....
    遙控的訊號頻率不能太高嗎? 50Hz不行?

    回覆刪除
  5. 版主,想請問一下,RF無線傳輸是否為"同步"串列傳輸呢,8051那隻程式傳送端和發送端皆設有同步訊號,但你接收端的接收函式名稱是寫USART_vReceiveByte(),這表示同時擁有同步跟非同步功能?
    串列阜中斷函式那裏你是寫_UART_ISR(),UART為非同步串列傳輸八,既然為非同步,為何還需要那同步訊號呢? 有點不是很明白,想請您指點,謝謝~

    回覆刪除
  6. 您好,我想詢問一下,我在接收的地方想收到數據後,再回送訊號,那接收端程式該怎麼寫好呢? 是不是可以給我些建議呢,謝謝您。

    回覆刪除
  7. 我對此產品非常有興趣,是否可談誤

    回覆刪除
  8. 請問兩端天線要如何選擇?傳送距離大約有多遠?
    謝謝!

    回覆刪除
    回覆
    1. 在這篇網頁 (http://ruten-proteus.blogspot.tw/2013/03/Wireless-RF-HT12DE.html) 搜尋"天線長度計算"這一節,有關於天線計算的說明。
      至於傳輸長度,要看有無遮蔽物、發送與接收功率。以這一組來說,大概空曠處 50-70米以上。

      刪除
  9. 請問有可能做到多個不同發送端同時發送不同的訊號給一個接收端,而接收端仍能辦別出資料及發送端嗎?

    回覆刪除
    回覆
    1. 可以!
      提供你一個網站,看一下這個 Arduino VirtualWire函式庫。
      下面是一個使用的範例
      http://ruten-proteus.blogspot.tw/2014/11/rgb-led-arduino-machester.html?showComment=1455800559297

      簡單的作法:就是設定每一個送出的訊息中包含每一個裝置的 ID,讓接收端以此做為辨識。

      刪除
  10. 請問一下,AT89S51(Keil C) RF發射程式,其中做delay的部分
    {
    #pragma asm
    MOV R6, #250
    delay_1: MOV R7, #200
    DJNZ R7, $
    DJNZ R6, delay_1
    #pragma endasm
    }
    我用Keil uVision5開發環境去跑,會出現Error耶,是因為組合語言的關係嗎?

    回覆刪除
    回覆
    1. 可能是你的專案檔沒有設定好輸出組合語言的選項,可以參考這篇網頁文章的說明,對照一下你現在的設定
      http://wenku.baidu.com/view/30159a21192e45361066f533.html

      若還是有問題,要去找一下 Keil5 相關的資料再看看!

      刪除
  11. 請問一下 如果要控制馬達 程式要怎麼修改

    回覆刪除
    回覆
    1. 根據自定義的字元,傳送控制字元在接收端的 UART_ISR() 裡面做修改

      刪除
  12. 請問可以再上傳一次ATtiny2313 RF發射與接收的程式嗎? 原文的連結點資源被刪除了

    回覆刪除