The main function of these devices is to send and receive text messages to and from another device. It is very much like communicating with a walkie-talkie, except with text and smoother communication scheme. We chose this as our final project because we were inspired by the recent technological trend in wireless communication systems. Also, we thought it could be a cool gadget for our cars so that the cars can communicate with each other on a road trip. (Obviously it is not intended for the driver to use.)
Before we began our design, there are some issues that we need to consider:
1) MaterialsHere is a list of materials we used: -2 Atmel 90s8515 microcontrollers.
-2 STK200 boards. (One supply by ourselves)
-2 4x20 LCD. (model Seiko L2014)
-2 RF transmitters (TWS-434, from www.rentron.com)
-2 RF receivers (RWS-434, from www.rentron.com)
-2 pairs of holtek640/648L encoder/decoder chips (from www.rentron.com)
2) Handshaking One of the biggest issues is that we had to consider was the fact that all the RF components are operating at the same 433.92 MHz range. This means that a delicate handshaking scheme needs to be developed in order to ensure correct communication. It turns out there are many cases that need to be taken into account. For example, what to do when both transmitters are transmitting, when not to transmit, when not to receive. How to let the other device know what my device is doing, and so forth. Even though a full-duplex operation is not possible with these same frequency components, it is really not a problem since we are dealing with data exchange. Unlike voice communication, sending a message occurs much less frequently. And therefore the overall performance is acceptable even without full-duplex. (Please reference program design section for more detailed implementations of the handshaking process).
3) Hardware pin/port requirementsAnother issue is that we needed (per device) 8 pins for transmitter, 8 pins for receiver, 8 pins for LCD, 1 pin for Transmit Enable on the transmitter, and pins for the keypad input. However, since the Atmel 90s8515 mcu only support 4 ports = 32 pins, we had to sacrifice the 1 keypad pin (thus disabling 1 column). The consequence of this is that we had to use 2 shift keys to get as much keys as possible. Still, we were only able to get from 'a' to 'w' plus 'space', 'clear', and 'send' keys. Programming then gets more complicated because now when I charge and discharge PORTB to read in the keypad, I have to take the 1 less pin into effect. (Please refer to hardware design for the pinout of the circuit.)
Here is the keymap for the key pad:
Before we began our design, there are some issues that we need to consider:
General:Our program used only timer1 as the time base. We chose timer1 to ensure accuracy and to get the time base of 1ms. We ran 4 main task timers, 1 for keypad scan, 1 for checking new messages, 1 for transmitter, and another for the receiver. The keypad scanning timer is run every 30ms, the mail checking timer is run every 100ms, while the transmitter and the receiver timers are run every 2ms.
Besides these timers we also had several other "expiry" timers that exits a certain operation for error protection. We used an "ack_timer" to exit the process when we don't receive an acknowledgement from the other device for a long time. There was also timeout that ends the receiving process if data transmission gets lost or obstracted. These timers value can be arbitrarily adjusted to fit the environment or situation.
More on Handshaking:The transmitter and receiver timers each has 3 states. It is the transition between these states that implements the handshaking scheme.
A simple, "ideal", scenario: Device A wants to send message to device B. A starts by sending an initialize character to B and then turning off the transmitter. At this time, A's receiver is set to be in "wait_for_ack" state. Basically, once 'X' has been sent, A is now waiting for an acknowledgement for B. If everything goes well, B gets the signal and turns off the transmitter to prevent interference. B then reply with an ACK. A now receives the ACK and proceed with sending the message. B is receives the message at the same time. Once sending and receiving of the message is done, the states restart on both sides.
Now, some error handling schemes:
1) If both are transmitting at the same time, neither device will get an acknowledgement, and this is where the ack_timer kicks in. After a specified time, both device will prompt a timeout message and resets the connection.
2) While one device is typing a message, it can still receive message.
3) The device will not send message until receiving is over.
4) If a device is interrupted while receiving message, the timeout timer will take effect and resets the connection after a certain time.
5) If the user is reading a message, the device will not accept new messages to prevent overwrite.
6) Since due to limited memory, only 1 received message and 1 send message will be saved. This means that every new received message, if allowed, will overwrite the existing one.
The hardware design is not difficult.
Here are the port/pin connections:
AT90s8515 PINS/PORTS -> | External Connections |
PORTA | (Transmitter) Holtek 640 Data Pins |
PORTB | 1 pin to Transmitter TE, 7 pins to Keypad |
PORTC | LCD connection as specified in lcd.h |
PORTD | (Receiver) Holtek 648l Data Pins. |
Keypad and LCD is connected as demonstrated in class, and the tranceiver and holtek chips are connected as shown in documentation (shown below).
TWS434:
RWS434:
After much tweaking and debugging, we finally got the devices to work as we specified. It is able to exchange messages and handle all handshaking and error cases as planned. We did not test these devices with antennas, but according to documentation, these transceivers should be able to communicate up to 400 ft.
If we were to do this project again, we would probably consider finding a pair of tranceivers that operates on different frequencies. This would simplify the logic and state transition of the project by alot since we would need much less handshaking and error control.
We would also change it so that it can communicate with more than one other device. This should not be too difficult , since all we have to do is to include the target device's identification number when we are about to send a message. That way, only the target device will pick up the transmission.
Source Code:
//EE476 - Spring 20019
//FINAL PROJECT - Wireless email pager
//
//Ajay Deonarine (add4) and Johnny Tseng (jjt9)
//-------------------------------------------
#include <90s8515.h>
#include <stdio.h> // sprintf
#include <delay.h> // delay_ms
#include <lcd.h> // LCD driver routines
#include <stdlib.h>
//**********************************************************
//Global Declarations
//**********************************************************
//timer 1 constants
#define prescale1 1
#define clear_on_match 8
//task timers
unsigned int keypad_timer; //task scheduleing timeout counters
unsigned int receiver_timer;
unsigned int transmitter_timer;
unsigned int checkmail;
#define keypad_ovfl 30
#define receiver_ovfl 2
#define transmitter_ovfl 2
#define checkmail_ovfl 100
//expire timers
int ACK_timer; //Ack timeout
unsigned int timeout; //receiving msg timeout
unsigned int trysend_timer; //try send timeout
#define ACK_ovfl 5000
#define timeout_ovfl 5000
#define trysend_ovfl 5000
/*------------------------------
SUBROUTINES
-------------------------------*/
void initialize(void);
//KeyPad detect state machine
void KeyCommand(void);
void KeyPad(void); //keypad scanning
void Release(void);
void Debounce(void);
void DetectTerm(void);
void StillPressed(void);
void DebounceRelease(void);
//Transmitter
void TrySend(void);
void SendAck(void);
void SendMsg(void); //send msg
//Receiver
void WaitForInit(void);
void WaitForAck(void);
void ReceiveMessage(void);
//RESET
void Reset(void); //clears lcd and message
/*------------------------------
KEYPAD STUFF
-------------------------------*/
#define maxkeys 27
//KEYPAD STATES
#define keycommand_state 0;
#define release_state 1;
#define debounce_state 2;
#define detectterm_state 3;
#define stillpressed_state 4;
#define debouncerelease_state 5;
unsigned char keypadEnable;
char keypadstate;
unsigned char key, butnum, lastnum;
unsigned char keycount;
unsigned char maybe;
unsigned char termSend, termReset, termSpace;
//key pad scan table
flash unsigned char keytbl[26]={0xee, 0xed, 0xeb, 0xde, 0xdd, 0xdb, 0xbe, 0xbd, 0xbb, 0x7e, 0x6c, 0x6d, 0x69, 0x5c, 0x5d, 0x59, 0x3c, 0x3d, 0x39, 0x7c, 0x6a, 0x6b, 0x5a, 0x7a, 0x5b, 0x3b};
flash unsigned char keymap[24]={ 'a', 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x'}; //missing xyz
/*------------------------------
SEND MESSAGE STUFF
-------------------------------*/
#define max_msg_size 81 //maximum num of char the message can have
//80 + 1, 1 is terminating char
unsigned char compose; //0=not composing, 1=compose mode
unsigned char reading;
unsigned char sendmessage[max_msg_size];
//unsigned char max_msg_size; //this is the maximum # of characters per message
unsigned char sendmsg_index; //current index of the message
unsigned char readytosend; //0=no data to send, 1=data present
/*------------------------------
READ MESSAGE STUFF
-------------------------------*/
unsigned char receivedmessage[max_msg_size];
unsigned char receivedmsg_index; //current index of the message
unsigned char readytoread; //0=no data to read, 1=data present
unsigned char readatleastonce=0;
/*------------------------------
RECEIVER STUFF
-------------------------------*/
#define r_wait_for_init 0;
#define r_wait_for_ack 1;
#define r_receive_message 2;
#define r_nop 99;
unsigned char receivedByte; //the byte read from the receiver
unsigned char r_status; //receiver status
unsigned char receivingmsg; //1 = receiver receiving MESSAGE
//unsigned char ACKreceived;
/*------------------------------
TRANSMITTER STUFF
-------------------------------*/
#define t_try_send 0;
#define t_send_message 1;
#define t_send_ack 2;
#define t_nop 99;
unsigned char t_status; //transmitter status
unsigned char sendingmsg; //1 = transmitter sending MESSAGE
/*------------------------------
LCD STUFF
-------------------------------*/
#asm
.equ __lcd_port=0x15
#endasm
//LCD location
//for sending
char lcd_x;
char lcd_y;
/*------------end of declaration---------------*/
//**********************************************************
//timer 1 compare-match A ISR
//use timer 1 to handle date and time
interrupt [TIM1_COMPA] void cmpA_overflow(void)
{
//task timers
if (keypad_timer>0) --keypad_timer; //to get 30ms
if (receiver_timer>0) --receiver_timer;
if (transmitter_timer>0) --transmitter_timer;
if (checkmail>0) --checkmail;
}
//**********************************************************
//Set it all up
//**********************************************************
void initialize(void){
//set up the ports
DDRD=0x00; // PORT D is an input for the receiver
DDRA=0xff; //PORT A is an output for the transmitter
DDRB=0xFF;
PORTB=0x00;
delay_us(15);
DDRB=0x00;
//set up timer 1
TIMSK=TIMSK | 0x40; //turn on timer 1 compare match interrupt
OCR1A = 4000; //to get 1ms
TCCR1B = prescale1 + clear_on_match; //start timer1
TCNT1 = 0; //and zero the timer
//init the task timers
keypad_timer = keypad_ovfl;
receiver_timer = receiver_ovfl;
transmitter_timer = transmitter_ovfl;
ACK_timer = -1;
trysend_timer = trysend_ovfl;
timeout = timeout_ovfl;
//init the keypad vars
keypadEnable = 1; //enable keypad
keycount = 0;
butnum = 99; //99 is the defined release state key
lastnum = 99;
key = 0;
maybe = 0;
termSend = 23; //set this to keytbl[29] = send message
termReset = 24;
termSpace = 25; //right space
//Message initializations
compose=0;
reading=0;
sendmsg_index = 0;
readytosend=0;
receivedmsg_index = 0;
readytoread = 0;
//transmitter initializations
t_status = t_nop;
sendingmsg=0;
//receiver initializations
r_status = r_wait_for_init;
receivingmsg=0;
//LCD initializations
//LCD uses PORT C
lcd_init(20); // initialize the LCD for 16 char wide
lcd_x=0;
lcd_y=0;
lcd_clear(); //clear it
//Initial LCD display
lcd_gotoxy(0,0);
lcd_putsf("Hello");
lcd_gotoxy(0,1);
lcd_putsf("please press: ");
lcd_gotoxy(0,2);
lcd_putsf("'a'=read,");
lcd_gotoxy(0,3);
lcd_putsf("'b'=compose");
lcd_gotoxy(0,0);
////////////////////////////
//crank up the ISRs
#asm
clr r17
sei
#endasm
}
//**********************************************************
//MAIN
//**********************************************************
void main(void)
{
//start
initialize();
// Start LOOP - never ends
while (1) {
/*------------------------------
Check to see if there is message
-------------------------------*/
if (checkmail == 0) {
checkmail = checkmail_ovfl;
if( (keypadEnable) && (readytoread) && (!compose) && (!reading)) {
//write a constant string from flash
lcd_gotoxy(0,0);
lcd_putsf("GOT MSG!");
lcd_gotoxy(0,1);
lcd_putsf("please press: ");
lcd_gotoxy(0,2);
lcd_putsf("'a'=read,");
lcd_gotoxy(0,3);
lcd_putsf("'b'=compose");
lcd_gotoxy(0,0);
}
}
/*------------------------------
KeyPad Scanning Task
-------------------------------*/
if ( (keypad_timer==0) && (keypadEnable) ) {
keypad_timer = keypad_ovfl; //reload timer
if(keypadstate==0) KeyCommand();
if(keypadstate==1) Release();
if(keypadstate==2) Debounce();
if(keypadstate==3) DetectTerm();
if(keypadstate==4) StillPressed();
if(keypadstate==5) DebounceRelease();
}
/*------------------------------
Transmitter Task
------------------------------*/
if (transmitter_timer==0){
transmitter_timer = transmitter_ovfl; //reload timer
if(t_status==0) TrySend();
if(t_status==1) SendMsg();
if(t_status==2) SendAck();
}
/*------------------------------
Receiver Task
-------------------------------*/
if (receiver_timer==0){
receiver_timer = receiver_ovfl; //reload timer
receivedByte=PIND; //read byte at receiver
if(r_status==0) WaitForInit();
if(r_status==1) WaitForAck();
if(r_status==2) ReceiveMessage();
}
}
}
/*---------------------End Main()---------------------------*/
/*-------------------------------------------------------------*/
//**********************************************************
//Transmitter TASK --
//**********************************************************
//Send Ackknowledgement back
void SendAck(void){
int i=0;
DDRB=0xFF;
PORTB=0xFF; //TE =high
PORTA='A'; //send A
delay_ms(300);
PORTA='A'; //send A
delay_ms(300);
PORTA='A'; //send A
delay_ms(500);
PORTB=0x00;
DDRB=0x00;
t_status = t_nop;
receivedmsg_index=0;
//reset message:
for(i=0; i<max_msg_size; i++){
receivedmessage[i]='\0';
}
r_status = r_receive_message;
if(!compose){
lcd_clear();
lcd_putsf("Receiving...");
}
}
//Try to initialize sending
void TrySend(void){
//if not receiving message, send X
if (!receivingmsg){
lcd_clear();
DDRB=0xFF;
PORTB=0xFF;
PORTA='X';
delay_ms(300);
PORTA='X';
delay_ms(300);
PORTA='X';
delay_ms(500);
PORTB=0x00;
DDRB=0x00;
//initialize next state var
sendingmsg=1;
receivingmsg=0;
r_status = r_wait_for_ack; //change receiver status to "wait for ack"
ACK_timer = ACK_ovfl; //start Ack expire timer
t_status = t_nop; //transmitter == do nothing
}
//else don't do anything until receiving ended
else {
t_status = t_try_send;
trysend_timer--;
//trysend expire
if (trysend_timer == 0){
lcd_clear();
lcd_putsf("trysend timeout");
delay_ms(2000);
Reset();
}
}
}
void SendMsg(void){
DDRB=0xFF;
PORTB=0xFF; //TE =high
//sending message here
if( (sendmsg_index < max_msg_size-1) && (sendmessage[sendmsg_index] != 'T') ){
PORTA = sendmessage[sendmsg_index];
sendmsg_index++;
delay_ms(300);
t_status=t_send_message;
}
//done sending
else {
//send terminating char 'T'
PORTA = sendmessage[sendmsg_index];
sendmsg_index++;
delay_ms(300);
//Done sending, reset
PORTB=0x00; //TE= off;
DDRB=0x00;
//Initialize next state variables
sendmsg_index=0;
sendingmsg = 0;
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("successful!");
t_status=t_nop;
delay_ms(2000);
Reset();
}
}
/*-------------------------------------------------------------*/
//**********************************************************
//Task Receiver --
//Wait for X from the other end to initialize
void WaitForInit(void){
//don't start unless not sending msg, and not reading, or msg read at least once
if (!sendingmsg && !reading ) {
if (receivedByte == 'X'){
delay_ms(300);
//initialize nextstate var
receivingmsg=1;
sendingmsg=0;
r_status = r_receive_message;
t_status = t_send_ack;
if(!compose){
lcd_clear();
lcd_putsf("GOTX");
}
}
}
}
//Wait for Ack
void WaitForAck(void){
ACK_timer--;
//Check to see if Ack is received
if (receivedByte == 'A'){
delay_ms(300);
r_status = r_nop; //receiver == do nothing
t_status = t_send_message; //transmitter == send message
lcd_clear();
lcd_putsf("gotACK");
//Initialize next state variables
sendingmsg=1; //sending msg next
receivingmsg=0; //turn receiving message off
ACK_timer = -1; //turn off Ack expire timer
sendmsg_index=0; //initialize variable
//lcd_puts(sendmessage);
}
//If ACK not received, and ack timer expires
else if (ACK_timer ==0){
//OUTPUT ERR and ABORT
lcd_clear();
lcd_putsf("TIMEOUT!");
DDRB=0xFF;
PORTB=0xFF;
PORTA='Z';
delay_ms(500);
PORTB=0x00;
DDRB=0x00;
delay_ms(2000);
Reset();
}
}
//Receive message
void ReceiveMessage(void){
PORTB=0x00;
DDRB=0x00;
timeout--;
// lcd_putchar(receivedByte);
// delay_ms(300);
if (timeout == 0){
lcd_clear();
lcd_putsf("connect timeout\n");
lcd_putsf("resetting...");
delay_ms(2000);
Reset();
}
//preventing from receiving ACk of own transmitter
else if ( (receivedByte=='A') || (receivedByte=='X')) {
//do nothing
delay_ms(100);
}
//store message
else if( (receivedmsg_index < max_msg_size) && (receivedByte!='T') && (receivedByte!='A') && (receivedByte!='X') ){
timeout = timeout_ovfl; //reset timeout expire timer
receivedmessage[receivedmsg_index] = receivedByte;
receivedmsg_index++;
delay_ms(300);
}
//Done receiving
else {
//Initialize nextstate var
readytoread=1;
receivedmsg_index=0;
receivingmsg = 0;
r_status = r_wait_for_init;
}
}
/*-------------------------------------------------------------*/
/*-------------------------------------------------------------*/
//**********************************************************
//Task KeyCommand -- keypad detection state machine
void KeyCommand(void){
//reset
sendmsg_index =0;
receivedmsg_index =0;
butnum=99;
KeyPad();
keypadstate=release_state;
}
//STATES STARTS HERE//
//Release
void Release(){
if (butnum==99){
KeyPad();
//goto Release State
keypadstate=release_state;
}
else{
maybe=butnum;
KeyPad();
keypadstate=debounce_state;
//goto Debounce State
}
}
//Debounce
void Debounce(){
if (butnum==maybe){
/////////////echo keypad//
// printf("%d\r\n",butnum);
keypadstate=detectterm_state;
//goto DetectTerm State
}
else{
KeyPad();
keypadstate=release_state;
//goto Release State
}
}
//DetectTerm
void DetectTerm(){
//check for term type
//go into compose mode
if ( (butnum == 1) && (!compose) ){
//ready for compose
lcd_clear();
lcd_x=0;
lcd_y=0;
compose=1;
reading=0;
}
//read message
else if ( (butnum==0) && (!compose) ) {
lcd_clear();
reading=1;
compose=0;
readatleastonce=1;
if (readytoread){
lcd_puts(receivedmessage);
}
else {
lcd_clear();
lcd_putsf("no msgs");
lcd_gotoxy(0,1);
lcd_putsf("press 'Clr' to exit");
}
}
else if ( (butnum== termReset) ){
Reset();
}
//send message
else if ( (butnum == termSend) && (compose) ){
//REQUEST SEND
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("Message saved. ");
lcd_gotoxy(0,1);
lcd_putsf("Connecting.. ");
lcd_gotoxy(0,2);
lcd_putsf("Please wait... ");
//
sendmessage[sendmsg_index] = 'T'; //end of msg
sendmsg_index++;
keypadEnable=0; //turn off keypad
t_status = t_try_send;
}
//otherwise
else if (compose) {
//checks to see if message exceeds max allowable size
if (sendmsg_index < max_msg_size - 1 ){
//echo to LCD
if(lcd_x == 20){
lcd_x==0;
lcd_y++;
}
if(butnum == termSpace){
lcd_putchar(' ');
lcd_x++;
sendmessage[sendmsg_index]=' ';
sendmsg_index++;
}
else {
lcd_putchar( keymap[butnum]);
lcd_x++;
//stores in message
sendmessage[sendmsg_index]=keymap[butnum];
sendmsg_index++;
}
} else {
//notify msg exceed max size
//output to lcd
lcd_clear();
lcd_putsf("msg exceeds max, ...send?");
lcd_gotoxy(0,1);
lcd_putsf("('Send'=send, 'Clr'=clr)");
}
}
//for checking still pressed
lastnum = butnum;
KeyPad();
//goto StillPressed State
keypadstate=stillpressed_state;
}
//StillPressed
void StillPressed(){
if (butnum == lastnum){
KeyPad();
keypadstate=stillpressed_state;
//goto StillPressed State
}
else{
KeyPad();
keypadstate=debouncerelease_state;
//goto DebounceRelease State
}
}
void DebounceRelease(void){
if (butnum == lastnum){
KeyPad();
keypadstate=stillpressed_state;
//goto StillPressed State
}
else{
KeyPad();
keypadstate=release_state;
//goto to Release State
}
}
//**********************************************************
//KeyPad -- KeyPad Scanning
void KeyPad(void){
//get upper nibble
DDRB= 0x0f;
PORTB = 0xf0;
delay_us(5);
key = PINB;
//get lower nibble
DDRB = 0xf0;
PORTB = 0x07; //only want to read in the lower 3 bits
delay_us(5);
key = key | PINB;
key = key | 0x08;
//find matching keycode in keytbl
if (key != 0xff){
for (butnum=0; butnum<maxkeys; butnum++){
if (keytbl[butnum]==key) break;
}
if (butnum==maxkeys) butnum=99; //99 = not pressed or not matched
}
else butnum=99; //99 = not pressed or not matched
}
/////////////////////////////////////
//Reset
void Reset(void){
//reset lcd
lcd_clear(); //clear it
lcd_x=0;
lcd_y=0;
//Reinitialize Screen
lcd_gotoxy(0,0);
lcd_putsf("Hello");
lcd_gotoxy(0,1);
lcd_putsf("please press: ");
lcd_gotoxy(0,2);
lcd_putsf("'a'=read,");
lcd_gotoxy(0,3);
lcd_putsf("'b'=compose");
lcd_gotoxy(0,0);
//reset send/receive message var
sendmsg_index = 0;
sendingmsg = 0;
receivingmsg = 0;
receivedmsg_index = 0;
compose = 0;
reading = 0;
//resets keypad
keypadEnable=1;
keypadstate=keycommand_state; //detect keypad again
butnum = 99;
//reset expire timer
ACK_timer = -1;
trysend_timer = trysend_ovfl;
timeout = timeout_ovfl;
//reset t/r status var
t_status = t_nop;
r_status = r_wait_for_init;
}
0 comments:
Post a Comment