Разработка электроники,

Систем автоматики,

Программного обеспечения

ООО "Антех ПСБ",
Санкт-Петербург

+79811865082

anteh@bk.ru

ООО Антех ПСБ примеры

8 bit AVR TWI/I2C RTC DS1307 DS1338. Драйвер DS13xx на AVR Assembler. Демонстрация работы мультизадачного драйвера для DS13xx

26.10.2015 https://anteh.ru

Речь об использовании аппаратного TWI/I2C интерфейса 8 битных AVR контроллеров для управления RTC DS1307 (VCC 4.5-5.5V) или его более современного аналога DS1338 (DS1338-18 1.71-5.5V, DS1338-3 2.7-5.5V, DS1338-33 3.0-5.5V).
Тема избитая, для бытового применения RTC+LCD или USART и пр. Для промышленного решения, когда RTC является одним из винтиков в общей массе используемых узлов, доступного решения не нашлось. Код должен быть универсальным, компактным, с минимальными задержками на выполнение, учитывать возможные нюансы работы RTC. По корыстным причинам "вылизанный" код, удовлетворяющий всем перечисленным требованиям, не приводится. Приведён полностью рабочий код AVR Assembler, который позволит быстро разобраться, что к чему и при желании довести до своего совершенства.
Рассматриваются режимы работы TWI Master transmitter(MT), Master receiver(MR). Режимы Slave transmitter(ST), Slave receiver(SR) не рассматриваются.

Фото первого макета разрабатываемого контроллера на котором отрабатывалась работа с DS1338-33:

контроллер ds1338 1307


При первоначальной подаче питания на DS13xx с подключённой 3V батареей резервного питания её регистры находятся в произвольном состоянии. Нужно их инициализировать. По крайней мере, первые 8 отвечающие за работу и настройки RTC. Описание регистров есть в многочисленной документации и сети. Документацию лучше искать на DS1307

Пишем драйвер
Инициализация TWI:
Пины SCL SDA AVR контроллера настраиваем в третье состояние. В коем они по умолчанию и находятся. TWI выход DS13xx, это открытый сток или коллектор, нужно подтянуть к VCC. Можно использовать встроенные подтягивающие резисторы AVR контроллера. Но их номинал для больших скоростей обмена велик 20k-100k и подойдёт для работы TWI/I2C на малых частотах с небольшим количеством устройств на шине. Номинал резистора зависит от рабочей частоты, от количества устройств на шине, емкости шины. Есть рекомендация 1k-4.7k при +5V. Документ AVR1320 рекомендует 2k для частоты 400kHz. Если устройств на шине несколько, то для уверенности в надёжности обмена данными, нужно воспользоваться осциллографом, импульсы на SCL SDA должны быть похожи на прямоугольные. Ниже будет осциллограмма для 1.2k pull-up резисторов, AtMega128A, 320kHz и одно устройство на шине DS1338-33.
Для AtMega128A настройка SCL SDA в третье состояние:

		;PD0	SCL I2C ThreeState
		;PD1	SDA I2C ThreeState
		;PD2
		;PD3
		;PD4
		;PD5
		;PD6
		;PD7
		ldi 	r16, 0b00000000
		out 	DDRD, r16
		ldi 	r16, 0b00000000
		out 	PORTD, r16

Настройка TWI регистров:

		ldi 	r27, high(TWBR)	;400kHz на кварце 16.0MHz
		ldi 	r26, low(TWBR)
		ldi 	r16, 12
		st		X+, r16
		ldi 	r16, 0b00000000	;TWSR
		st		X, r16		

		call	DS1338_I ;Инициализация RTC. Если первая подача напряжения, 
						 ;то полная инициализация в том числе и дефолтным 
						 ;временем. Если не первая, то инициализация 
						 ;частичная/“пожарная”

Другие TWI регистры не трогаем.

Добавляем буфер для работы с DS13xx:

.dseg
	StartRAM:
		…
		DS1338_buf: .BYTE 67	;Буфер DS1338 для обмена данными c DS13xx
		…
	EndRAM:

Область между StartRAM EndRAM обнуляется при инициализации контроллера:

		ldi 	r29, high(StartRAM)		;* Обнуляем память данных
		ldi 	r28, low(StartRAM)		;*
		ldi 	r27, high(EndRAM-StartRAM)	;*
		ldi 	r26, low(EndRAM-StartRAM)	;*
	nxzr34:								;*
		st		Y+, r15					;*
		ld		r16, -X					;*
		or		r27, r27				;*
		brne	nxzr34					;*
		or		r26, r26				;*
		brne	nxzr34					;*

		clr		r25		;GIR0 регистр глобальных флагов
		clr		r15		;Нулевой регистр

В памяти программ создаём 2 посылки инициализации DS13xx

.cseg	
		;Инициализирующая посылка DS1338
		; 16.10.2015 пятница(5) 21:15:33
		;                LENG  SADR  adr   00h         01h   02h         03h   04h   05h   06h   07h         выравнивающий
		DS1338_init: .DB 0x0a, 0xd0, 0x00, 0b00110011, 0x15, 0b00100001, 0x05, 0x16, 0x10, 0x15, 0b00000000, 0x00
		;                                  33          15    21          5     16    10    15
		;                    LENG  SADR  adr   07h   
		DS1338_sub_init: .DB 0x03, 0xd0, 0x07, 0x00
		;LENG -количество байт в посылке к передаче

DS1338_init –последовательность байт записываемая при инициализации DS13xx при первом включении, когда DS1338_first_init_flag !=0xA5
DS1338_sub_init –последовательность байт записываемая при каждом перезапуске контроллера. По непроверенным слухам 07h конфигурационный регистр DS13xx может слетать. Поэтому пока предусмотрена процедура его инициализации при запуске контроллера. Сюда же можно добавить проверку корректности CH бита –в чём смысла не вижу, ибо если он действительно слетает, то вместе с ним нужно и время подправлять. Если SQW/OUT не используется, то "слетане" 07h ни на что не повлияет. Нужно уделить внимание стабильности 32.768kHz кварца.

Функция копирования из CSEG в DSEG:

;X -DSEG
;Z -CSEG
CSEG_to_DSEG_with_start_LEN:
		add		r30, r30
		adc		r31, r31
		lpm		r17, Z+
		st		X+, r17
	CSEG_to_DSEG_nxrd:
		lpm		r16, Z+
		st		X+, r16
		dec		r17
		brne	CSEG_to_DSEG_nxrd
ret

В ESEG размещаем:

eseg
		DS1338_first_init_flag: .DB 0x00 ;Флаг инициализации DS1338 при 
										 ;первоначальной подаче питания. 
										 ;Если =0xA5 то инициализация была

Для работы с DS13xx достаточно 2х функций записи и чтения регистров:
(1)
;Функция используемые регистры не сохраняет. В прерываниях не запускать
;Z -Передаваемая последовательность байт в DS1338 из DSEG
;Формат посылки записи данных в регистры DS13xx:
;DS1338_buf = [N][SLA+W][adr][data0][data1]...[data63]
;[N] -количество байт передаваемых в DS13xx без учёта самого байта длины
;[SLA+W] =0xD0 статический адрес SLAVE DS13xx и флаг записи данных
;[adr] -адрес регистра начала записи в DS13xx
;[data0]-[data63] -значения регистров DS13xx любое количество байт, в том числе и =0 т.е. данных может не быть, нужно для настройки на чтение данных с произвольного регистра DS13xx
Z_to_DS1338:

ret
(2)
;Чтение данных из DS13xx
;Для настройки на нужный регистр для чтения, нужно произвести запись в DS13xx байта статического адреса и байта адреса регистра. Затем произвести операцию чтения, в DS1338_to_Z это реализовано в функции автоматически
;Формат последовательности байт чтения из DS13xx:
;В функцию DS1338_to_Z передаётся: DS1338_buf = [N][adr]
;[N] -количество байт читаемых из DS13xx
;[adr] -адрес регистра начала чтения из DS13xx
;Из функции DS1338_to_Z возвращается DS1338_buf = [N][data0][data1]..[dataN]
;[N] -количество байт прочтённое из DS13xx
;[data0][data1]..[dataN] -прочитанные данные
DS1338_to_Z:

ret

Обмен данными сводится к заполнению DS1338_buf посылкой к чтению или записи в приведённом выше формате и запуску функции Z_to_DS1338(запись) или DS1338_to_Z(чтение).

Функция инициализации DS13xx с проверкой, была ли произведена полная инициализация. И если была, то запуск частичной инициализации:

;Инициализация DS1338
DS1338_I:
		ldi		r27, high(DS1338_first_init_flag) ;Чтение флага произведённой первоначальной инициализации
		ldi		r26, low(DS1338_first_init_flag)
		call	EEPROM_X_to_r16_read
		cpi 	r16, 0xa5
		breq	DS1338_I_NoNeedTimeInit

		ldi 	r31, high(DS1338_init) ;Копирование полной инициализирующей посылки в DS1338_buf
		ldi 	r30, low(DS1338_init)
		ldi		r27, high(DS1338_buf)
		ldi		r26, low(DS1338_buf)		
		call	CSEG_to_DSEG_with_start_LEN
	DS1338_I_repeat:

		clt					;T -флаг ошибки, если T=1 то обмен данными завершился ошибкой
		call	Z_to_DS1338
		brts	DS1338_I_Error
		
		ldi		r16, 0xa5	;Установка флага инициализации DS1338 при первоначальной подаче напряжения
		ldi		r27, high(DS1338_first_init_flag)
		ldi		r26, low(DS1338_first_init_flag)
		call	EEPROM_r16_to_X_write
	DS1338_I_Error:
ret
	DS1338_I_NoNeedTimeInit:
		ldi 	r31, high(DS1338_sub_init) ;Запись 07h байта в DS1338 на всякий пожарный по слухам может слететь
		ldi 	r30, low(DS1338_sub_init)
		ldi		r27, high(DS1338_buf)
		ldi		r26, low(DS1338_buf)		
		call	CSEG_to_DSEG_with_start_LEN		
		rjmp	DS1338_I_repeat

И теперь собственно сами загадочные и волшебные Z_to_DS1338 и DS1338_to_Z:

;Функция используемые регистры не сохраняет. В прерываниях не запускать
;Z -Передаваемая последовательность байт в DS1338 из DSEG
;Формат посылки записи данных в DS13xx:
;[N][SLA+W][adr][data0][data1]...[data63]
;[N] -количество байт передаваемых в DS13xx без учёта самого байта длины
;[SLA+W] =0xD0 статический адрес SLAVE DS13xx и флаг записи
;[adr] -адрес регистра начала записи в DS13xx
;[data0]-[data63] -значения регистров DS13xx любое количество байт, в том числе и =0
Z_to_DS1338:
		ldi 	r31, high(DS1338_buf)
		ldi 	r30, low(DS1338_buf)
		ld		r17, Z+		;Количество байт к записи в DS1338

		ldi 	r27, high(TWCR)
		ldi 	r26, low(TWCR)
		ldi 	r29, high(TWSR) ;Статусный регистр
		ldi 	r28, low(TWSR)

		;[S] START
		ldi		r16, (1<<TWINT)|(1<<TWSTA)|(1<<TWEN) ;Возможная очистка TWINT, установка TWSTA бита передачи стартового признака и разрешение работы TWI
        st		X, r16
		;[Wait]
		call	r16_to_DS1338_wait  ;Ожидание выполнения операции
		;[Check status]
		ld		r16, Y				;Проверка статуса операции
		andi 	r16, 0b11111000
		cpi		r16, 0x08			;A START condition has been transmitted
		brne	r16_to_DS1338_ERROR

		;[Send address to DS1338]
		ld		r16, Z+		;Байт адреса
		st		-X, r16		;TWDR загрузка в передающий регистр
		ldi 	r16, (1<<TWINT) | (1<<TWEN)
		adiw	r26, 1
		st		X, r16		;TWCR
		;[Wait]
		call	r16_to_DS1338_wait  ;Ожидание выполнения операции
		;[Check status]
		ld		r16, Y				;Проверка статуса операции
		andi 	r16, 0b11111000		
		cpi		r16, 0x18			;SLA+W has been transmitted ACK has been received
		brne	r16_to_DS1338_ERROR

		dec		r17	;Байт адреса передан

		;[Send register address and data to DS1338]
	DS1338_I_nxbr:		
		ld		r16, Z+
		st		-X, r16		;TWDR загрузка в передающий регистр
		ldi 	r16, (1<<TWINT) | (1<<TWEN)
		adiw	r26, 1
		st		X, r16		;TWCR
		;[Wait]
		call	r16_to_DS1338_wait  ;Ожидание выполнения операции
		;[Check status]
		ld		r16, Y				;Проверка статуса операции
		andi 	r16, 0b11111000
		cpi		r16, 0x28			;Data byte has been transmitted ACK has been received 
		brne	r16_to_DS1338_ERROR

		dec		r17
		brne	DS1338_I_nxbr

		;[P] STOP
		ldi 	r16, (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)
		st		X, r16	;STOP signal to TWCR
ret
	r16_to_DS1338_ERROR:
		st		X, r15 ;TWCR = 0b00000000
		set		;T=1 установка признака ошибки TWI обмена
ret

r16_to_DS1338_wait: ;Ожидание выполнения операции
        ld		r19, X
	    sbrs	r19, TWINT
		rjmp	r16_to_DS1338_wait
ret
;Чтение из DS13xx
;Для настройки на нужный регистр для чтения нужно произвести запись в DS13xx байта статического адреса и байта адреса регистра
;Затем произвести операцию чтения, в DS1338_to_Z это реализовано автоматически
;Формат последовательности байт чтения из DS13xx:
;В функцию DS1338_to_Z передаётся: DS1338_buf = [N][adr]
;[N] -количество байт читаемых из DS13xx
;[adr] -адрес регистра начала чтения из DS13xx
;Из функции DS1338_to_Z возвращается S1338_buf = [N][data0][data1]..[dataN]
;[N] -количество байт прочтённое из DS13xx
;[data0][data1]..[dataN] -прочитанные данные
DS1338_to_Z:
		;Настраиваемся на нужный адрес
		ldi 	r31, high(DS1338_buf) ;Настройка буфера
		ldi 	r30, low(DS1338_buf)
		ld		r16, Z+ ;Сохранение количества байт к чтению
		push 	r16
		ld		r17, Z	;Адрес начала чтения		
		ldi 	r16, 2		;Количество байт записываемых в DS13xx
		st		-Z, r16
		ld		r16, Z+		;Настройка на нужный байт
		ldi 	r16, 0xd0	;Адрес DS13xx с признаком запись
		st		Z+, r16
		st		Z, r17		;Адрес начала чтения
		call	Z_to_DS1338 ;Настраиваем указатель DS13xx на нужный регистр

		;Производим чтение заданного количества байт в DS1338_buf, начиная с указанного адреса
		ldi 	r31, high(DS1338_buf)
		ldi 	r30, low(DS1338_buf)
		pop		r17			;Восстанавливаем количество байт к чтению
		st		Z+, r17


		ldi 	r27, high(TWCR)
		ldi 	r26, low(TWCR)
		ldi 	r29, high(TWSR) ;Статусный регистр
		ldi 	r28, low(TWSR)

		;[S] START
		ldi		r16, (1<<TWINT)|(1<<TWSTA)|(1<<TWEN) ;Возможная очистка TWINT, установка TWSTA бита передачи стартового признака и разрешение работы TWI
        st		X, r16
		;[Wait]
		call	r16_to_DS1338_wait  ;Ожидание выполнения операции
		;[Check status]
		ld		r16, Y				;Проверка статуса операции
		andi 	r16, 0b11111000
		cpi		r16, 0x08			;A START condition has been transmitted
		brne	r16_to_DS1338_ERROR


		;[Send address to DS1338 with R]
		ldi		r16, 0xd1	;Байт адреса с признаком чтения
		st		-X, r16		;TWDR загрузка в передающий регистр
		ldi 	r16, (1<<TWINT) | (1<<TWEN)
		adiw	r26, 1
		st		X, r16		;TWCR
		;[Wait]
		call	r16_to_DS1338_wait  ;Ожидание выполнения операции
		;[Check status]
		ld		r16, Y				;Проверка статуса операции
		andi 	r16, 0b11111000		
		cpi		r16, 0x40			;SLA+R has been transmitted ACK has been received
		brne	r16_to_DS1338_ERROR

		;Производим чтение r17 байт из DS13xx
	ReadNextBytes45:
		dec		r17
		brne	ReadNoLastByte		
		ldi 	r16, (1<<TWINT|1<<TWEN) ;Чтение последнего байта
		rjmp	dg5yks89dwm46
	ReadNoLastByte:
		ldi 	r16, (1<<TWINT|1<<TWEN|1<<TWEA) ;чтение не последнего байта
	dg5yks89dwm46:

		st		X, r16 ;TWCR
		;[Wait]
		call	r16_to_DS1338_wait  ;Ожидание выполнения операции
		;[Check status]
		ld		r16, Y				;Проверка статуса операции
		andi 	r16, 0b11111000		
		or		r17, r17
		brne	NoLastByteStatusCheck
		cpi		r16, 0x58			;Data byte has been received NOT ACK has been returned
		brne	r16_to_DS1338_ERROR
		rjmp	dfe4hbse46
	NoLastByteStatusCheck:
		cpi		r16, 0x50			;Data byte has been received ACK has been returned
		brne	r16_to_DS1338_ERROR
	dfe4hbse46:
		;[Safe received data]
		ld		r16, -X	;Чтение TWDR
		st		Z+, r16
		ld		r16, X+	;Возврат на TWCR

		or		r17, r17
		brne	ReadNextBytes45

		;[P] STOP
		ldi 	r16, (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)
		st		X, r16	;STOP signal to TWCR
ret

Из функций чтения записи можно убрать проверку статуса операции, что значительно сократит количество кода. Проверка использовалась для отладки.
Оптимально убрать из функций задержки на ожидание выполнения TWI операций. Для этого нужно организовать независимое/мульти задачное/параллельное выполнение кода обмена данными с RTC таймером, это позволит читать время из RTC на любой частоте не оказывая влияния на работу остального кода. Влияние будет в несколько десятков тактов, запрет прерываний в коде обмена с DS13xx не используется. Ниже приведена видео демонстрация работы такого кода.

 

Чтение данных состоит из 2х операций:

1. 2 байта, записываются в DS13xx настраивая указатель на регистр, с которого будет производиться чтение. Это TWI адрес DS13xx и адрес регистра, с которого начнётся чтение данных.

2. Чтение 7 байт даты и времени. Последний, конфигурационный байт не читается, не нужен.

чтение даты времени из ds1307 ds1338

Осциллограммы pull-up=1.2k Ftwi=320kHz DS1338-33

ds1307 ds1308 read data oscilloscope

Более подробно:

осциллограмма чтения данных из ds1338 ds1307 anteh.ru

Питание DS1338-33: VCC=5V.

На осциллограммах чётко просматривается короткий импульс после восьмого бита при записи данных в DS13xx и после 9го при чтении полагаю, это связано с ACK.

Если кому интересно: DS1338-33 Rpull-up=1.2k, постоянный опрос без задержек или опрос с периодом 2ms, не работает на Ftwi=770kHz, TWBR=1. На Ftwi=715kHz работает TWBR=2.

Проверил, для DS1338-33 на Ftwi=715kHz с TWBR=2 работает отлично с Rpull-up=1.2k и =4.7k с 4.7k фронты конечно "покруглее", но работает чётко. Для максимально заявленной в документации на DS1338-33 Ftwi=400kHz pull-up можно и побольше поставить. А если выбрать частоту ещё меньше, то и встроенных pull-up контроллера будет достаточно. Минимальная частота для кварца контроллера 14.7456MHz Ftwi=444 Hz. Проверено, DS1338-33 VCC=5V минимум работает на 444Hz-740kHz частотах TWI Rpull-up=4.7k.

Качество видео, демонстрирующего мульти задачный обмен данными с DS13xx вышло не очень, но смысл передаёт. Видео демонстрирует, реализацию вышеприведённого подхода. Обмен с DS1338-33 на Ftwi=444 Hz, кварц AtMega128A=14.7456MHz, TWBR=255 и TWSP=11 пред делитель TWI =64. И циклическая передача посылки данных между USART0 USART1 2xRS485 на 115200. Посылка состоит из 2х частей первая статическая около 30 байт, она формируется только один раз при инициализации контроллера и далее циркулирует от USART0 к USART1 и обратно в бесконечном цикле. Т.е. если произойдут какие-либо сбои или ошибки, то посылка будет испорчена и это сразу будет видно по выводу снифера. Вторая часть этой же посылки динамическая -это дата и время читаемое с DS1338-33, эта часть посылки формируется с заданным интервалом -270ms и длится 210ms. Напоминаю Ftwi=444 Hz кварц AtMega128A=14.7456MHz настройки TWI на минимальную частоту чтобы показать, что TWI и 2 USARTа, настроенные на максимальную частоту, друг другу не мешают.

(1) По X 100 мкс в клетке. Частота Ftwi=740kHz USART0 и USART1 =115200. Опрос DS1338 постоянный. Желтый верхний канал SCL, синий средний SDA, сиреневый нижний USARTx RS485 дифференциальный выход. Видео демонстрирует отсутствие каких либо задержек при смежной работе 2хUSART и TWI и ещё ряда функций:

(2) Частоты TWI и USART те же. Но добавлен период опроса DS1338 несколько десятков миллисекунд. Демонстрируется небольшое влияние на TWI работы USARTов и остального кода прошивки, это выражено в псевдо случайном увеличении периодов между байтами данных TWI:

Задержки можно уменьшить, если у используемого контроллера TWI регистры будут с прямым доступом а также, для хранения промежуточных переменных использовать свободные регистры, а не память.

(3) Частота Ftwi=440 Hz. Частота RS485 USART 115200. Опрос постоянный. TWI также не вносит никаких задержек и сам не теряется:

(4) Тоже, что и (3) но опрос DS13xx с периодом несколько десятков миллисекунд, что позволяет детально разглядеть обмен чтения даты времени по TWI:

За дорого, по безналичному расчёту могу предложить выше расхваливаемую мультизадачную функцию, точнее несколько функций управления DS13xx RTC таймером. AVR Assembler. Помимо всего можно удобно читать и записывать все регистры DS1338 DS1307. Рассматривается вопрос разработки подобного кода под Ваши устройства для самых разнообразных датчиков и контролируемых узлов

 

Copyright ©Новиков Алексей Александрович,

2023 Санкт-Петербург, 197372, ООО "Антех ПСБ",

anteh собака bk.ru