.

INCLUDES RFID interfacing with avr GPS interfacing with avr RF Module interfacing with avr Stepper Motor With AVR
Showing posts with label AVR Tips. Show all posts
Showing posts with label AVR Tips. Show all posts

Tuesday, December 20, 2011

CONVERSIONS

Converting Int to ASCII Coded Decimal
Converting integers to ASCII coded decimal is pretty simple. To understand how it is done, you first have to think about how the numbers we're using in written documents are built up:
Let's take the number 233 and rip it apart:
Multiply number with
100
10
1
decimal
2
3
3
result
200
30
3
These add up to 233. The number consists of single digits which specifiy the number of hundreds, tens, and ones we add together.
To get these results, we need to divide the number; first by 100, then by 10 and then by 1. As the AVR doesn't have a divide instruction, this has to be done manually:
Divide by 100:
- copy the number into a temporary register
- compare the number with 100
- if greater or equal, increase the hundreds count and subtract 100 from the temporary register
- go to the compare again
When this is done, the number in the temporary register is lower than 100. Now we can proceed with 10s and 1s. Instead of dividing it by 1 we can just copy the remaining number to the register that holds the ones.
Unfortunately, this is not enough to convert a number to decimal coded ASCII. In an ASCII table we can see that '0' is 0x30. So we add 0x30 to the single digits (hundreds, tens, ones) and can now print it on the screen (via UART, USB, LCD interface, whatever).
It's now also possible to reformat the number, delete characters we don't need (print a space instead of 0 hundreds if the number was lower than 100) or add additional characters in between.
Here's a flow chart of how the conversion can be done:

It should be pretty easy for you to write the code for this yourself.
Doing this with a 16-bit number is just the same, but with 5 digits and 16-bit compares. The code space needed (as well as cpu time) is 40% bigger. If you have a lot of free program space, you can build up a case-like structure to do the conversion: If the number is greater than 200, the hundreds counter is loaded with 2 and 200 is subtracted from the original number. This is faster but requires more space. It's up to you.

Converting Int to ASCII Coded Hex
This conversion is a bit more difficult than int to ASCII coded decimal, as you don't only have to display numbers, but characters as well. In the ASCII table, these are not found directly after the numbers.
However, the first task is to load the two nibbles that make up to an 8 bit integer into separate registers:

The reason why you have to swap the nibbles in reg A is that the register holding the high nibble should have a value between 0x00 and 0x0F (15). If we didn't swap the nibbles, their value would be 0x00..0xF0 which we can't convert to ASCII.
Now we have two nibbles, each in a separate register, which are between 0x00 (0) and 0x0F (15). These must now be converted into their ASCII representative: For 0 it's '0', for 10 it's 'A' and for 15 it's 'F'. This can be done with a lookup table or by using a case structure.
A lookup table for this would have the ASCII values of the possible nibble values at the nibble positions:
Table Position:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Nibble value:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ASCII:
'0'
'1'
'2'
'3'
'4'
'5'
'6'
'7'
'8'
'9'
'A'
'B'
'C'
'D'
'E'
'F'
The conversion only consists of replacing the register value by a corresponding table value (which is the overall concept of a lookup table).
When this is done and the number we had was 128, we now have reg A holding '8' and reg B holding '0' because 128 is 0x80.
Converting 16-bit numbers from int to ASCII coded hex is not much harder. The result needs 4 registers and we need to convert two ints without having to use any 16-bit instructions:
1024 (hex 0x0400) converts to '0' and '4' for the high byte and '0' and '0' for the low byte.


Converting ASCII Coded Decimal to INT
Ascii Coded Decimal strings are, for example, used in the Ymodem protocol for sending file size information. To use that string as a file size it has to be converted to an integer in order to be processed fast and code-efficient.
The conversion is not as straight-forward as others, as the string length has to be taken into account:
'512' has a length of 3 characters, and therefore the '5' means 500, and not 50 or 5. As the most significant digits are usually sent first, the string is not too hard to be converted. If the least significant digit is sent first the string has to be stored first and can't be processed byte for byte.
Given that the most significant digit is sent first, the following steps are necessary for each character received:

For the '512' string, this happens:
'5' is converted to 5 by subtracting ASCII '0' from '5'. Then the result (which is still zero) is multiplied by 10. The result is zero.
Now 5 is added. Result = 5
Now the '1' is received and converted to 1. The result (which is 5) is multiplied by 10, so we now have 50.
1 is added which equals 51.
The '2', being the last character of the string, is again converted, the result is pultiplied by 10 (=510) and 2 is added. The result is 512. Easy huh?
The only thing I didn't mention until now is how to determine when the string ends. This depends on the transmitter or source of the string. If it is a null-terminated string, we can use the following program flow:



Converting ASCII Coded Hex to Int
For the conversion from ASCII coded hex to int we first need some code that converts one ASCII character (which represents one hex nibble) to its decimal value. 'A' -> 10. This can't (or shouldn't) be done with a lookup table, because the ASCII value of 'A' is bigger than the size of efficient code doing the same thing. Therefore, the lookup table would also be quite big. So, it's better to choose the case structure.
Then the second nibble is converted in the same way and the two nibbles are combined to form one int.

This conversion is done for the two nibble characters. These are then combined in one byte:

Maybe the nibbles have to be swapped again, depending on how the two nibbles were sent: If the high nibble was sent first, the received byte can left as it is: The high nibble was added, the nibbles were swapped and then the low nibble was added.
Consequently, if the low nibble is sent first, the nibbles have to be swapped again.

Numbers

As you read through these pages you might want to have a look at an ascii table. You can find one in the Banner frame ("quick links").
Conversions are very important for user interaction: If a register has the value 0x30 its corresponding ascii character is '0'. But if you want it to be displayed as '48' (0x30 is 48), you need to convert the number. In the case of '0' this is not that important, but 0xFF is displayed as a block. And '48' is better than '0' if you're displaying a temperature...
Some protocols, such as Ymodem, also use strings of values we have to convert first before we can perform calculations on them: Ymodem sends a file size of 512 bytes as '512'. An AVR has to convert this from ascii coded decimal to 16bit int first before it knows what '512' means.
Some number formats you should have in mind when doing calculations:
128
'128'

0x30

0b11001010
'11001010'
; normal decimal value
; ascii coded decimal. In this case you need three bytes ('1', '2' and
; '8') to store that number.
; hex value
;
; binary value
; ascii coded binary
It's up to you which number format you use for a specific task. Ascii coded hex is quite often used for debugging purposes, because the numbers are all of the same size (number of characters needed) and becase the conversion always takes the same number of cpu cycles and doesn't require much space. Ascii coded decimal is better for things like temperatures or rpm of a motor. Ascii coded binary is good for displaying flag registers (SREG, Interrupt flag registers and so on).
I'll show you ways to convert numbers in both directions: From int to something you can display and back.

Commonly Used Number Formats
The ALU of an AVR only knows the integer number, unsigned as well as signed, and only 8 bits wide. The 8 bit limit is not as bad, as we can still use the carry bit to make 16-, 24- and 32 bit operations possible.
Converting numbers from one format to another is not as easy and requires the person writing the code to understand the number formats first.
All conversions explained on these pages have the integer as one "end" (source or result). This is the number the AVR actually deals with.
Other formats use ASCII characters or the fact that a digit (which has a range of 0 to 9) only uses one nibble of a byte.
HEX format:
In AVR Assembler (and on this site) HEX numbers are written with the "$"-sign or "0x" at the beginning:
$10 is equal to 16 and 0x20 is equal to 32.
The Hex format splits the 8 bits of a byte into "nibbles" of 4 bits (the high nibble and the low nibble) and displays them with a number or character:

Nibble value:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Hex:
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
If an 8-bit number is sent or printed as ASCII Coded Hex, the number is split into high nibble and low nibble (in the case of ox20 these are 2 and 0). Then the nibbles are converted to their ASCII representative: 0x32 for 2 and 0x30 for 0. These values can be printed on screen. The values from the table above can not be preinted on the screen: In the ASCII table these are either not defined or control characters. A won't be displayed as 'A'.
Binary format:
The binary format should be quite clear: 0b00001000 is equal to 8, 0b00011000 is equal to 24. Easy. When a number comes as ASCII coded binary, the 1s and 0s are sent as their ASCII representative, 0x30 and 0x31, and thus have to be converted before they are "real" bits. The binary format also requires bit shifting for the conversion.
Binary Coded Decimal (BCD) format:
Binary coded decimal is very handy for storing two digits (0..9) in one byte without much coding. The digits are directly written to a byte nibble.
0x22 means that the low nibble contains the number 2 and the high nibble contains the number 2 as well. A consequence of this is that a byte can only hold value in the range of 0 to 99: The values 10 to 15 (A to F in Hex format) are not allowed in BCD format.
This format can for example be written to a port which has a 7447 connected to it. This IC is a 7-segment LED driver which converts this format so that the segments of the LED display show the number of the nibble.
The Ascii Table
Very often you'll need to convert ascii to hex or decimal numbers and back. An ascii table is THE tool you'll need for that. Here is one.
If you need the hex value of 'H', look for H. It's in column $4x and row $x8. 'H' = $48 or 0x48. Other way around: You need to know what 0x69 is when shown as a character. Column $6x, row $x9: 'i'
The "ctrl" column contains the control characters in short form. The real name can be found further down on this page in a seperate table.
We're working on a printable version of this... Most probably we'll have to divide the table by three or so and fill three pages. One won't be enough for all this...


$0x
$1x
$2x
$3x
$4x
$5x
$6x
$7x
$8x
$9x
$Ax
$Bx
$Cx
$Dx
$Ex
$Fx
dec
char
ctrl
dec
char
ctrl
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
dec
char
$x0
000

NUL
016

DLE
032
spc
048
0
064
@
080
P
096
`
112
p
128
144

160

176
°
192
À
208
Ð
224
à
240
ð
$x1
001

SOH
017

DC1
033
!
049
1
065
A
081
Q
097
a
113
q
129

145
161
¡
177
±
193
Á
209
Ñ
225
á
241
ñ
$x2
002

STX
018

DC2
034
"
050
2
066
B
082
R
098
b
114
r
130
146
162
¢
178
²
194
Â
210
Ò
226
â
242
ò
$x3
003

ETX
019

DC3
035
#
051
3
067
C
083
S
099
c
115
s
131
ƒ
147
163
£
179
³
195
Ã
211
Ó
227
ã
243
ó
$x4
004

EOT
020

DC4
036
$
052
4
068
D
084
T
100
d
116
t
132
148
164
¤
180
´
196
Ä
212
Ô
228
ä
244
ô
$x5
005

ENQ
021

NAK
037
%
053
5
069
E
085
U
101
e
117
u
133
149
165
¥
181
µ
197
Å
213
Õ
229
å
245
õ
$x6
006

ACK
022

SYN
038
&
054
6
070
F
086
V
102
f
118
v
134
150
166
¦
182
198
Æ
214
Ö
230
æ
246
ö
$x7
007

BEL
023

ETB
039
'
055
7
071
G
087
W
103
g
119
w
135
151
167
§
183
·
199
Ç
215
×
231
ç
247
÷
$x8
008

BS
024

CAN
040
(
056
8
072
H
088
X
104
h
120
x
136
ˆ
152
˜
168
¨
184
¸
200
È
216
Ø
232
è
248
ø
$x9
009

HT
025

EM
041
)
057
9
073
I
089
Y
105
i
121
y
137
153
169
©
185
¹
201
É
217
Ù
233
é
249
ù
$xA
010

LF
026

SUB
042
*
058
:
074
J
090
Z
106
j
122
z
138
Š
154
š
170
ª
186
º
202
Ê
218
Ú
234
ê
250
ú
$xB
011

VT
027

ESC
043
+
059
;
075
K
091
[
107
k
123
{
139
155
171
«
187
»
203
Ë
219
Û
235
ë
251
û
$xC
012

FF
028

FS
044
,
060
<
076
L
092
\
108
l
124
|
140
Œ
156
œ
172
¬
188
¼
204
Ì
220
Ü
236
ì
252
ü
$xD
013

CR
029

GS
045
-
061
=
077
M
093
]
109
m
125
}
141

157

173
­

189
½
205
Í
221
Ý
237
í
253
ý
$xE
014

SO
030

RS
046
.
062
>
078
N
094
^
110
n
126
~
142
Ž
158
ž
174
®
190
¾
206
Î
222
Þ
238
î
254
þ
$xF
015

SI
031

US
047
/
063
?
079
O
095
_
111
o
127

143

159
Ÿ
175
¯
191
¿
207
Ï
223
ß
239
ï
255
ÿ
0x20 ('spc") means space, of course.
Here is the control character table.
SOH - Start Of Header DLE - Data Link Escape
STX - Start Of teXt DC1 - Device Control 1
ETX - End Of teXt DC2 - Device Control 2
EOT - End Of Transmission DC3 - Device Control 3
ENQ - ENQuiry DC4 - Device Control 4
ACK - ACKnowledge NAK - Negative AcKnowledge
BEL - BELl SYN - SYNchronous idle
BS - BackSpace ETB - End of Transmission Block
HT - Horizontal Tabulation CAN - CANcel
LF - Line Feed EM - End of Medium
VT - Vertical Tabulation SUB - SUBstitute
FF - Form Feed ESC - ESCape
CR - Carriage Return FS - File Separator
SO - Shift Out GS - MainForm.Group Separator
SI - Shift In RS - Record Separator
US - Unit Separator



Assembler Basics

Assembler is a low-level language which does not know any C-like commands like for{;;} or while{}.
Assembler instructions are small, for example out PortD, r15 writes the contents of register 15 (which in an AVR can hold one byte) to PortD (which is 8 I/O lines handled as one I/O register).
Other assembler instructions only work on the register rather than on registers AND I/O registers or SRAM. "inc r15" is one of them. It increments the value register 15 holds by one. This is useful for loops (like for{;;}).
Almost every instruction leaves certain bits in the Status Register set or cleared based on the instruction's result. These bits can be used by branch instructions or arithmetic instructions in order to perform correctly (branch/don't branch, increment result etc).
Branch instructions jump to a specific code address (or code line) if the microcontroller is in a specific state or just go on with the next code line if this state is not present. If the counting variable in a loop has not reached the desired value, they can let the mcu repeat the loop.
Here is a small example code snippet showing how arithmetic, I/O and branch instructions work together:
ldi r16, 0
for_loop:
inc r16
out PortD, r16
cpi r16, 10
brlo for_loop
; load register 16 with zero
; this is a label we can jump or branch to
; increment register 16
; write contents of r16 to PortD
; compare value in r16 with 10 (this leaves a status for brlo)
; if value 10 not reached, repeat loop
In the loop the counter (r16) is increased in every iteration and written to PortD. When it reaches 10, brlo will not jump to the beginning of the loop, but to the next instruction. This small example gives a good impression of how small the steps you can take in assembler are. It can't get smaller, as this is what the mcu does. None of the instructions will be split into smaller ones by the assembler. With the comments in mind have a look at the AVR instruction set and also have a look at other instructions of the same type.
Assembler is very sensitive to programming errors. Try the above example with the increase instruction and the compare instruction swapped. What happens? The first value on PortD is 0, the last one is 9.
Now have a look at the "Flow Charts" section and try to write a flow chart of the code above. You'll see that flow charts make code less "cryptic" and readable. And keep them up to date every time you make a big enhancement to your code so that you can still read it after two weeks. Comments are also very important, especially if you can't make a flow chart every time your code changes. In assembler, a comment can be written for almost every line of code, especially when tricks are used.

An asm Introduction And The Embedded "Hello World"!!!!

Assembler is a low-level language. It consists of a list of instructions that are in no way comparable to anything you might know from C, Basic or Pascal. The AVR has about 120 of them, depending on the type and it's peripherals and size. You can download the instruction set from Atmel's website and print out the summary (a list of all instructions and what type of operands they have). If you download it, I suggest printing out pages 1 and 10 to 15. That's not too much and yet is everything you need for a start.

Let's have look at probably the easiest program possible:
main:
rjmp main
; this is a the label "main"
; Relative JuMP to main
"Main" (the first line) is not translated by the assembler, but used as a label. This label replaces an address in the AVR's code space (FLASH memory). At exactly this address the next instruction (rjmp main) is placed. When the instruction is exectued, the cpu will jump to "main" again. The jump will be repeated over and over, resulting in an infinite loop.
After power-up, or when a reset has occured, the micro will always start program execution from address 0x0000. The first bytes in code space are the "Interrupt Vector Table". The AVR has internal peripherals, like timers, a UART or an analog-to-digital converter. These can generate interrupts which will stop normal code execution in order to react on certain events as fast as possible. This is good to know.

The interrupt vector table can be used by you to tell the micro what it has to do when a specific interrupt occurs. The normal AVRs have space for one instruction per interrupt vector (an rjmp for example). This instruction will be executed when the interrupt occurs (There's more to tell you about this, but not now...)
The first interrupt vector is the "reset vector". It contains the instruction the cpu should execute when a reset occurs. We will use it to jump to our program we already had above:
.org 0x0000
rjmp main

main:
rjmp main
; the next instruction has to be written to address 0x0000
; the reset vector: jump to "main"
;
; this is the label "main"
; Relative JuMP to main
Assuming that our AVR is running at 4 MHz (4 Million clock cycles per second), how long does all this take? AVRs are pretty fast - most instructions can be executed in one or two clock cycles. Some instructions need a bit more time, but these are not important now. As the external clock is not divided internally (some other microcontrollers do that, like the HC11 from motorola, but that's an old one), two clock cycles at 4 MHz means that the instruction takes 0.0000005 seconds. Pretty fast!
"main" itself only needs 0.0000005 seconds per round, as it only consists of a single rjmp. Right now, our main program doesn't actually DO anything.
The first thing I did when I started on AVRs was making an LED flash. LEDs can be connected to the AVR's I/O ports . These can be set to be input or output individually for each pin and you can also enable an internal pull-up resistor if the port pin is set to input. Each I/O port has three registers you can work with: The port's data register (for example PortB), the Data Direction Register (for example DDRB) and the Pin register (for example PinB). For confiugring a pin as an output pin, set its corresponding bit in the data direction register. The output value (0 or 1) can then be set in the Port Data register.
If you have an STK500, get yourself an ATmega8 in a DIP package and a 4 MHz crystal. The micro can plugged into the green target socket named "SCKT3200A2". Connect the 6-pin cable from "ISP6PIN" to the ISP header for the green socket. That's the one named "SPROG2". The default jumper setting for the oscillator system is the software oscillator. We want to use the crystal oscillator instead. Set the "OSCSEL" jumper to close pins 2 and 3 (pin 1 is marked with a "1"). "Vtarget", "AREF", "RESET" and "XTAL1" should be closed. Of course, the mega8 should be the only micro plugged into the STK. Do not insert more than one AVR at a time! Now take one of the two wire-cables and use it to connect "PB3" (that's one of the "PORTB" pins) to "LED0" (that's one of the "LEDS" pins). The crystal belongs into the crystal socket on the STK500.
The STK500 LEDs are connected to the AVR via some extra components. If you want, you can take a look at the LED circuit in the STK500 documentation (it's included in the AVR Studio help file and also came with the STK). The most important fact is that the LED is connected to be "active low", which means that if PortB.3 is low now (we connected it to one of the LEDs), the LED will be ON.
If you don't have an STK, connect the LED to PortB.3 via a current limiting resistor (about 470 Ohms is OK, the value is not critical). Connect the resistor to the port pin and the LED's cathode, the anode goes to Vcc. This will result in the same "active low" behavior.
Let's now change our program a bit so that it configures all PortB pins as output pins. After a reset, all Data bits are set to zero, so the LED should be ON when the program is executed:
.org 0x0000
rjmp main

main:
ldi r16, 0xFF
out DDRB, r16
loop:
rjmp loop
; the next instruction has to be written to address 0x0000
; the reset vector: jump to "main"
;
; this is the label "main"
; load register 16 with 0xFF (all bits are 1)
; write the value in r16 (0xFF) to Data Direction Register B
; this is a new label we use for a "do nothing loop"
; jump to loop
The new loop was inserted so that we can set the Data Direction bits to our needs and then loop without doing that again. We could, however, include the load and store instructions (ldi and out) in the loop. It wouldn't hurt, but the micro would configure the Ports to the same value over and over again.

BASCOM-AVR

BASCOM AVR compiler

Bascom AVR is a compiler that convert basic program language into hex code that you can program your microcontroller with. Bascom AVR also has a build in programmer to program your microcontroller with. You can choose from various programming adapters:
    Parallel Port:
  • Sample Electronics Programmer
  • STK200-300 Programmer
    Serial Port:
  • STK500 Board
  • AVR ISP Programmer
  • KITSRUS K122 Programmer
    USB:
  • MCS USB Programmer
  • USB ISP Programmer
On the AVR Programming Hardware pages you can find a schematic of the Sample Electronics Programming Cable.