RX TX Module Programming





Running TX433 and RX433 RF modules with AVR microcontrollers

Sometimes in embedded design you may want to go wireless. Might be you will want to log various readings of remotely placed sensors, or simply build a remote control for robot or car alarm system.
Radio communications between two AVR microcontrollers can be easy when specialized modules are used. Lets try to run very well known RF modules TX433 and RX433 that (or similar) can be found almost in every electronics shop and pair of them cost about ~15 bucks.

Transmitter and receiver modules are tuned to work correctly at 433.92MHz. Transmitter can be powered from 3 to 12V power supply while receiver accepts 5V. 5V is common for AVR microcontrollers so no problems with interfacing. Modules don't require addition components – just apply power and connect single data line to send information to/from and that's it. For better distances apply 30 – 35cm antennas. Modules use Amplitude-Shift Keying(ASK) modulation method and uses 1MHz bandwidth.
I have constructed two separate circuits for testing on Atmega8 microcontrollers.
Transmitter

Receiver

For testing I have used a prototyping board and breadboard.

As you can see I have used one LED for indicating RF activity. Ok enough about hardware part – actually there is nothing more to say – circuits are simple.
Lets move on to software part. Radio transmission is a bit more complicated than wired communications because you never know what radio signals are present on air. So all matters how transmitted signal is encoded. And this is a part where you have many choices: use hardware encoding like USART or write your own based on one of many ending methods like NRZ, Manchester etc. In my example I have used AVR USART module to form data packs. Using hardware encoders solves many problems like synchronization, start and stop, various signal checks. But as long as I was practising you cannot rely on plain USART signal. Here you can actually improvize by adding various checks and so on.
I decided to form 4 byte data packages in order to send one byte information. These include:
• one dummy synchronization byte (10101010);
• one address byte – in case there are more receivers(or transmitters);
• one data byte;
• and checksum which is actually a sum of address and data(address+data).
Why did I use a dummy byte at the beginning of package. Simply I noticed, that when transmitter doesn't transmit any data – receiver catches various noises that come from power supply or other sources because receiver likes adjust its input gain depending on input signal level. First byte tunes receiver to accept normal signal after then address byte, data and checksum can be read more reliably. Probably with different transmission modules you may exclude this dummy byte.
Ttransmitter program for AVR Atmega8:

#include
#include
#ifndef F_CPU
//define cpu clock speed if not defined
#define F_CPU 8000000
#endif
//set desired baud rate
#define BAUDRATE 1200
//calculate UBRR value
#define UBRRVAL ((F_CPU/(BAUDRATE*16UL))-1)
//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 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=(1< (0< //Enable Transmitter and Receiver and Interrupt on receive complete
UCSRB=(1<}
void USART_vSendByte(uint8_t u8Data)
{
// Wait if a byte is being transmitted
while((UCSRA&(1< // Transmit data
UDR = u8Data;
}
void Send_Packet(uint8_t addr, uint8_t cmd)
{
USART_vSendByte(SYNC);//send synchro byte
USART_vSendByte(addr);//send receiver address
USART_vSendByte(cmd);//send increment command
USART_vSendByte((addr+cmd));//send checksum
}
void delayms(uint8_t t)//delay in ms
{
uint8_t i;
for(i=0;i _delay_ms(1);
}
int main(void)
{
USART_Init();
while(1)
{//endless transmission
//send command to switch led ON
Send_Packet(RADDR, LEDON);
delayms(100);
//send command to switch led ON
Send_Packet(RADDR, LEDOFF);
delayms(100);
}
return 0;
}

In my case I used UART 1200 baud rate. It may be increased or decreased depending on distance and environment. For longer distances lower baud rates works better as there is bigger probability for transmission errors. Maximum bit rate of transmitter is 8kbits/s what is about 2400 baud. But what works in theory usually do not work in practice. So 1200 baud is maximum what I could get working correctly.
Transmitter sends two commands (LEDON and LEDOFF) to receiver with 100ms gaps. Receiver recognizes these commands and switches LED on or off depending on received command. This way I can monitor if data transfer works correctly. If LED blink is periodical – then transmission goes without errors. If there is an error in received data then LED gives shorter blink.
Receiver program code:

#include
#include
#include
#ifndef F_CPU
//define cpu clock speed if not defined
#define F_CPU 4000000
#endif
//set desired baud rate
#define BAUDRATE 1200
//calculate UBRR value
#define UBRRVAL ((F_CPU/(BAUDRATE*16UL))-1)
//define receive parameters
#define SYNC 0XAA// synchro signal
#define RADDR 0x44
#define LEDON 0x11//LED on command
#define LEDOFF 0x22//LED off command
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=(1< (0< //Enable Transmitter and Receiver and Interrupt on receive complete
UCSRB=(1< //enable global interrupts
}
uint8_t USART_vReceiveByte(void)
{
// Wait until a byte has been received
while((UCSRA&(1< // Return received data
return UDR;
}
ISR(USART_RXC_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)
{
PORTC&=~(1<<0);//LED ON
}
else if(data==LEDOFF)
{
PORTC|=(1<<0);//LED OFF
}
else
{
//blink led as error
PORTC|=(1<<0);//LED OFF
_delay_ms(10);
PORTC&=~(1<<0);//LED ON
}
}
}
}
void Main_Init(void)
{
PORTC|=(1<<0);//LED OFF
DDRC=0X001;//define port C pin 0 as output;
//enable global interrupts
sei();
}
int main(void)
{
Main_Init();
USART_Init();
while(1)
{
}
//nothing here interrupts are working
return 0;
}

Receiver program receives all four bytes, then checks if checksum of received bytes is same as received checksum value. If checksum test passes then receiver addresses are compared and if signal is addressed to receiver it analyses data.
After all I have noticed that without antennas transmission is more erroneous even if modules are standing near by. Of course with all my power chords around the room I was getting lots of noises that receiver was catching between data transmissions.

DC Motor interfacing with Microcontrollers tutorial




►Introduction

Whenever a robotics hobbyist talk about making a robot, the first thing comes to his mind is making the robot move on the ground. And there are always two options in front of the designer whether to use a DC motor or a stepper motor. When it comes to speed, weight, size, cost... DC motors are always preffered over stepper motors. There are many things which you can do with your DC motor when interfaced with a microcontroller. For example you can control the speed of motor, you can control the direction of rotation, you can also do encoding of the rotation made by DC motor i.e. keeping track of how many turns are made by your motors etc. So you can see DC motors are no less than a stepper motor.

In this part of tutorial we will learn to interfacing a DC motor with a microcontroller. Usually H-bridge is preffered way of interfacing a DC motor. These days many IC manufacturers have H-bridge motor drivers available in the market like L293D is most used H-Bridge driver IC. H-bridge can also be made with the help of trasistors and MOSFETs etc. rather of being cheap, they only increase the size of the design board, which is somtimes not required so using a small 16 pin IC is preffered for this purpose.

►Working Theory of H-Bridge

The name "H-Bridge" is derived from the actual shape of the switching circuit which control the motoion of the motor. It is also known as "Full Bridge". Basically there are four switching elements in the H-Bridge as shown in the figure below.



As you can see in the figure above there are four switching elements named as "High side left", "High side right", "Low side right", "Low side left". When these switches are turned on in pairs motor changes its direction accordingly. Like, if we switch on High side left and Low side right then motor rotate in forward direction, as current flows from Power supply through the motor coil goes to ground via switch low side right. This is shown in the figure below.



Similarly, when you switch on low side left and high side right, the current flows in opposite direction and motor rotates in backward direction. This is the basic working of H-Bridge. We can also make a small truth table according to the switching of H-Bridge explained above.
Truth Table
High Left High Right Low Left Low Right Description
On Off Off On Motor runs clockwise
Off On On Off Motor runs anti-clockwise
On On Off Off Motor stops or decelerates
Off Off On On Motor stops or decelerates

As already said, H-bridge can be made with the help of trasistors as well as MOSFETs, the only thing is the power handling capacity of the circuit. If motors are needed to run with high current then lot of dissipation is there. So head sinks are needed to cool the circuit.

Now you might be thinkin why i did not discuss the cases like High side left on and Low side left on or high side right on and low side right on. Clearly seen in the diagra, you don't want to burn your power supply by shorting them. So that is why those combinations are not discussed in the truth table.

So we have seen that using simple switching elements we can make our own H-Bridge, or other option we have is using an IC based H-bridge driver. Both of them are discussed in the next section of the tutorial.


DC Motor interfacing with Microcontrollers tutorial: BJT Based H-Bridge for DC motors

►BJT H-Bridge

A simple H-bridge can be made with the help of Power BJTs like TIP31 and TIP32. An example and a working demo of this circuit is shown in the figure below.


►BJT H-Bridge Demo


DC Motor interfacing with Microcontrollers tutorial: L293D H-Bridge interfacing

►L293D Dual H-Bridge Motor Driver

L293D is a dual H-Bridge motor driver, So with one IC we can interface two DC motors which can be controlled in both clockwise and counter clockwise direction and if you have motor with fix direction of motion the you can make use of all the four I/Os to connect up to four DC motors. L293D has output current of 600mA and peak output current of 1.2A per channel. Moreover for protection of circuit from back EMF ouput diodes are included within the IC. The output supply (VCC2) has a wide range from 4.5V to 36V, which has made L293D a best choice for DC motor driver.

A simple schematic for interfacing a DC motor using L293D is shown below.



As you can see in the circuit, three pins are needed for interfacing a DC motor (A, B, Enable). If you want the o/p to be enabled completely then you can connect Enable to VCC and only 2 pins needed from controller to make the motor work.


As per the truth mentioned in the image above its fairly simple to program the microcontroller. Its also clear from the truth table of BJT circuit and L293D the programming will be same for both of them, just keeping in mind the allowed combinations of A and B. We will discuss about programming in C as well as assembly for running motor with the help of a microcontroller.





DC Motor interfacing with Microcontrollers tutorial: Programming Microcontroller

►Assembly programming
CODE:
L293D_A equ P2.0 ;L293D A - Positive of Motor
L293D_B equ P2.1 ;L293D B - Negative of Motor
L293D_E equ P2.2 ;L293D E - Enable pin of IC

org 0H
Main:
acall rotate_f ;Rotate motor forward
acall delay ;Let the motor rotate
acall break ;Stop the motor
acall delay ;Wait for some time
acall rotate_b ;Rotate motor backward
acall delay ;Let the motor rotate
acall break ;Stop the motor
acall delay ;Wait for some time
sjmp Main ;Do this in loop

rotate_f:
setb L293D_A ;Make Positive of motor 1
clr L293D_B ;Make negative of motor 0
setb L293D_E ;Enable to run the motor
ret ;Return from routine

rotate_b:
clr L293D_A ;Make positive of motor 0
setb L293D_B ;Make negative of motor 1
setb L293D_E ;Enable to run the motor
ret ;Return from routine

break:
clr L293D_A ;Make Positive of motor 0
clr L293D_B ;Make negative of motor 0
clr L293D_E ;Disable the o/p
ret ;Return from routine

delay: ;Some Delay
mov r7,#20H
back: mov r6,#FFH
back1: mov r5,#FFH
here: djnz r5, here
djnz r6, back1
djnz r7, back
ret




►C programming
CODE:
#include #define L293D_A P2_0 //Positive of motor
#define L293D_B P2_1 //Negative of motor
#define L293D_E P2_2 //Enable of L293D

// Function Prototypes
void rotate_f(void); //Forward run funtion
void rotate_b(void); //Backward run function
void breaks(void); //Motor stop function
void delay(void); //Some delay

void main(){ //Our main function
while(1){ //Infinite loop
rotate_f(); //Run forward
delay(); //Some delay
breaks(); //Stop
delay(); //Some delay
rotate_b(); //Run Backwards
delay(); //Some delay
breaks(); //Stop
delay(); //Some delay
} //Do this infinitely
}

void rotate_f(){
L293D_A = 1; //Make positive of motor 1
L293D_B = 0; //Make negative of motor 0
L293D_E = 1; //Enable L293D
}

void rotate_b(){
L293D_A = 0; //Make positive of motor 0
L293D_B = 1; //Make negative of motor 1
L293D_E = 1; //Enable L293D
}

void breaks(){
L293D_A = 0; //Make positive of motor 0
L293D_B = 0; //Make negative of motor 0
L293D_E = 0; //Disable L293D
}

void delay(){ //Some delay...
unsigned char i,j,k;
for(i=0;i<0x20;i++) j="0;j<255;j++)" k="0;k<255;k++);">

LCD interfacing with Microcontrollers tutorial - 4-bit Mode

►Introduction

Till now whatever we discussed in the previous part of ths LCD tutorial, we were dealing with 8-bit mode. Now we are going to learn how to use LCD in 4-bit mode. There are many reasons why sometime we prefer to use LCD in 4-bit mode instead of 8-bit. One basic reason is lesser number of pins are needed to interface LCD.

In 4-bit mode the data is sent in nibbles, first we send the higher nibble and then the lower nibble. To enable the 4-bit mode of LCD, we need to follow special sequence of initialization that tells the LCD controller that user has selected 4-bit mode of operation. We call this special sequence as resetting the LCD. Following is the reset sequence of LCD.
 Wait for abour 20mS
 Send the first init value (0x30)
 Wait for about 10mS
 Send second init value (0x30)
 Wait for about 1mS
 Send third init value (0x30)
 Wait for 1mS
 Select bus width (0x30 - for 8-bit and 0x20 for 4-bit)
 Wait for 1mS

The busy flag will only be valid after the above reset sequence. Usually we do not use busy flag in 4-bit mode as we have to write code for reading two nibbles from the LCD. Instead we simply put a certain ammount of delay usually 300 to 600uS. This delay might vary depending on the LCD you are using, as you might have a different crystal frequency on which LCD controller is running. So it actually depends on the LCD module you are using. So if you feel any problem running the LCD, simply try to increase the delay. This usually works. For me about 400uS works perfect.

►Sending data/command in 4-bit Mode

We will now look into the common steps to send data/command to LCD when working in 4-bit mode. As i already explained in 4-bit mode data is sent nibble by nibble, first we send higher nibble and then lower nibble. This means in both command and data sending function we need to saperate the higher 4-bits and lower 4-bits.


The common steps are:
 Mask lower 4-bits
 Send to the LCD port
 Send enable signal
 Mask higher 4-bits
 Send to LCD port
 Send enable signal

We are done with the theory part now, In the next section we will take a look at the programming microcontroller to control LCD in 4-bit mode.

LCD interfacing with Microcontrollers tutorial - 4-bit Mode

►4-bit Initialization

Initialization of LCD is completed only after the reset sequence and basic initialization commands. We have already discussed about the reset sequence of the lcd in the previous section. So lets look at the programming now...


►Assembly Program
CODE:
;In this whole 4-bit tutorial LCD is connected to
;my controller in following way...
;D4 - P3.0
;D5 - P3.1
;D6 - P3.2
;D7 - P3.3
;EN - P3.7
;RS - P3.5

lcd_port equ P3 ;LCD connected to Port3
en equ P3.7 ;Enable connected to P3.7
rs equ P3.5 ;Register select to P3.5

lcd_reset: ;LCD reset sequence
mov lcd_port, #0FFH
mov delay,#20 ;20mS delay
acall delayms
mov lcd_port, #83H ;Data = 30H, EN = 1, First Init
mov lcd_port, #03H ;Data = 30H, EN = 0
mov delay,#15 ;Delay 15mS
acall delayms
mov lcd_port, #83H ;Second Init, Data = 30H, EN = 1
mov lcd_port, #03H ;Data = 30H, EN = 0
mov delay,#5 ;Delay 5mS
acall delayms
mov lcd_port, #83H ;Third Init
mov lcd_port, #03H
mov delay,#5 ;Delay 5mS
acall delayms
mov lcd_port, #82H ;Select Data width (20H for 4bit)
mov lcd_port, #02H ;Data = 20H, EN = 0
mov delay,#5 ;Delay 5mS
acall delayms
ret

lcd_init:
acall lcd_reset ;Call LCD Reset sequence
mov a,#28H ;4-bit, 2 line, 5x7 dots
acall lcd_cmd ;Call LCD command
mov a,#0CH ;Display ON cursor OFF
acall lcd_cmd ;Call LCD command
mov a,#06H ;Set entry mode (Auto increment)
acall lcd_cmd ;Call LCD command
mov a,#80H ;Bring cursor to line 1
acall lcd_cmd ;Call LCD command
ret




►C Program
CODE:
//The pins used are same as explained earlier
#define lcd_port P3

//LCD Registers addresses
#define LCD_EN 0x80
#define LCD_RS 0x20

void lcd_reset()
{
lcd_port = 0xFF;
delayms(20);
lcd_port = 0x03+LCD_EN;
lcd_port = 0x03;
delayms(10);
lcd_port = 0x03+LCD_EN;
lcd_port = 0x03;
delayms(1);
lcd_port = 0x03+LCD_EN;
lcd_port = 0x03;
delayms(1);
lcd_port = 0x02+LCD_EN;
lcd_port = 0x02;
delayms(1);
}

void lcd_init ()
{
lcd_reset(); // Call LCD reset
lcd_cmd(0x28); // 4-bit mode - 2 line - 5x7 font.
lcd_cmd(0x0C); // Display no cursor - no blink.
lcd_cmd(0x06); // Automatic Increment - No Display shift.
lcd_cmd(0x80); // Address DDRAM with 0 offset 80h.
}




►Sending Dommand/Data to LCD in 4-bit mode

►Assembly Program
CODE:
lcd_cmd: ;LCD command Routine
mov temp,a ;Save a copy of command to temp
swap a ;Swap to use higher nibble
anl a,#0FH ;Mask the first four bits
add a,#80H ;Enable = 1, RS = 0
mov lcd_port,a ;Move it to lcd port
anl a,#0FH ;Enable = 0, RS = 0
mov lcd_port,a ;Move to lcd port

mov a,temp ;Reload the command from temp
anl a,#0FH ;Mask first four bits
add a,#80H ;Enable = 1
mov lcd_port,a ;Move to port
anl a,#0FH ;Enable = 0
mov lcd_port,a ;Move to lcd port

mov delay,#1 ;delay 1 ms
acall delayms
ret

lcd_dat: ;LCD data Routine
mov temp,a ;Keep copy of data in temp
swap a ;We need higher nibble
anl a,#0FH ;Mask first four bits
add a,#0A0H ;Enable = 1, RS = 1
mov lcd_port,a ;Move to lcd port
nop
clr en ;Enable = 0

mov a,temp ;Reload the data from temp
anl a,#0FH ;we need lower nibble now
add a,#0A0H ;Enable = 1, RS = 1
mov lcd_port,a ;Move to lcd port
nop
clr en ;Enable = 0

mov delay,#1 ;Delay 1mS
acall delayms
ret




►C Program
CODE:
void lcd_cmd (char cmd)
{
lcd_port = ((cmd >> 4) & 0x0F)|LCD_EN;
lcd_port = ((cmd >> 4) & 0x0F);

lcd_port = (cmd & 0x0F)|LCD_EN;
lcd_port = (cmd & 0x0F);

delayus(200);
delayus(200);
}

void lcd_data (unsigned char dat)
{
lcd_port = (((dat >> 4) & 0x0F)|LCD_EN|LCD_RS);
lcd_port = (((dat >> 4) & 0x0F)|LCD_RS);

lcd_port = ((dat & 0x0F)|LCD_EN|LCD_RS);
lcd_port = ((dat & 0x0F)|LCD_RS);

delayus(200);
delayus(200);
}

LCD Programming - Creating custom character


CGRAM and Character Building

As already explained, all character based LCD of type HD44780 has CGRAM area to create user defined patterns. For making custom patterns we need to write values to the CGRAM area defining which pixel to glow. These values are to be written in the CGRAM adress starting from 0x40. If you are wondering why it starts from 0x40? Then the answer is given below.



Bit 7 is 0 and Bit 6 is 1, due to which the CGRAM adress command starts from 0x40, where the address of CGRAM (Acg) starts from 0x00. CGRAM has a total of 64 Bytes. When you are using LCD as 5x8 dots in function set then you can define a total of 8 user defined patterns (1 Byte for each row and 8 rows for each pattern), where as when LCD is working in 5x10 dots, you can define 4 user defined patterns.

Lets take an of bulding a custom pattern. All we have to do is make a pixel-map of 7x5 and get the hex or decimal value or hex value for each row, bit value is 1 if pixel is glowing and bit value is 0 if pixel is off. The final 7 values are loaded to the CGRAM one by one. As i said there are 8 rows for each pattern, so last row is usually left blank (0x00) for the cursor. If you are not using cursor then you can make use of that 8th row also. so you get a bigger pattern.


To explain the above explaination in a better way. I am going to take an example. Lets make a "Bell" pattern as shown below.



Now we get the values for each row as shown.
Bit: 4 3 2 1 0 - Hex
Row1: 0 0 1 0 0 - 0x04
Row2: 0 1 1 1 0 - 0x0E
Row3: 0 1 1 1 0 - 0x0E
Row4: 0 1 1 1 0 - 0x0E
Row5: 1 1 1 1 1 - 0x1F
Row6: 0 0 0 0 0 - 0x00
Row7: 0 0 1 0 0 - 0x04
Row8: 0 0 0 0 0 - 0x00

We are not using row 8 as in our pattern it is not required. if you are using cursor then it is recommended not to use the 8th row. Now as we have got the values. We just need to put these values in the CGRAM. You can decided which place you want to store in. Following is the memory map for custom patterns in CGRAM.
Memory Map
Pattern No. CGRAM Address (Acg)
1 0x00 - 0x07
2 0x08 - 0x0F
3 0x10 - 0x17
4 0x18 - 0x1F
5 0x20 - 0x27
6 0x28 - 0x2F
7 0x30 - 0x37
8 0x38 - 0x3F

We can point the cursor to CGRAM address by sending command, which is 0x40 + CGRAM address (For more information please see Table 4 in commands section). Lets say we want to write the Bell pattern at second pattern location. So we send the command as 0x48 (0x40 + 0x08), and then we send the pattern data. Below is a small programming example to do this.
CODE:
;LCD Ports are same as discussed in previous sections

LCD_build:
mov A,#48H ;Load the location where we want to store
acall LCD_command ;Send the command
mov A,#04H ;Load row 1 data
acall LCD_senddata ;Send the data
mov A,#0EH ;Load row 2 data
acall LCD_senddata ;Send the data
mov A,#0EH ;Load row 3 data
acall LCD_senddata ;Send the data
mov A,#0EH ;Load row 4 data
acall LCD_senddata ;Send the data
mov A,#1FH ;Load row 5 data
acall LCD_senddata ;Send the data
mov A,#00H ;Load row 6 data
acall LCD_senddata ;Send the data
mov A,#04H ;Load row 7 data
acall LCD_senddata ;Send the data
mov A,#00H ;Load row 8 data
acall LCD_senddata ;Send the data
ret ;Return from routine


The above routine will create bell character at pattern location 2. To display the above generated pattern on LCD, simply load the pattern location (0,1,2,...7) and call the LCD_senddata subroutine. Now we can also write the above routine in C as...
CODE:
//LCD Ports are same as discussed in previous sections
void LCD_build(){
LCD_command(0x48); //Load the location where we want to store
LCD_senddata(0x04); //Load row 1 data
LCD_senddata(0x0E); //Load row 2 data
LCD_senddata(0x0E); //Load row 3 data
LCD_senddata(0x0E); //Load row 4 data
LCD_senddata(0x1F); //Load row 5 data
LCD_senddata(0x00); //Load row 6 data
LCD_senddata(0x04); //Load row 7 data
LCD_senddata(0x00); //Load row 8 data
}


I think now most of you find programing in C more simple than assembly. We can also summarize the above in a simple small routine so that you can simply call the build routine providing a pointer to array containing the build data. Below example shows how to do it.
CODE:
//Input:
// location: location where you want to store
// 0,1,2,....7
// ptr: Pointer to pattern data
//
//Usage:
// pattern[8]={0x04,0x0E,0x0E,0x0E,0x1F,0x00,0x04,0x00};
// LCD_build(1,pattern);
//
//LCD Ports are same as discussed in previous sections

void LCD_build(unsigned char location, unsigned char *ptr){
unsigned char i;
if(location<8){
LCD_command(0x40+(location*8));
for(i=0;i<8;i++)
LCD_senddata(ptr[ i ]);
}
}

LCD Programming - Sending command and Data

►Sending Commands to LCD

To send commands we simply need to select the command register. Everything is same as we have done in the initialization routine. But we will summarize the common steps and put them in a single subroutine. Following are the steps:
• Move data to LCD port
• select command register
• select write operation
• send enable signal
• wait for LCD to process the command

Keeping these steps in mind we can write LCD command routine as.

CODE:
;Ports used are same as the previous example
;Routine to send command to LCD

LCD_command:
mov LCD_data,A ;Move the command to LCD port
clr LCD_rs ;Selected command register
clr LCD_rw ;We are writing in instruction register
setb LCD_en ;Enable H->L
clr LCD_en
acall LCD_busy ;Wait for LCD to process the command
ret ;Return from busy routine

; Usage of the above routine
; A will carry the command for LCD
; e.g. we want to send clear LCD command
;
; mov a,#01H ;01H is command for clearing LCD
; acall LCD_command ;Send the command


The equivalent C code Keil C compiler. Similar code can be written for SDCC.
CODE:
void LCD_command(unsigned char var)
{
LCD_data = var; //Function set: 2 Line, 8-bit, 5x7 dots
LCD_rs = 0; //Selected command register
LCD_rw = 0; //We are writing in instruction register
LCD_en = 1; //Enable H->L
LCD_en = 0;
LCD_busy(); //Wait for LCD to process the command
}
// Using the above function is really simple
// var will carry the command for LCD
// e.g.
//
// LCD_command(0x01);



Setting cursor position on LCD
To set the cursor position on LCD, we need to send the DDRAM address...
CODE:
Bit7 6 5 4 3 2 1 0
1 AD6 AD5 AD4 AD3 AD2 AD1 AD0


The seventh bit is always 1, and bit 0 to 7 are DDRAM address (See the introduction section of LCD). so if you want to put the cursor on first position the address will be '0000000B' in binary and 7th bit is 1. so address will be 0x80, so for DDRAM all address starts from 0x80.

For 2 line and 16 character LCD. The adress from 0x80 to 0x8F are visible on first line and 0xC0 to 0xCF is visible on second line, rest of the DDRAM area is still available but is not visible on the LCD, if you want to check this thing, then simply put a long sting greater than 16 character and shift the entire display, you will see all the missing character coming from the back.. this way you can make scrolling line on LCD (see more on shifting display in commands section).

Below is an example for setting cursor position on LCD.
CODE:
;We are placing the cursor on the 4th position
;so the DDRAM address will be 0x03
;and the command will be 0x80+0x03 = 0x83
mov a,#83H ;load the command
acall LCD_command ;send command to LCD

CODE:
// to do the same thing is C
// as we done before
LCD_command(0x83);



►Sending Data to LCD

To send data we simply need to select the data register. Everything is same as the command routine. Following are the steps:
• Move data to LCD port
• select data register
• select write operation
• send enable signal
• wait for LCD to process the data

Keeping these steps in mind we can write LCD command routine as.

CODE:
;Ports used are same as the previous example
;Routine to send data (single character) to LCD

LCD_senddata:
mov LCD_data,A ;Move the command to LCD port
setb LCD_rs ;Selected data register
clr LCD_rw ;We are writing
setb LCD_en ;Enable H->L
clr LCD_en
acall LCD_busy ;Wait for LCD to process the data
ret ;Return from busy routine

; Usage of the above routine
; A will carry the character to display on LCD
; e.g. we want to print A on LCD
;
; mov a,#'A' ;Ascii value of 'A' will be loaded in accumulator
; acall LCD_senddata ;Send data


The equivalent C code Keil C compiler. Similar code can be written for SDCC.
CODE:
void LCD_senddata(unsigned char var)
{
LCD_data = var; //Function set: 2 Line, 8-bit, 5x7 dots
LCD_rs = 1; //Selected data register
LCD_rw = 0; //We are writing
LCD_en = 1; //Enable H->L
LCD_en = 0;
LCD_busy(); //Wait for LCD to process the command
}
// Using the above function is really simple
// we will pass the character to display as argument to function
// e.g.
//
// LCD_senddata('A');


Now you have seen that its really easy to send command and data to LCD. Now what if we have a string to send to LCD? how we are going to do that?

Is simple, we will store the LCD string in the ROM of controller and call the string character by character. A simple exmple is shown below.

CODE:
;Sending string to LCD Example

LCD_sendstring:
clr a ;clear Accumulator for any previous data
movc a,@a+dptr ;load the first character in accumulator
jz exit ;go to exit if zero
acall lcd_senddata ;send first char
inc dptr ;increment data pointer
sjmp LCD_sendstring ;jump back to send the next character
exit:
ret ;End of routine

; Usage of the above routine
; DPTR(data pointer) will carry the address
; of string to send to LCD.
; e.g. we want to print "LCD Tutorial" on LCD then
;
; mov dptr,#my_string ;my_string is the label where the string is stored
; acall LCD_sendstring ;Send string
;
; To store a string..
; my_string:
; DB "LCD Tutorial", 00H
; 00H indicate that string is finished.


The equivalent C code Keil C compiler. Similar code can be written for SDCC.
CODE:
void LCD_sendstring(unsigned char *var)
{
while(*var) //till string ends
LCD_senddata(*var++); //send characters one by one
}
// Using the above function is really simple
// we will pass the string directly to the function
// e.g.
//
// LCD_sendstring("LCD Tutorial");




Now we are ready with sending data and sending command to LCD. Now the last and final section which is creating custom characters or patterns to display on LCD. Please proceed to the next section to read more.

LCD interfacing with Microcontrollers tutorial - Checking busy flag

►Reading the busy Flag

As discussed in the previous section, there must be some delay which is needed to be there for LCD to successfully process the command or data. So this delay can be made either with a delay loop of specified time more than that of LCD process time or we can read the busy flag, which is recomended. The reason to use busy flag is that delay produced is almost for the exact amount of time for which LCD need to process the time. So is best suited for every application.


Steps to read busy flag

when we send the command, the BF or D7th bit of the LCD becomes 1 and as soon as the command is processed the BF = 0. Following are the steps to be kept in mind while reading the Busy flag.
• Select command register
• Select read operation
• Send enable signal
• Read the flag

So following the above steps we can write the code in assembly as below...

CODE:
;Ports used are same as the previous example

LCD_busy:
setb LCD_D7 ;Make D7th bit of LCD data port as i/p
setb LCD_en ;Make port pin as o/p
clr LCD_rs ;Select command register
setb LCD_rw ;we are reading
check:
clr LCD_en ;Enable H->L
setb LCD_en
jb LCD_D7,check ;read busy flag again and again till it becomes 0
ret ;Return from busy routine


The equivalent C code Keil C compiler. Similar code can be written for SDCC.
CODE:
void LCD_busy()
{
LCD_D7 = 1; //Make D7th bit of LCD as i/p
LCD_en = 1; //Make port pin as o/p
LCD_rs = 0; //Selected command register
LCD_rw = 1; //We are reading
while(LCD_D7){ //read busy flag again and again till it becomes 0
LCD_en = 0; //Enable H->L
LCD_en = 1;
}
}


The above routine will provide the necessary delay for the instructions to complete. If you dont want to read the busy flag you can simply use a delay routine to provide the a specific ammount of delay. A simple delay routine for the LCD is given below.
CODE:
LCD_busy:
mov r7,#50H
back:
mov r6,#FFH
djnz r6,$
djnz r7,back
ret ;Return from busy routine


CODE:
void LCD_busy()
{
unsigned char i,j;
for(i=0;i<50;i++) //A simple for loop for delay
for(j=0;j<255;j++);
}

Now we are ready with the initialization routine and the busy routine for LCD. In the next section we will see how to send data and command to the LCD.

LCD Initialization (Programming)


CODE:
LCD_data equ P2 ;LCD Data port
LCD_D7 equ P2.7 ;LCD D7/Busy Flag
LCD_rs equ P1.0 ;LCD Register Select
LCD_rw equ P1.1 ;LCD Read/Write
LCD_en equ P1.2 ;LCD Enable

LCD_init:
mov LCD_data,#38H ;Function set: 2 Line, 8-bit, 5x7 dots
clr LCD_rs ;Selected command register
clr LCD_rw ;We are writing in instruction register
setb LCD_en ;Enable H->L
clr LCD_en
acall LCD_busy ;Wait for LCD to process the command
mov LCD_data,#0FH ;Display on, Curson blinking command
clr LCD_rs ;Selected instruction register
clr LCD_rw ;We are writing in instruction register
setb LCD_en ;Enable H->L
clr LCD_en
acall LCD_busy ;Wait for LCD to process the command
mov LCD_data,#01H ;Clear LCD
clr LCD_rs ;Selected command register
clr LCD_rw ;We are writing in instruction register
setb LCD_en ;Enable H->L
clr LCD_en
acall LCD_busy ;Wait for LCD to process the command
mov LCD_data,#06H ;Entry mode, auto increment with no shift
clr LCD_rs ;Selected command register
clr LCD_rw ;We are writing in instruction register
setb LCD_en ;Enable H->L
clr LCD_en
acall LCD_busy ;Wait for LCD to process the command
ret ;Return from routine


Now we can do the same thing in C, I am giving example using Keil C. Similar code can be written for SDCC.
CODE:
#include .
#define LCD_data P2
#define LCD_D7 P2_7
#define LCD_rs P1_0
#define LCD_rw P1_1
#define LCD_en P1_2

void LCD_init()
{
LCD_data = 0x38; //Function set: 2 Line, 8-bit, 5x7 dots
LCD_rs = 0; //Selected command register
LCD_rw = 0; //We are writing in data register
LCD_en = 1; //Enable H->L
LCD_en = 0;
LCD_busy(); //Wait for LCD to process the command
LCD_data = 0x0F; //Display on, Curson blinking command
LCD_rs = 0; //Selected command register
LCD_rw = 0; //We are writing in data register
LCD_en = 1; //Enable H->L
LCD_en = 0;
LCD_busy(); //Wait for LCD to process the command
LCD_data = 0x01; //Clear LCD
LCD_rs = 0; //Selected command register
LCD_rw = 0; //We are writing in data register
LCD_en = 1; //Enable H->L
LCD_en = 0;
LCD_busy(); //Wait for LCD to process the command
LCD_data = 0x06; //Entry mode, auto increment with no shift
LCD_rs = 0; //Selected command register
LCD_rw = 0; //We are writing in data register
LCD_en = 1; //Enable H->L
LCD_busy();
}



With the help of the above code, you are able to initialize the LCD.

Programming for Bipolar Stepper Motor



Programming for Bipolar Stepper Motor

►C Programming
CODE:
void main(){
while(1){
stepper = 0x08;
delay();
stepper = 0x02;
delay();
stepper = 0x04;
delay();
stepper = 0x01;
delay();
}
}



►Assembly Programming
CODE:
main:
mov stepper, #08H
acall delay
mov stepper, #02H
acall delay
mov stepper, #04H
acall delay
mov stepper, #01H
acall delay
sjmp main







Stepper Motor interfacing with Microcontrollers: Step Sequence

Stepper motors can be driven in two different patterns or sqeunces. namely,
• Full Step Sequence
• Half Step Sequence
we will go through these sequences one by one.


►Full Step Sequence

In the full step sequence, two coils are energized at the same time and motor shaft rotates. The order in which coils has to be energized is given in the table below.
Full Mode Sequence
Step A B A\ B\
0 1 1 0 0
1 0 1 1 0
2 0 0 1 1
3 1 0 0 1


►Half Step Sequence

In Half mode step sequence, motor step angle reduces to half the angle in full mode. So the angualar resolution is also increased i.e. it becomes double the angular resolution in full mode. Also in half mode sequence the number of steps gets doubled as that of full mode. Half mode is usually preffered over full mode. Table below shows the pattern of energizing the coils.



Half Mode Sequence
Step A B A\ B\
0 1 1 0 0
1 0 1 0 0
2 0 1 1 0
3 0 0 1 0
4 0 0 1 1
5 0 0 0 1
6 1 0 0 1
7 1 0 0 0


►Step Angle

Step angle of the stepper motor is defined as the angle traversed by the motor in one step. To calculate step angle,simply divide 360 by number of steps a motor takes to complete one revolution. As we have seen that in half mode, the number of steps taken by the motor to complete one revolution gets doubled, so step angle reduces to half.


As in above examples, Stepper Motor rotating in full mode takes 4 steps to complete a revolution, So step angle can be calculated as...

Step Angle ø = 360° / 4 = 90°

and in case of half mode step angle gets half so 45°.

So this way we can calculate step angle for any stepper motor. Usually step angle is given in the spec sheet of the stepper motor you are using. Knowing stepper motor's step angle helps you calibrate the rotation of motor also to helps you move the motor to correct angular position.

►Step Sequence for 2-wire control of Unipolar stepper motor

As seen in above explanation, In every step of the sequence, two wires are always set to opposite polarities. Because of this, it's possible to control steppers with only two wires instead of four, with a slightly more complex circuit. The stepping sequence is the same as it is for the two coils A and B, and the opposite polarity value is given to A\ and B\. The sequence is given in the table below:
2-wire Mode Sequence
Step A B
0 0 1
1 1 1
2 1 0
3 0 0

►Step Sequence for Bipolar stepper motor

Bipolar motor has simpler construction. It has two windings with no center taps and a permanent magnet at the center just like unipolar stepepr motors. Being simpler in contruction, the stepping sequence is a little complex, as the power for both the coils has to be controlled in such a way that the polarity of the poles get reversed. This polarity sequence is shown in the table below.
Polarity Sequence
Step A A\ B B\
0 +ve -ve -ve -ve
1 -ve -ve +ve -ve
2 -ve +ve -ve -ve
3 -ve -ve -ve +ve

The above polarity sequence can be interpreted in terms of logic levels for microcontroller by activating one coil at a time as shown in the table below.
Step Sequence
Step A A\ B B\
0 1 0 0 0
1 0 0 1 0
2 0 1 0 0
3 0 0 0 1

We have now learnt most of the necessary things regarding a stepper motor. In the next section we will discuss about the various techniques to interface a stepper motor.

Programming for 2-wire connection of Unipolar Stepper Motor

►Programming for 2-wire connection of Unipolar Stepper Motor

►C Programming
CODE:
void main(){
while(1){
stepper = 0x03;
delay();
stepper = 0x01;
delay();
stepper = 0x00;
delay();
stepper = 0x02;
delay();
}
}



►Assembly Programming
CODE:
main:
mov stepper, #03H
acall delay
mov stepper, #01H
acall delay
mov stepper, #00H
acall delay
mov stepper, #02H
acall delay
sjmp main


Stepper Motor interfacing with Microcontrollers: Programming Stepper motor




►Programming Full step Sequence

►C Programming
I am assuming that stepper motor is connected at Port 1.0 to Port 1.3. Adjusting the delay will increase or decrease the speed of the motor. Here just for demonstration i have taken some delay, you can change it as you want.

[Tip: Do testing.. ]



CODE:
#include .
#define stepper P1
void delay();

void main(){
while(1){
stepper = 0x0C;
delay();
stepper = 0x06;
delay();
stepper = 0x03;
delay();
stepper = 0x09;
delay();
}
}

void delay(){
unsigned char i,j,k;
for(i=0;i<6;i++)
for(j=0;j<255;j++)
for(k=0;k<255;k++);
}



►Assembly Programming
CODE:
org 0H

stepper equ P1

main:
mov stepper, #0CH
acall delay
mov stepper, #06H
acall delay
mov stepper, #03H
acall delay
mov stepper, #09H
acall delay
sjmp main

delay:
mov r7,#4
wait2:
mov r6,#0FFH
wait1:
mov r5,#0FFH
wait:
djnz r5,wait
djnz r6,wait1
djnz r7,wait2
ret
end


►Programming Half step Sequence

►C Programming
Just the main routine changes rest everything remains same, i mean same delay routine.

CODE:
void main(){
while(1){
stepper = 0x08;
delay();
stepper = 0x0C;
delay();
stepper = 0x04;
delay();
stepper = 0x06;
delay();
stepper = 0x02;
delay();
stepper = 0x03;
delay();
stepper = 0x01;
delay();
stepper = 0x09;
delay();
}
}



►Assembly Programming
Here also the main routine changes rest everything remains same.

CODE:
main:
mov stepper, #08H
acall delay
mov stepper, #0CH
acall delay
mov stepper, #04H
acall delay
mov stepper, #06H
acall delay
mov stepper, #02H
acall delay
mov stepper, #03H
acall delay
mov stepper, #01H
acall delay
mov stepper, #09H
acall delay
sjmp main

Serial Communication Tutorial



►Introduction

UART (Universal Asynchronous Receiver Transmitter) or USART (Universal Synchronous Asynchronous Receiver Transmitter) are one of the basic interface which you will find in almost all the controllers available in the market till date. This interface provide a cost effective simple and reliable communication between one controller to another controller or between a controller and PC.


►RS-232 Basics

RS-232 (Recommended Standard 232) is a standard for serial binary data signals connecting between a DTE (Data terminal equipment) and a DCE (Data Circuit-terminating Equipment).
Voltage Levels:
The RS-232 standard defines the voltage levels that correspond to logical one and logical zero levels. Valid signals are plus or minus 3 to 25 volts. The range near zero volts is not a valid RS-232 level; logic one is defined as a negative voltage, the signal condition is called marking, and has the functional significance of OFF. Logic zero is positive, the signal condition is spacing, and has the function ON.
So a Logic Zero represented as +3V to +25V and Logic One represented as -3V to -25V.

►RS-232 Level Converters

Usually all the digial ICs works on TTL or CMOS voltage levels which cannot be used to communicate over RS-232 protocol. So a voltage or level converter is needed which can convert TTL to RS232 and RS232 to TTL voltage levels.
The most commonly used RS-232 level converter is MAX232. This IC includes charge pump which can generate RS232 voltage levels (-10V and +10V) from 5V power supply. It also includes two receiver and two transmitters and is capable of full-duplex UART/USART communication.


Chapter 3

Lets Learn AVR - Step 1:

In this part of tutorial you will learn
1. Reading from Program memory
2. Reading from RAM
3. Writing to RAM
4. Practice programs

Here is the summary of Load and store instructions that are used for dealing with SRAM of AVR Microcontroller
i. LD Rn,X/Y/Z
>either X or Y or Z register can be used
>this will load the value which is stored in memory location pointed by register X/Y/Z to the destination register Rn (can be R0, R1.. any etc)
ii. LD Rn,X+/Y+/Z+
>This instruction will load the value which is stored in memory at location pointed by X/Y/Z registers and then increment the memory location by 1
>This is a post increment instruction
iii. LD Rn, -X/-Y/-Z
>Load Rn with value stored at location pointed by pre-decrement of address stored in X/Y/Z
iv. LDD Rn,Y/Z+displacement
>Load Rn with value at address Z or Y + displacement
>e.g. Z is 0x0090, Displacement is 0x10 so Rn will be loaded with value stored at 0x0090+0x10 = 0x0100
v. ST X/Y/Z, Rn
>Store the value of Rn to location pointed by X or Y or Z
vi. ST X+/Y+/Z+, Rn
>Store the value in Rn to location pointed by X or Y or Z and increment the address pointer
vii. STD Y/Z+displacement, Rn
>Store the value in Rn to location pointed by Y or Z + Displacement
viii. LDS Rn, SRAM_Address
>Load value from SRAM Address to the Rn register
>SRAM Address is the immediate value e.g. LDS R0,0x0100
ix. STS SRAM_Address, Rn
>Store Rn to immediate SRAM location


To read from Program memory we have special instructions like
i. LPM
>Load form program memory, This instruction is used in most of the AVRs and its hard coded in the architecture.
>This instruction will load R0 with the address specified by register Z [This is hardcoded]
ii. LPM Rn, Z
>Load Rn from program memory pointed by register Z
>This instruction is not supported by all AVRs e.g ATMega8515, AT90S8515
iii. LPM Rn,Z+
>Load Rn from program memory and increment the memory location pointed by Z
>This instruction is also not supported by all AVRs


Note: load from program memory instructions are not supported by all AVR architectures. Most of the architectures support LPM instruction which is hard coded to load R0 from location in Z. where as in some AVR this is also not implemented.

Now we are done with the instructions overview.. now lets practice them..



Program 1: Copy 10 Bytes memory block stored in Program memory(ROM) to Data memory (SRAM)
CODE:
;This program is to copy memory block from Program
;memory to AVR RAM (10 Bytes)

.include "8515def.inc"

.org $0
.def Temp = R0 ;Temprary variable
.def count = R17 ;Byte Count

ldi ZH,HIGH(2*data) ;Load Z with address where
ldi ZL,LOW(2*data) ;our data is stored
ldi XL,$60 ;Load Destination RAM location
ldi XH,$0
ldi count,$A ;Load count 10 Bytes
again:
lpm ;Load value from program memory
inc ZL ;Increment memory location
st X+,Temp ;Store byte to the RAM location
dec count ;Decrement Count
brne again ;Check if all bytes moved
end:
rjmp end ;End of program

;Our data which we will copy from Program Memory to RAM
Data:
.db $10,$20,$30,$40,$50,$60,$70,$80,$90,$95

In the above code.. you can see while loading the address of program memory location, i multiplied it with 2, i.e. LDI ZH,High(2*Data)
The reason is, the program memory is organized in word manner i.e. two bytes for each command, So the address has to be multiplied by 2. You can try running these programs and see its working in the Simulator of AVR Studio.





Program 2: Find Greatest of 3 numbers Stored in program memory
CODE:
;Program to find greatest of 3 numbers
;
;numbers are stored in ROM and the final
;result will be stored in a register

.include "8515def.inc"

.def num1 = r0 ;Location for First number
.def num2 = r1 ;Location for second number
.def answer = R2 ;location for Final answer

.org $0
ldi ZL, Low(2*Data) ;Load the program memory address
ldi ZH, High(2*data)

lpm ;Load first number
mov num2,num1 ;Move it to num2
inc ZL ;Increment the address
lpm ;Load second number
cp num1,num2 ;Compare them
brlt next ;Jump if num1 mov num2,num1 ;If num1>num2 then move num1
next: ; to num2 location
inc ZL ;Increment the address
lpm ;Load the third number
cp num1,num2 ;Again compare
brlt final ;Check if num1 mov answer,num1 ;If No, then num1 is answer
final:
mov answer,num2 ;If Yes, then num2 is our answer
end:
rjmp end ;End of program


Data: ;Our 3 numbers
.db $23,$23,$14 ;Try changing them and see results






Program3: Program to swap two numbers

.include "8515def.inc"

.def temp = R16 ;Temporary register
.def num1 = R17 ;Number one location
.def num2 = R18 ;Location for second number

.cseg
.org $0

ldi ZH,0x00 ;Assuming the two numbers are stored
ldi ZL,0x90 ;at location 0x0090 in RAM
ld num1,Z+ ;Load first number
ld num2,Z ;Load second number
mov temp,num1 ;copy num1 to temp
mov num1,num2 ;copy num2 to num1
mov num2,temp ;copy temp to num2
ldi ZL,0x90 ;load the RAM location back
st Z+,num1 ;store the first number
st Z, num2 ;store the second number
end:
rjmp end ;end of prog





Program 4: Find Greatest of 5 numbers
CODE:
;Program to find greatest of 5 numbers
;
;numbers are stored in ROM and the final
;result will be stored in a register

.include "8515def.inc"

.def num1 = r0 ;Location for First number
.def num2 = r1 ;Location for second number
.def answer = R2 ;location for Final answer
.def count = r16 ;Count

.org $0
ldi ZL, Low(2*Data) ;Load the program memory address
ldi ZH, High(2*data)
ldi count,4 ;Load count
lpm ;Load first number
mov num2,num1 ;Move it to num2
adiw Z,1
again:
lpm ;Load second number
cp num1,num2 ;Compare them
brlt next ;Jump if num1 mov num2,num1 ;If num1>num2 then move num1
next: ; to num2 location
adiw Z,1 ;Increment the address
dec count ;are we done with all 5 numbers?
brne again ;if no then jump back
final:
mov answer,num2 ;If Yes, then num2 is our answer
end:
rjmp end ;End of program

;Try changing them and see results
Data: ;Our 5 numbers
.db $3,$23,$14,$50,$20,$0
;last zero is to aligh the bytes in word manner
;we have 5 numbers so i am padding with a 0 to make it
;even. if we dont't do this.. compiler will do it
;automatically to prevent misalignment.





Program5 Copy memory block from RAM to RAM
CODE:
;Program to copy a block of memory (10 Bytes)
; from one RAM location to another RAM location.

.include "8515def.inc"

.def temp = r0
.def count = r16

.org 0
ldi ZL,0x60 ;Lets fill the RAM with some numbers
ldi ZH,0x00 ;to copy, so we can check is it working
ldi count,10 ;Load count
fill:
st Z+,count ;Store value to RAM location
dec count
brne fill

ldi ZL,0x60 ;Load memory location to copy from
ldi ZH,0x00
ldi YL,0x90 ;Load destination memory location
ldi YH,0x00
ldi count,10 ;Load count

copy:
ld temp,Z+ ;Load value to temporary register
st Y+,temp ;Store it to location
dec count ;decrement counter
brne copy

end:
rjmp end ;End of program...




Program 6: Sorting of 10 numbers
CODE:
;Program to sort 10 numbers
;in ascending order
;10 numbers are stored in ROM
;and sorted answer is stored in RAM
;at location 0x0060

.include "8515def.inc"

.def count1 = r17 ;First Count
.def count2 = r18 ;Second count
.def temp = r0 ;Temp reg for swap
.def num1 = r1 ;Num 1
.def num2 = r2 ;Num 2

.cseg ;Code segment starts
.org 0
ldi Zh,high(2*mydata) ;Load memory add where
ldi Zl,low(2*mydata) ;data is stored in ROM
ldi Yh,0x00 ;Destination location
ldi Yl,0x60 ;0x0060 in RAM
ldi count1,10 ;Load count

copy:
lpm ;Load from program mem
st Y+,temp ;Store it to RAM
adiw Z,1 ;Increment Z
dec count1 ;Decrement counter
brne copy ;copy all 10 bytes

ldi ZH,0x00 ;Load first pointer
ldi ZL,0x60
ldi count1,10 ;Load counter for it

sort:
mov YL,ZL ;Load second pointer
mov YH,ZH
adiw Y,1 ;Increment it
ld num1,Z ;load first number
mov count2,count1 ;load count2
subi count2,1 ;Count2 = Count1-1
breq end ;end if last num

again:
ld num2,Y ;Load second number
cp num1,num2 ;Compare them
brlo next ;Jump if num1 mov temp,num2
mov num2,num1 ;If num1>num2 then swap
mov num1,temp
st Z,num1 ;and store them on their
st Y,num2 ;respective locations
next:
adiw Y,1 ;Increment the address
dec count2 ;dec count2 for Y pointer
brne again ;check if count is zero
adiw Z,1 ;increment Z pointer
dec count1 ;Dec count1 for Z pointer
brne sort ;finish if zero

end:
rjmp end ;End of program

mydata:
.db $10,$90,$50,$20,$91,$23,$55,$62,$39,$80





Program 7: Clear SRAM area from 0x60 to RAMEND
CODE:
;Program to clear RAM
;this program is for ATMega8515 So ram area is from
;0x60 to RAMEND

.include "8515def.inc"

.def temp = r16 ;Temporary variable
.def cnth = r25 ;Counter High byte
.def cntl = r24 ;Counter Low byte

.org 0

ldi cnth,0x02 ;Load count 0x200 to clear
ldi cntl,0x00 ;memory from 0x00 to 0x1FF
clr temp ;clear temp
ldi ZH,0x00 ;Load starting address of
ldi ZL,0x60 ;RAM 0x0060 in Z pointer

clrram:
st Z+,temp ;Store 0 in current Z location
sbiw cnth:cntl,1 ;Decrement the counter
brne clrram ;end if zero

end:
rjmp end ;End of program


Chapter 2

Note: The program i wrote is for ATMEGA8515 and there is not much difference in other AVRs except some has extra features.


Before programming AVR, first you need to know few important programming tips and AVR registers.
CODE:
.include "8515def.inc"

This include the definition file for ATmega8515, so that we can directly address the registers with their names.
All AVRs have 32 general purpose registers from R0 - R32.
R0-R15 registers have certain restrictions of use, i.e. they are not allowed to load immediate value.
e.g. LDI R15, $35
the above statement will give you an error, saying "Invalid Register"
Where as registers from R16-R32 can be used for this purpose i.e.
LDI R16,$35
is a valid statement.
You can move values from one register to other by using
MOV R15,R16

Not only the registers from R0 to R15 has restriction on LDI but also other commands where the use of R0-R16 is not allowed. usually all commands having immediate operands are not allowed.
For the ease of programming, you can also give names to the register like
CODE:
.def myregister = R16


There are some special registers like X,Y and Z for 16-Bit operations. They are used to read and write to XRAM. They are also used for reading from program memory like reading from a lookup table (we will discuss about them when we use them). These special registers are actually combination of two 8-bit General purpose registers. i.e. X is actually R26:R27, Y is R28:R29 and Z is R30:R31.
The lower byte of the 16-bit-adress is located in the lower register, the higher byte in the upper register. Both parts have their own names, e.g. the higher byte of Z is named as ZH (R31) and the lower Byte is ZL (R30). Similarly for X (XL and XH) and for Y (YL and YH). These names are defined in the standard header file for the chips (which we include while writing program). Dividing these 16-bit-pointer-names into two different bytes is done like follows:
CODE:
LDI YH,HIGH(LABEL) ; Set the MSB
LDI YL,LOW(LABEL) ; Set the LSB

where LABEL is address for any lookup table or any memory location.

Some important notes for using registers
1. Define names for registers with the .DEF directive, never use them with their direct name Rx. This helps you making better use of registers and you will never confuse yourself while using them.
2. If you need pointer access reserve R26 to R31 for that purpose.
3. 16-bit-counter are best located R25:R24.
4. If you need to read from the program memory, e.g. fixed tables, reserve Z (R31:R30) and R0 for that
purpose.
5. If you plan to have access to single bits within certain registers (e.g. for testing flags), use R16 to
R23 for that purpose


Now coming to ports, The information about a specific port of a certain type of AVR can be easily obtained in the AVR Datasheet. Port names are defined in the include file of the CPU..
if you don't have an include file then you can define yourself as..
CODE:
.equ PORTA = $1B ;incase of ATmega8515

So if you are not able to find an include file you can use the .EQU directive to define ports and other registers.
Making port as i/p or o/p is purely dependent on data direction register called DDRx (DDRA for port A etc.) The DDxn bit in the DDRx Register selects the direction of this pin. If DDxn is written
logic one, Pxn is configured as an output pin. If DDxn is written logic zero, Pxn is configured
as an input pin.
for writing and reading data to Ports, PORTx registers are there. and to read from ports PINx registers are there.
for example..
writing to port
CODE:
.def output = R16
LDI output, $FF
OUT DDRA, output ; making as o/p
LDI output, $00
OUT PORTA, output ; clear all PORTA pins

Reading from port
CODE:
.def input = R17
LDI input, $00
OUT DDRA,input
IN input, PINA



We are finished with the basics of AVR, lets try programming with simplest program. Blinking an LED!
CODE:
.include "8515def.inc" ;Include file

RJMP MAIN ;Reset vector

MAIN:
ldi R16,low(RAMEND) ;Load stack with
out SPL,R16 ;RAMEND - highest value
ldi R16,high(RAMEND) ;of internal SRAM
out SPH,R16
SBI DDRA,0 ;Make PORTA Pin 0 as o/p

DO:
SBI PORTA,0 ;Set Pin 0 of PORTA
RCALL DELAY ;Wait for some time
CBI PORTA,0 ;Cleare Pin 0 of PORTA
RCALL DELAY ;Wait for some time
RJMP DO ;Forever loop!

DELAY: ;The delay routine
LDI R16,$20 ;Load some delay value
LOOP1:
SER R17 ;Make R17 as $FF
LOOP:
DEC R17 ;Decrement R17
BRNE LOOP ;Jump if not zero
DEC R16 ;Decrement R16
BRNE LOOP1 ;Jump if not zero
RET ;Return

chapter 1


The first step in the development of any micrcontroller is to know about its architecture and instruction set. So i advice you all to keep a copy of instruction set and architecture manual. You can download them from the link below.
AVR Instruction Set
Architecture Manual or Datasheet for your AVR Microcontroller

The above document will help you to get familiar with the instruction set and to know about your AVR in a better way.
After this, we now have to decide the IDE on which you are going to work or write program for your AVR. I advice you to use AVR Studio 4 from Atmel Corporation, which is a free IDE for AVR and it has many features like programming, debugging etc.
You can get your free copy of AVR studio from link below.
AVR Studio 4.12 (build 460) (45 MB, updated 11/05) - Registration needed
AVR Studio 4.12 Service Pack 4 (build 498) (25 MB, updated 10/06) - No registration
AVR Studio 4.13 (build 528) (73 MB, updated 03/07) - Registration needed

So you can download any of the copy above as per your need more information about AVR studio can be obtained from
AVR Studio 4

All documents and IDE for writing program is ready.. only thing left is a programmer, with which we are going to program our AVR. As we know almost all AVR comes with ISP (In System Programmable) ports. So you don't need any special hardware to program your AVR. Please see the link below for ISP programmer for AVR.
Download PonyProg - serial device programmer

Note: Do not forget to run the setup after installing PonyProg.

Setup Information:
In interface setup, select parallel and then from the drop down select AVR ISP I/O.
slect the LPT Port (parallel port) available on your PC. Then click ok!

To load Hex file:
Go to File-> Open Program (FLASH) file
then from the drop down where ".e2p" is show, select ".hex" and load your hex file.

General properties of AVR microcontrollers

This applies to all types of AVR microcontrollers because they all use same AVR core technology. So lets go through some of them:
• Ability to work at 1MIPS/MHz (MIPS – Millions Instructions Per Second);
• On chip Flash memory (now tiny series have up to 8kB, mega up to 256kB) with write cycle not less than 1000 times;
• On chip RAM memory (tiny series up to 512B, mega up to 8kB);
• On chip EEPROM memory (tiny series up to 512B, mega up to 4kB) with write cycle not less than 100000 times;
• Working frequencies up to 20MHz;
• Protection fuses from unintentional reading and modification of program and data memories;
• Ability to program in system – directly on board using ISP cable;
• Four types of clock generators: Internal generator, RC generator of clock cycles, Internal generator with external crystal resonator, and External synchronisation;
• Two or three power down modes;
• Some microcontroller can work at very low voltages like 1.8V

Other internal processor properties:
• Static architecture where minimal frequency equal to zero;
• ALU is connected directly to general purpose registers;
• Most of commands are performed in one clock cycle;
• Multilevel system of interrupts;
• Many sources of interrupts – two of them are external;
• Three level hardware stack.

I/O system properties:
• Software configurable and selection of I/O ports;
• Ports can be programmed as inputs and outputs independently to each other;
• Input buffers uses Smith triggers on all pins;
• Support of Internal Pull-Up resistors (about 35 to 120kOhm)

Peripheral devices vary from device to device. Refer to datasheets to find out what peripherals are included in concrete model. In general we can mention:
• 8 bit timer counter with pre-scaler;
• 16 bit timer counter with pre-scaler ;
• Watch dog timer;
• PWM generator;
• Analog comparator;
• ADC;
• Hardware modulator;
• SPI interface;
• TWI (I2C) interface;
• UART

Why AVR Microcontroller?

AVR microcontroller are 8-bit on chip system with RICS (Reduced Instruction Set Computer) command system. The most of commands are performed in one clock cycle.

In AVR microcontroller reading next command is done during execution of previous command. So the overal number of commands in 1second is almos equal to working frequency.

AVR microcontrollers are manufactured using high quality CMOS technology, contains EEPROM memory inside the chip and differs from other microcontrollers that they require less power in higher frequencies.

Firmware and data can be uploaded directly on board using ISP programming capability.

Going further it is necessary to mention that AVR microcontroller have 32 general purpose registers. PIC's have one general purpose register so called accumulator. And comparing to similar PIC microcontrollers AVR's have more advanced architecture that allows to run one instruction per clock cycle while PIC microcontrollers run one instruction in 4 clock cycles. This doenst apply to more advanced PIC's.

As someone said: “If you want to make LED flasher – any microcontroller wil do je job, but if you want the best results in extreme situations – choose AVR”.