;Micro: atmega16
;This program gets a value from the adc, converts it to ascii,
;then spits it out the usart
;make sure the internal 1 Mhz oscilator fuse bit isn't set or this
;program won't work, it needs to be run off the stk500's 3.69Mhz clock
;books for avr noobies and greenies
;Programing and Customizing the Avr
;Smiley's book C Programming for Microcontrollers
www.smileymicros.com
;AVR An Introductory Course by John Morton
;Print out the datasheets and any appnote that applies to what you want
;to do
;define registers
.def c10000 = r16
.def c1000 = r17
.def c100 = r18
.def c10 = r19
.def c1 = r20
.def temp = r21
.def upper = r22
.def lower = r23
.def treg = r24
;the line below tells the assembler to include the file m16def.inc
;this included a bunch of definitions so we can type stuff like
;"spl" (stack pointer low) instead of a bunch of numbers
.include "m16def.inc"
;the next two lines tell the assembler the code segment is begining
;and we're starting at address 0x00 (the first one available)
.cseg
.org 0x00
;this is your interupt vector table, if you have interupts enabled
;and one is triggered it tells the microcontroller what to do next
jmp reset ;reset
jmp reset ;exturnal interrupt request0
jmp reset ;exturnal interrupt request1
jmp reset ;timer counter2 compare match
jmp reset ;timer counter2 overflow
jmp reset ;timer counter1 capture event
jmp reset ;timer counter1 compare matchA
jmp reset ;timer counter1 compare matchB
jmp timer1_over ;timer counter1 overflow
jmp timer1_over ;timer counter0 overflow
jmp reset ;spi complete
jmp reset ;usart rx complete
jmp reset ;usart data register empty
jmp reset ;usart tx complete
jmp adc_complete ;adc converstion complete
jmp reset ;eeprom ready
jmp reset ;analog comparator
jmp reset ;two wire serial interface
jmp reset ;external interupt request2
jmp reset ;timer counter0 compare match
jmp reset ;store program memory ready
;these are the things we want to do after a reset
reset:
;STACK, PORTB, PORTA, TIMER1, ADC, GLOBAL INTERRUPTS
;some memory locations can not be accessed dirrectly, you have to
;use one of the general purpose registers to get to them. The ldi (load
;immediate) instructions is used for this.
;STACK
ldi r16, low(ramend) ;initialize stack, sets lower and upper
out spl, r16 ;boundries
ldi r16, high(ramend)
out sph, r16
;set port b up for out put by loading all 8 bits of ddrd (data
;direction register b) with "1"
;PORTB
ldi r16, 0xff ;portb all output
out ddrb, r16 ;0xff = 0b11111111 = 255
;to make a pin an input make the the bit/bits 0
;PORTA
ldi r16, 0x00
out ddra, r16 ;porta all input
;the goal here is to get the timer to overflow about once a second, the
;values below will make the timer overflow every 1.14 seconds if the
;clock speed is 3.69MHz, like it is on the stk500
;if you don't understand the "(1<<cs11)|(1<<cs10)" read the datasheets
;for the meaning of cs11 and cs10, there in the timer/counter1 register
;discription. As for the bitwise OR (|) google "bitwise or"
;TIMER1
ldi r16, (1<<cs11)|(1<<cs10) ;prescale = 64
out tccr1b, r16 ;sets the cs11 and cs10 bits
ldi r16, (1<<toie1) ;overflow interrupt enabled
out timsk, r16
;ADC
ldi r16, 0b10001101 ;adc enable,
out adcsr, r16 ;adc interrupt prescale = 32
;GLABAL INTERRUPTS
sei ;enable global interupts
;USART
ldi r16, 0x02 ;portd pin1 output
out ddrd, r16
ldi r16, (1<<txen) ;tx enabled
out ucsrb, r16
ldi r16, 23 ;baudrate = 9600
out ubrrl, r16 ;the value 23 will only give you
;a baudrate of 9600 if your
;clock speed is 3.69MHz
;there is a table and formula
;to find this vaule in the datasheets
main:
rjmp main ;this loops is only interrupted by an
;interrupt from the adc finishing a
;conversion or timercounter1 overflow
;(see the interupt vector table above)
;This is what we want to do when timercounte1 overflows
timer1_over:
sbi adcsr, adsc ;tell the adc to start a conversion
reti
;This is what we want to do when the adc completes a conversion
adc_complete:
in lower, adcl ;get low byte
in upper, adch ;get high byte
;Here is the part that converts the 16bit value stored in "lower" and
;"upper" to ascii and send it out the usart
;I'm not sure how other people do it but here's I did it. I use a
;register for each place holder (one's, ten's, hundred's, etc). For the
;upperbyte I look at each bit starting with the least significant bit.
;bits if the first bit (bit zero) is set then I add 2 to the hundred's
;place, 5 to the ten's place, and 6 to the one's place (256). If the second
;bit (bit 1) is set I add a 5 to the hundreds place, 1 to the tens place,
;and 2 to the one's place (512) keep inspecting the bits until they're
;all inspected and you've added the apropriate values:
;bit 0 256
;bit 1 512
;bit 3 1024
;bit 4 2048
;bit 5 4096
;bit 6 8192
;bit 7 16384
;
SixteenBitToAsciiOutUSART:
ldi c10000, 0 ;set all place holders to 0
ldi c1000, 0
ldi c100, 0
ldi c10, 0
ldi c1, 0
UpperByte:
sbrc upper, 0 ;check each bit, if bit is set add the
rcall two56 ;appropriate numbers to C1-C10000
sbrc upper, 1 ;sbrc = skip if bit in register is cleard
rcall five12
sbrc upper, 2
rcall one024
sbrc upper, 3
rcall two048
sbrc upper, 4
rcall four096
sbrc upper, 5
rcall eight192
sbrc upper, 6
rcall one6384
sbrc upper, 7
rcall three2768
rjmp LowerByte
two56:
ldi temp, 6
add c1, temp
ldi temp, 5
add c10, temp
ldi temp, 2
add c100, temp
ret
five12:
ldi temp, 2
add c1, temp
ldi temp, 1
add c10, temp
ldi temp, 5
add c100, temp
ret
one024:
ldi temp, 4
add c1, temp
ldi temp, 2
add c10, temp
ldi temp, 1
add c1000, temp
ret
two048:
ldi temp, 8
add c1, temp
ldi temp, 4
add c10, temp
ldi temp, 2
add c1000, temp
ret
four096:
ldi temp, 6
add c1, temp
ldi temp, 9
add c10, temp
ldi temp, 4
add c1000, temp
ret
eight192:
ldi temp, 2
add c1, temp
ldi temp, 9
add c10, temp
ldi temp, 1
add c100, temp
ldi temp, 8
add c1000, temp
ret
one6384:
ldi temp, 4
add c1, temp
ldi temp, 8
add c10, temp
ldi temp, 3
add c100, temp
ldi temp, 6
add c1000, temp
ldi temp, 1
add c10000, temp
ret
three2768:
ldi temp, 8
add c1, temp
ldi temp, 6
add c10, temp
ldi temp, 7
add c100, temp
ldi temp, 2
add c1000, temp
ldi temp, 3
add c10000, temp
ret
;Here's where we work on the lower byte. The above method works on the
;lower byte but this part of the code was the first thing I programmed
;for the avr that did not involve making an led blink and I wanted
;to try out different branching instructions
;
;start with the hundreds place
;compare the byte by 100, if lower work on the ten's place, if not
;subtract 100 and repeat.
;
;repeat but use 10 and 1
;
;
;
;
LowerByte:
LowerHundredsPlace:
cpi lower, 100
brlo LowerTensPlace
subi lower, 100
inc c100
rjmp LowerHundredsPlace
LowerTensPlace:
cpi lower, 10
brlo LowerOnesPlace
subi lower, 10
inc c10
rjmp LowerTensPlace
LowerOnesPlace:
add c1, lower
;Now the all the place holders have vaules in them but some of them
;will have values above 10, so we have to get the place holder value
;below 10, we use a process simular to the one to convert the lowerbyte
;
;example
;123, 1 in the hundred's place, 2 in the ten's place, 3 in the one's place
;before we go to the next part of the program it may look like this:
;1 in the hundred's place, 1 in the ten's place, 13 in the one's place
SolveOnes:
cpi c1, 10
brlo SolveTens
subi c1, 10
inc c10
rjmp SolveOnes
SolveTens:
cpi c10, 10
brlo SolveHundreds
subi c10, 10
inc c100
rjmp SolveTens
SolveHundreds:
cpi c100, 10
brlo SolveThousands
subi c100, 10
inc c1000
rjmp SolveHundreds
SolveThousands:
cpi c1000, 10
brlo Add48
subi c1000, 10
inc c10000
rjmp SolveThousands
;convert the value to ascii by adding 48 or ORI 0b00110000
;I picked the ori part up at an avr tutorial website, google
;"avr tutorial" it will be one of the first few sites, it's in
;German and English
Add48:
ori c1, 0b00110000
ori c10, 0b00110000
ori c100, 0b00110000
ori c1000, 0b00110000
ori c10000, 0b00110000
;this part sends the values out the usart and adds a ";" as a delimiter
Tx10000:
sbis UCSRA, UDRE
rjmp Tx10000
out UDR, c10000
Tx1000:
sbis UCSRA, UDRE
rjmp Tx1000
out UDR, c1000
Tx100:
sbis UCSRA, UDRE
rjmp Tx100
out UDR, c100
Tx10:
sbis UCSRA, UDRE
rjmp Tx10
out UDR, c10
Tx1:
sbis UCSRA, UDRE
rjmp TX1
out UDR, c1
Tx59:
sbis ucsra, udre ;delimiter
rjmp Tx59
ldi temp, 59
out udr, temp
reti
[/code]