
Разработка программного обеспечения контроллера
Важным моментом является настройка битов конфигурации контроллера, так называемые Fuse. Выбрана следующая конфигурация:
Extended fuse = 0x03 – Режим совместимости с AtMega103 отключен, WDT отключен
High Fuse Byte = 0x99 – Внутрисхемный отладчик, CKOPT, сохранение данных EEPROM при стирании, переход к загрузчику - отключены, JTAG, последовательная запись данных – включены.
Low Fuse Byte = 0xEE – BOR отключено, время запуска после сброса – по умолчанию, тактовый генератор работает от внешнего высокочастотного кварца с диапазоном 3-8 МГц.
Программу можно разбить на ряд процессов, протекающих параллельно и связанных только в нескольких ключевых точках:
-
Измерение
-
Передача данных (момент начала передачи привязан к готовности данных)
-
Прием пакета и сброс флагов ошибки
-
Тест системы (должен производиться только в то время, когда ОЗУ свободно)
Рассмотрим реализацию измерения.
Данный процесс является основным, он запускает передачу данных в нужный момент, а также запускает процесс самотестирования.
Требуется 10 раз в секунду получить данные для всех 5 каналов, причем каждый канал нужно измерить 8 раза и усреднить по этим отсчетам данные. Для этого настраивается таймер на генерацию 10*5*8 = 400 отсчетов в секунду.
По каждому отсчету таймера запускается АЦП.
По прерыванию по завершению измерения АЦП производится считывание измеренных данных, их прибавление к сумме и переключение на следующий канал. После пятого канала производится возврат к нулевому каналу. При получении 8 отсчетов для каждого канала производится усреднение и запись данных в ОЗУ. Причем для проведения теста ОЗУ запись производится только при получении нечетной выборки, при этом результаты четной выборки хранятся в специальном буфере rezbuf и записываются в ОЗУ вместе с результатами предыдущей нечетной выборки.
Такой алгоритм запуска АЦП выбран для того чтобы ввести паузу между переключением внутреннего мультиплексора и измерением, необходимую для перезаряда внутренних емкостей аналоговых линий микроконтроллера.
Для получения ровно 400 отсчетов в секунду нужно разделить тактовую частоту контроллера 7372800 Гц на 400:
7372800 / 400 = 18432,
Нужно запрограммировать таймер таким образом, чтобы он генерировал прерывания каждые 18432 тактов. Можно заметить, что 16384 = 288*64, поэтому можно использовать 16-ти битный Timer0 с делителем на 64. Число 288 заносится в регистр сравнения таймера. Используются прерывания от модуля сравнения таймера, и в каждом прерывании к значению регистра сравнения прибавляется 288. Это позволяет получить точное число отсчетов в секунду, даже если прерывания не будут обрабатываться сразу после наступления события, например из-за занятости контроллера процедурой обработки другого прерывания.
При получении очередного отсчета АЦП производятся следующие действия:
- Вычисляется номер двухбайтовой ячейки в массиве adcsigma
- Считывается результат преобразования АЦП и прибавляется к выбранной ячейке
- Увеличивается номер канала (и одновременно – ячейки). Если следующий номер – 4, то происходит возврат к номеру 0. Номер канала хранится непосредственно в регистре управления мультиплексором АЦП.
- Увеличивается количество отсчетов adcnum. Если получено меньше 8 отсчетов, то процедура завершается
- Если получено 8 отсчета, то они обрабатываются следующим образом:
- Определяется позиция в массиве выходных данных на основе счетчика выходных данных arrpos.
- Каждая из 5 двухбайтовых ячеек массива adcsigma считывается, делится на 16 (на 4 – из-за усреднения, и на 4 – чтобы результат лежал в диапазоне 0..255), и записывается в массив выходных данных.
- Массив adcsigma обнуляется.
- Позиция в массиве arrpos увеличивается на 1.Если новая позиция =2400, arrpos сбрасывается.
- При значении arrpos=2399. запускается передача массива данных в порт.
Основной массив данных включает в себя 12009 байт информации. Со скоростью 38400, при параметрах передачи 8N1 передача всего пакета займет 312,7 миллисекунд. Передача одного байта занимает 0.02604мс. Рассчитаем момент начала передачи таким образом, чтобы к моменту получения последних 8 отсчетов передавать оставалось 10 байт информации (с небольшим перекрытием):
312,7 – 0.02604*10 = за 312,45 миллисекунды до завершения последнего измерения. Момент времени можно задавать при выбранном алгоритме только с точностью 0.01 секунды. Выберем момент запуска равным 240-0,312 секунды, то есть 2397 отсчет. На момент получения 2400 отсчета будет передаваться
313/ 0.02604 = 11982 байт.
Передача завершится через
(12009-11982) * 0.02604 = 27 миллисекунды после завершения измерения.
Такой сложный алгоритм запуска передачи выбран для того, чтобы провести тест внешней памяти в тот момент, когда данные в ней не используются. После завершения передачи до момента, когда алгоритм получения данных будет записывать данные во внешнее ОЗУ, останется около 61 миллисекунды на тест ОЗУ – все данные на этот момент будут переданы, а результаты нового измерения ещё будут накапливаться во внутреннем ОЗУ, в буфере adcsigma и rezbuf.
Тестирование системы заключается в проверке работы внешнего ОЗУ как наиболее сложной части системы. Тестирование осуществляется заполнением ОЗУ данными с определенным значением, затем проверкой и изменением данных на инверсное значение и повторной проверкой. Это позволяет проверить наличие «битых» ячеек, имеющих всегда нулевое или единичное значение, а также шину данных ОЗУ. Проверка шины адреса ОЗУ осуществляется при повторной проверке после изменения значения – если на одну ячейку ссылаются два адреса, то после изменения значения по первому адресу изменится и значение по второму адресу, и вторая проверка даст ошибку в этом месте. В качестве данных записывается некоторое значение, получаемое путем прибавления к начальному значению (нулю) числа 3, т.е. записывается 0,3,6,9,… и т.д.
Так как во внешнем ОЗУ используется только 12000 значений, проверка проводится только по 16384 первым значениям. Это позволяет использовать ОЗУ объемом 16К вместо установленных 32К.
Прием пакета осуществляется в прерывании, состояние которого задается байтом rsrdpos. Возможно несколько состояний – ожидание начала приема (прием байта 0xАА), продолжение – прием байта 00, далее прием двух байтов и их сравнение. Если принят байт, не соответствующий ожидаемому, происходит возврат в состояние ожидания байта 0xАА. В противном случае если оба принятых байта совпали – сбрасываются соответствующие биты порта B.
Алгоритм основной программы:
Алгоритм обработчика прерывания от модуля сравнения таймера
Алгоритм обработчика прерывания от АЦП
Алгоритм обработчика прерывания от приемника UART
Алгоритм обработчика прерывания от освобождения буфера передатчика UART
Листинг программы
.include "m128def.inc"
.DSEG
;Делитель для выравнивания частоты отсчетов АЦП
timerdiv: .BYTE 1
; Переменные получения данных с АЦП
adcsigma: .BYTE 10 ; 5 двухбайтовых сумм для усреднения результата
adcnum: .BYTE 1 ; Счетчик полученных измерений
arrpos: .BYTE 2 ; Позиция в буфере результата
; Буфер для хранения четных данных
rezbuf: .BYTE 5
; Переменные отправки данных по RS232
rsstate: .BYTE 1 ; Состояние алгоритма передачи
rspos: .BYTE 2 ; Положение передаваемого байта в буфере
rscrc: .BYTE 1 ; Контрольная сумма
; Переменные получения блока по RS232
rsrdpos: .BYTE 1 ; Состояние алгоритма приема пакета
rsbyte: .BYTE 1 ; Принятый байт
; Переменные процедуры тестирования внешнего ОЗУ
chkcnt: .BYTE 1 ; Счетчик блоков до повторного теста
work: .BYTE 1 ; устанавливается в не ноль при необходимости тестирования
; Сегмент кода векторов прерываний - 35 векторов. Используются только
; команды jmp, так как они занимают ровно столько места, сколько выделено
; под каждый вектор, и нет нужды задавать в явном виде адреса векторов.
.CSEG
.org 0x0
jmp main ; 1
jmp toreti ; 2
jmp toreti ; 3
jmp toreti ; 4
jmp toreti ; 5
jmp toreti ; 6
jmp toreti ; 7
jmp toreti ; 8
jmp toreti ; 9
jmp toreti ; 10
jmp toreti ; 11
jmp toreti ; 12
jmp toreti ; 13
jmp toreti ; 14
jmp toreti ; 15
jmp TimerOcr ; 16
jmp toreti ; 17
jmp toreti ; 18
jmp GetNextByte ; 19
jmp SendNextByte; 20
jmp toreti ; 21
jmp ADCgetrez ; 22
jmp toreti ; 23
jmp toreti ; 24
jmp toreti ; 25
jmp toreti ; 26
jmp toreti ; 27
jmp toreti ; 28
jmp toreti ; 29
jmp toreti ; 30
jmp toreti ; 31
jmp toreti ; 32
jmp toreti ; 33
jmp toreti ; 34
jmp toreti ; 35
; Мой сегмент кода
.CSEG
; "Пустая" процедура прерывания для неинициализированных векторов
toreti:
reti
;================================================
; Процедура, вызываемая при получении очередного байта
; Состояния приемника - rsrdpos
; 0 - получаем очередной байт, проверяем на 0xAA
; 1 - получаем очередной байт, проверяем на 00
; 2 - получаем и сохраняем
; 3 - получаем, проверяем с сохраненным. Если совпадает - меняем состояния флагов
; При сбое в любом состоянии (кроме 2 - в нём сбиваться нечему) возвращаемся к нулевому состоянию
GetNextByte:
push r16
push r17
push r18
lds r16,UDR0 ; Получаем принятый байт
lds r17,rsrdpos
; Выбираем куда переходить
ldi r18,1
sub r18,r17
breq rdst1
ldi r18,2
sub r18,r17
breq rdst2
ldi r18,3
sub r18,r17
breq rdst3
; Сюда - при нулевом либо ошибочном состоянии
ldi r17,0xAA
sub r16,r17
breq setst1
resetreader:
; Сбрасываем состояние
ldi r17,0
sts rsrdpos,r17
rjmp exitgnb
setst1:
ldi r17,1
sts rsrdpos,r17
rjmp exitgnb
rdst1:
ldi r17,0x00
sub r16,r17
brne resetreader
ldi r17,2
sts rsrdpos,r17
rjmp exitgnb
rdst2:
sts rsbyte,r17
ldi r17,3
sts rsrdpos,r17
rjmp exitgnb
rdst3:
lds r17,rsbyte
sub r17,r16
brne resetreader
; Применяем байт
in r17,PORTB
com r16
ori r16,0xC0
and r17,r16
out PORTB,r17
rjmp resetreader ; Возвращаемся к ожиданию байта
exitgnb:
pop r18
pop r17
pop r16
reti
;================================================
; Процедура, вызываемая при опустошении буфера передачи
; Вход - SendNextByte
; Внутренности процедуры - чтобы хватало длины отноительного перехода
snbis6:
lds r16,rscrc
out UDR0,r16
cbi UCSR0A,5 ; Запрещаем прерывание от освобождения буфера передачи
ldi r16,0
sts rsstate,r16 ; Сбрасываем в ноль
rjmp outsnb
snbis20:
snbis40:
ldi r16,0
out UDR0,r16
rjmp snbincstate
snbis21:
ldi r16,0xAA
out UDR0,r16
rjmp snbincstate
snbis22:
ldi r16,0xAA
out UDR0,r16
cbi UCSR0A,5 ; Запрещаем прерывание от освобождения буфера передачи
ldi r16,0
sts rsstate,r16 ; Сбрасываем в ноль
rjmp outsnb
snbis41:
ldi r16,0xFF
out UDR0,r16
rjmp snbincstate
snbis42:
ldi r16,0xFF
out UDR0,r16
cbi UCSR0A,5 ; Запрещаем прерывание от освобождения буфера передачи
ldi r16,0
sts rsstate,r16 ; Сбрасываем в ноль
rjmp outsnb
SendNextByte:
push r0
push r16
push r17
push r28
push r29
; Обработка - в зависимости от состояния
; Состояния могут быть:
; любой байт, не состоящий в списке - ничего не делаем, прерывания запрещаются. В это состояние процедура даже попадать не должна
; 1 - Передается первый байт, надо передать 0x00 и перейти к следующему состоянию
; 2 - Передается второй байт, надо передать 0xE1, прибавить к CRC и перейти к следующему состоянию
; 3 - Передается третий байт, надо передать 0x2E, прибавить к CRC и перейти к следующему состоянию
; 4 - Передается четвертый байт, надо передать байт флагов переполнения, прибавить к CRC и перейти к следующему состоянию
; 5 - Передается основной пакет. Надо передавать очередной байт, прибавлять к CRC пока не будет передано 12000 байтов. Затем перейти к следующему состоянию и разрешить проверку ОЗУ.
; 6 - Надо передать CRC и запретить прерывания - больше передавать нечего
; Передача пакета "тест успешен"
; 20 - Передаем 00, переходим к следующему
; 21 - Передаем AA
; 22 - Передаем AA и останавливаем передачу
; Передача пакета "тест не пройден"
; 40 - Передаем 00, переходим к следующему
; 41 - Передаем FF
; 42 - Передаем FF и останавливаем передачу
lds r0,rsstate
ldi r16,1
sub r16,r0
breq snbis1
ldi r16,2
sub r16,r0
breq snbis2
ldi r16,3
sub r16,r0
breq snbis3
ldi r16,4
sub r16,r0
breq snbis4
ldi r16,5
sub r16,r0
breq snbis5
ldi r16,6
sub r16,r0
breq snbis6
ldi r16,20
sub r16,r0
breq snbis20
ldi r16,21
sub r16,r0
breq snbis21
ldi r16,22
sub r16,r0
breq snbis22
ldi r16,40
sub r16,r0
breq snbis40
ldi r16,41
sub r16,r0
breq snbis41
ldi r16,42
sub r16,r0
breq snbis42
; Данного варианта нет в списке - запрещаем прерывания
cbi UCSR0A,5 ; Запрещаем прерывание от освобождения буфера передачи
ldi r16,0
sts rsstate,r16 ; На всякий случай сбрасываем в ноль
rjmp outsnb
snbis1:
ldi r16,0
out UDR0,r16
rjmp snbincstate
snbis2:
ldi r16,0xE1
rjmp addcrcincstate
snbis3:
ldi r16,0x2E
rjmp addcrcincstate
snbis4:
in r16,PORTB
andi r16,0x3F
rjmp addcrcincstate
snbis5:
; Тут получаем очередной байт из буфера
lds r0,rspos
lds r1,rspos+1
ldi r28,0 ; Прибавляем начальный адрес в памяти
ldi r29,0x80
add r28,r0
adc r29,r1
ld r16,Y ; Считываем
; Запускаем на передачу, добавляем к CRC
out UDR0,r16
lds r0,rscrc
add r0,r16
sts rscrc,r0
; Прибавляем 1 к счетчику байтов
ldi r16,1
ldi r17,0
add r16,r0
adc r17,r1
sts rspos,r0
sts rspos+1,r1
; Проверяем, не завершена ли передача пакета?
ldi r16,0x2E
sub r1,r16
brne outsnb
ldi r16,0xDE
sub r0,r16
brne outsnb
; Всё передано - проверяем, не запустить ли тест системы
lds r16,chkcnt
inc r16
sts chkcnt,r16
subi r16,0x0A
brne snbincstate ; Переходим к передаче CRC
; Выставляем флаг разрешения теста ОЗУ
ldi r16,0xFF
sts work,r16
; переходим к передаче CRC
rjmp snbincstate
; Выдаем в порт r16 и добавляем его к CRC
addcrcincstate:
out UDR0,r16
lds r0,rscrc
add r0,r16
sts rscrc,r0
; Переходим к следующему состоянию
snbincstate:
lds r0,rsstate
inc r0
sts rsstate,r0
outsnb:
pop r29
pop r28
pop r17
pop r16
pop r0
reti
;================================================
; Процедура, вызываемая каждые 1/400 секунды
TimerOcr:
push r16
push r17
; Корректируем значение регистра сравнения
; Для этого ещё используем счетчик тактов прерывания
lds r16,timerdiv
inc r16
sts timerdiv,r16
ldi r17,0x38 ;
sub r16,r17
ldi r17,0x120 ; Прибавляемая константа
brne normalotr
; Сбрасываем счетчик
sts timerdiv,r16 ; r16 равен нулю
ldi r17,0xF5 ; Прибавляемая константа на 8 отсчетов больше
normalotr:
in r16,OCR0
add r16,r17
out OCR0,r16
; Запускаем АЦП
sbi ADCSR,6
;
pop r17
pop r16
reti
;================================================
; Процедура, вызываемая при завершении измерения АЦП
ADCgetrez:
push r0
push r1
push r2
push r3
push r16
push r17
push r20
push r28
push r29
push r30
push r31
; Получаем результат и прибавляем его к соответствующей сумме
;
; Рассчитываем адрес в массиве сумм
in r16,ADMUX
andi r16,0x05
add r16,r16
ldi r30,Low(adcsigma)
ldi r31,High(adcsigma)
ldi r17,0
add r30,r16
adc r31,r17
; Считываем предыдущую сумму
ld r0,Z
ldd r1,Z+1
; Прибавляем результат АЦП
in r16,ADCL
in r17,ADCH
add r0,r16
adc r1,r17
; Сравниваем с порогами - 0 и 1023.
mov r2,r16
or r2,r17
breq toalarm ; Если ноль - переполнение
; Прибавляем единицу
clr r2
clr r3
inc r2
add r2,r16
adc r3,r17
ldi r16,0x04 ; Если прибавили единицу и получилось 1024 (0x400) - тоже переполнение
sub r16,r17
brne tonoalarm
toalarm:
; Фиксируем переполнение данного канала
in r16,ADMUX
andi r16,0x05
ldi r17,1
; Рассчитываем, какой бит установить, для этого сдвигаем единицу
; влево столько раз, каков номер канала
calcbitcycle:
or r16,r16
breq tosetled
rol r17
dec r16
rjmp calcbitcycle
tosetled:
; Устанавливаем соответствующий бит регистра PORTB
in r16,PORTB
or r16,r17
out PORTB,r16
tonoalarm:
; Записываем новую сумму
st Z,r0
std Z+1,r1
; Переходим к следующему каналу
in r16,ADMUX
inc r16
out ADMUX,r16
mov r17,r16
andi r17,0x05
subi r17,0x05 ; Количество каналов
breq tocheck8
jmp nilabel
tocheck8:
; Все пять каналов получены - возвращаемся к нулевому каналу
andi r16,0xF0
out ADMUX,r16
; Прибавляем счетчик измерений
lds r16,adcnum
inc r16
andi r16,3 ; От этого зависит число усреднений
sts adcnum,r16
; Проверяем - если 0, то нужно усредять и сохранять. Все 4 получены.
breq tousedata
jmp nilabel
tousedata:
; Да, надо использовать результаты
;
; Рассчитываем адрес массиве данных
lds r16,arrpos
lds r17,arrpos+1
; Выбираем, записываем во внешнее ОЗУ или в промежуточный буфер
sbrc r16,0
rjmp realwrite
; Сброшен - т.е. четный номер. Записываем в промежуточный буфер
ldi r30,Low(rezbuf)
ldi r31,High(rezbuf)
rjmp tocalcandwrite
realwrite:
andi r16,0xFE
; Умножаем номер элемента на 5, сдвигами и сложениями
mov r0,r16
mov r1,r17 ; Одно значение
rol r16
rol r17
andi r16,0xfe ; Сбрасываем младший бит. r16/r17 теперь умножены на два
add r0,r16
adc r1,r17 ; одно + удвоенное значение, т.е. *3
rol r16
rol r17
add r0,r16
adc r1,r17 ;
lds r30,0 ; Массив находится во внешнем ОЗУ
lds r31,0x80 ; Всегда по фиксированному адресу
add r30,r0 ; Прибавляем
adc r31,r1
; Теперь Z ссылается на первую ячейку, в которую надо записать результат
; Записываем промежуточный буфер
ldi r16,0x05
ldi r28,Low(rezbuf)
ldi r29,High(rezbuf)
cyclecopy1:
ld r2,Y+
st Z+,r2
; Повторяем r16 раз
dec r16
brne cyclecopy1
; На выходе - адрес в Z уже указывает на нужное место в ОЗУ
tocalcandwrite:
; Для каждой двухбайтовой ячейки массива adcsigma производим следующие действия:
; - Считываем оба байта
; - Делим на 8 - находим среднее значение
; - Делим на 8 - чтобы результат был от 0 до 255
; - Сохраняем в массив результатов только один байт
; Деление на 8*8=64 - это сдвиг на 8 бита вправо.
ldi r28,Low(adcsigma)
ldi r29,High(adcsigma)
ldi r16,0x80 ; Для перевода в дополнительный код
ldi r20,5 ; Повторяем 8 раз
cycle1:
ld r2,Y+
ld r3,Y+
ror r3
ror r2
ror r3
ror r2
ror r3
ror r2
ror r3
ror r2
; Теперь в r2 получились данные, в которых "0" соответствует числу 0x80, а "+U" - 255.
; Нужно перевести результат в дополнительный код вычитанием из числа 0x80
sub r2,r16
st Z+,r2
; Повторяем r4 раз
dec r20
brne cycle1
; Скопировали. Стираем массив adcsigma
ldi r28,Low(adcsigma)
ldi r29,High(adcsigma)
ldi r20,10 ; Повторяем 10 раз
clr r2
cycle2:
st Y+,r2
dec r20
brne cycle2
; === Переходим к следующему отсчету
lds r16,arrpos
lds r17,arrpos+1
ldi r28,1
ldi r29,0
add r16,r28
adc r17,r29
; Сравниваем с 2400 - если уже равно, надо сбрасывать счетчик в ноль
; 1200 = 0x960
ldi r29,0x04
sub r29,r16
brne notclearpos
ldi r29,0xB0
sub r29,r17
brne notclearpos
; Сбрасываем указатель в ноль
ldi r16,0
ldi r17,0
notclearpos:
sts arrpos,r16
sts arrpos+1,r17
; Отсчет увеличили
; === Тут проверяем, не запустить ли передачу данных?
; 1157 = 0x485
ldi r29,0x04
sub r29,r16
brne nilabel
ldi r29,0x85
sub r29,r17
brne nilabel
; Да. Запускаем передачу - получен 2397-й отсчет
ldi r16,0x00
sts rscrc,r16 ; Сбрасываем контрольную сумму
; Заносим нулевую позицию в буфере
sts rspos,r16
sts rspos+1,r16
ldi r16,0x01
sts rsstate,r16 ; Переходим в состояние "1" процедуры отправки пакета в порт
sbi UCSR0A,5 ; Разрешаем прерывание от освобождения буфера передачи
ldi r16,0x55
out UDR0,r16 ; Начинаем передавать первый байт пакета. После того как байт начнет передаваться, произойдет прерывание от опустошения буфера
; Передача начата. Дальнейшее сделает процедура обработки буфера передачи
nilabel:
pop r31
pop r30
pop r29
pop r28
pop r20
pop r17
pop r16
pop r3
pop r2
pop r1
pop r0
reti
;================================================
; Основная программа
main:
; Конфигурируем стек
ldi r16,0
out SPL,r16
ldi r16,0x10
out SPH,r16
; Конфигурируем порты
ldi r16,0
out DDRA,r16
out DDRC,r16
out DDRE,r16
sts DDRF+0x20,r16
ldi r16,0x7F
out DDRB,r16
ldi r16,0x40
out DDRD,r16
ldi r16,0
out PORTB,r16
out PORTD,r16
; Конфигурируем ОЗУ - будем использовать диапазон 0x8000-0xFFFF
in r16,MCUCR
ori r16,0x80
out MCUCR,r16
ldi r16,0x00 ; ОЗУ работает с минимальными задержками по всем адресам
sts XMCRA+0x20,r16
ldi r16,0x80 ; Полный диапазон адресов, Bus keeper включен
sts XMCRB+0x20,r16
; Конфигурируем UART, скорость 38400, прерывания на прием сразу разрешены
ldi r16,0x20
out UCSR0A,r16 ; Удвоения скорости нет
ldi r16,0x98
out UCSR0B,r16 ; Разрешены прерывания от приема байта, включен приемник и передатчик, 8 бит данных
ldi r16,0x06
sts UCSR0C+0x20,r16 ; Асинхронный режим, 1 стопбит, 8 бит данных
ldi r16,0x00
sts UBRR0H+0x20,r16
ldi r16,11
out UBRR0L,r16 ; Скорость - 38400. Константа взята из datasheet на контроллер
; Конфигурируем таймер0 на генерацию 1/400 секундных интервалов
ldi r16,0x04 ; Входы и выходы таймера не используются, делитель на 64
out TCCR0,r16
ldi r16,0x00 ; Синхронизация от внутренней частоты
out ASSR,r16
ldi r16,0xCD ; 288 - делитель для получения нужного периода
out OCR0,r16
in r16,TIMSK
ori r16,0x02
out TIMSK,r16
; Конфигурируем АЦП
ldi r16,0xC0 ; Внутренний 2.56В источник опорного напряжения, правое выравнивание результата, канал 0
out ADMUX,r16
ldi r16,0x8E ; АЦП разрешен, делитель на 64, прерывания от АЦП разрешены, измерение выключено
out ADCSR,r16
; Устанавливаем начальные значения переменных
ldi r16,0xFF
sts work,r16 ; Начинаем с теста ОЗУ
ldi r16,0
sts adcsigma,r16
sts adcsigma+1,r16
sts adcsigma+2,r16
sts adcsigma+3,r16
sts adcsigma+4,r16
sts adcsigma+5,r16
sts adcsigma+6,r16
sts adcsigma+7,r16
sts adcsigma+8,r16
sts adcsigma+9,r16
sts adcsigma+10,r16
sts adcsigma+11,r16
sts adcsigma+12,r16
sts adcsigma+13,r16 ; Сумм нету
sts adcnum,r16 ;
sts arrpos,r16 ; Позиция - нулевая
sts arrpos+1,r16 ;
sts rsstate,r16 ; Ничего не передается
sts timerdiv,r16
; Основной цикл программы
sei ; Разрешаем прерывания
maincycle:
ldi r16,work
or r16,r16
breq maincycle
; Сбрасываем флаг
ldi r16,0
sts work,r16
; Здесь - процедура теста ОЗУ
ldi r30,0
ldi r31,0x80
ldi r16,0
ldi r17,3
; Заполнение
wr1clk:
st Z+,r16
add r16,r17
sbrs r31,6
rjmp wr1clk
; Проверка прямого значения и заполнение инверсным
ldi r30,0
ldi r31,0x80
ldi r16,0
ldi r17,3
wr2clk:
ld r18,Z
com r18
st Z+,r18
sub r18,r16
nop ; brne chkerr
add r16,r17
sbrs r31,6
rjmp wr2clk
; Проверка инверсного значения
ldi r30,0
ldi r31,0x80
ldi r16,0
ldi r17,3
wr3clk:
ld r18,Z+
com r18
sub r18,r16
nop ; brne chkerr
add r16,r17
sbrs r31,6
rjmp wr3clk
; Проверка прошла успешно
; Отключаем светодиод "ошибка"
in r16,PORTD
andi r16,0xBF
out PORTD,r16
; Переходим к состоянию "передача блока удачной проверки"
ldi r16,20
jmp startpkg
chkerr:
; Проверка завершилась ошибкой
; Включаем светодиод "ошибка"
in r16,PORTD
ori r16,0x40
out PORTD,r16
; Переходим к состоянию "передача блока ошибки проверки"
ldi r16,40
startpkg: ; Запуск передачи блока
cli ; Запрещаем прерывания - дальше идет блок, работающий с теми же данными, что и процедуры прерывания
sts rsstate,r16 ; Переходим в состояние "1" процедуры отправки пакета в порт
sbi UCSR0A,5 ; Разрешаем прерывание от освобождения буфера передачи
ldi r16,0xA5
out UDR0,r16 ; Начинаем передавать первый байт пакета. После того как байт начнет передаваться, произойдет прерывание от опустошения буфера
sei ; Разрешаем прерывания
ldi r16,0 ;
sts chkcnt,r16 ; Блоки считаем с нуля - повторный тест через 10 блоков
jmp maincycle
Список используемой литературы.
-
Булычев А…Л…Галкин В. И.,Прохоренко В. А. Аналоговые интегральные схемы.
Справочник.- 2-е издание, переработанное и дополненное.- Минск: Беларусь,1993.
-
Якубовский С.В. и др. Цифровые и аналоговые интегральные микросхемы.
Справочник.- М: Радио и связь.1989.
-
Бродин В.Б.,Шагурин И.И. Микроконтроллеры. Архитектура, программирование,
Интерфейс.- М.:ЭКОМ,1999.
-
Сайт фирмы Atmel- www.atmel.com.
-
Сайт фирмы Texas Instruments- www.ti.com.
-
Сайт фирмы Maxim Dallas Semiconductor- www.maxim-ic.com.
-
Сайт фирмы Intel- www.intel.com.