avr modbus вопросы

Обсуждаем контроллеры компании Atmel.
Ответить
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 386
Зарегистрирован: Чт окт 31, 2013 10:54:32

Сообщение alex38779 »

Z_h_e писал(а): Для этого используйте указатели. Если скорости передачи и обработки принятого позволяют, то можно обойтись и без буферного массива.
Попробую реализовать :shock:
Z_h_e писал(а): Параметры UART еще надо знать, т.е. время передачи одного байта.
Скорость возьму 19200, кол-во байт передаваемых маcтером 10. t = 10/19200, с учетом что t = 1.5 времени передачи байта, t = ~0.8
Как писал выше Alkul, время должно быть примерно 0,8мс

И еще раз приведу расчет:

частота 8МГц
предделитель 256
время переполнения таймера 0,8мс

256/8000000 = 0,032мс
0,8мс/0,032мс = 25
Начальное значение TCNT = 256-25 = 231


Для тестирования хочу сделать отправку через прерывание опустошения буфера. Но почему то шлет постоянно только первое значение 0x30. Без прерывания отправка работает, но так не хочу делать, так как будет отниматься время работы программы, в процессе тупления в цикле в ожидании флага передачи.

Настройки прерывания такие:

Код: Выделить всё

 UCSRB |= ( 1 << TXEN ) | ( 1 <<RXEN );
	/*Разрешаем прерывание на  передачу и опустошение буфера*/
	UCSRB |=  1<<TXCIE; // | 1<<RXCIE;
	UCSRB |=  1<<UDRIE;
        sei();	
Массив данных:

Код: Выделить всё

char sendBuffer[5] = 
{
	0x30,0x00,0x01,0x02,0xaa,
};
Прерывание:

Код: Выделить всё

/*Индекс переданных данных*/
volatile uint8_t indexByteSend = 0;

/*Отправка байта*/
ISR(USART_UDRE_vect)
{		
	if (indexByteSend > 5)
	{	
		UCSRB &= ~(1<<UDRIE);					
	}
	else
	{		
		UDR = sendBuffer[indexByteSend];	
		indexByteSend++;	
	}		
}
P.S. возможно это глюк протеуса, т.к. во время симуляции дергается курсор быстро. Потом добавлял светодиод в условие передачи байта, что бы он загорелся, как туда программа зайдет, а он мигал... Значить контроллер сбрасывается в начальное состояние. Поэтому и шлется только 1 байт, и все заново начинается.

Надо подождать когда придет реальное железо..
Контактная информация:
Реклама
Друг Кота
Аватара пользователя
Сообщения: 6323
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

Опробованная п/п расчета CRC-16 .
Быстрая, но требует "лишних" 512 быйт флеши, так что для Тинек не пойдет.
Источник кода у нас Alkulом один, но я свой перепёр на асм, потому он быстрее :)
Спойлер

Код: Выделить всё

 ; Это вход для произвольного массива,
 ; адрес и длина которого должны уже
 ; быть в XL,XH,r17 соответственно .
 ; Таблицу нужно размещать  на  границе "страницы",
 ;  чтобы адрес  ее   был  .org 0x?00 или  0x?80 .
 ;
 ; Результат  в R24 (Lo), R25( Hi)

	;
CRC_R:	push	ZL
	push	ZH
	ldi	r25,0xFF
	ldi	r24,0xFF
	;
crc_l1:	ld	ZL,x+
	eor	ZL,r25 ; i=Hi^[x]
	ldi	ZH,high(CRCHi*2)
	lpm
	eor	r24,r0 ; Hi=Lo^CRCHi[i]
	mov	r25,r24
	ldi	ZH,high(CRCLo*2)
	lpm
	mov	r24,r0 ; Lo=CRCLo[i]
	dec	r17
	brne	crc_l1
	;
crc_22:	pop	ZH
	pop	ZL
	ret
 
.org	0xF00

CRCHi:
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40
.db	0x01,0xC0,0x80,0x41
.db	0x01,0xC0,0x80,0x41
.db	0x00,0xC1,0x81,0x40

CRCLo:
.db	0x00,0xC0,0xC1,0x01
.db	0xC3,0x03,0x02,0xC2
.db	0xC6,0x06,0x07,0xC7
.db	0x05,0xC5,0xC4,0x04
.db	0xCC,0x0C,0x0D,0xCD
.db	0x0F,0xCF,0xCE,0x0E
.db	0x0A,0xCA,0xCB,0x0B
.db	0xC9,0x09,0x08,0xC8
.db	0xD8,0x18,0x19,0xD9
.db	0x1B,0xDB,0xDA,0x1A
.db	0x1E,0xDE,0xDF,0x1F
.db	0xDD,0x1D,0x1C,0xDC
.db	0x14,0xD4,0xD5,0x15
.db	0xD7,0x17,0x16,0xD6
.db	0xD2,0x12,0x13,0xD3
.db	0x11,0xD1,0xD0,0x10
.db	0xF0,0x30,0x31,0xF1
.db	0x33,0xF3,0xF2,0x32
.db	0x36,0xF6,0xF7,0x37
.db	0xF5,0x35,0x34,0xF4
.db	0x3C,0xFC,0xFD,0x3D
.db	0xFF,0x3F,0x3E,0xFE
.db	0xFA,0x3A,0x3B,0xFB
.db	0x39,0xF9,0xF8,0x38
.db	0x28,0xE8,0xE9,0x29
.db	0xEB,0x2B,0x2A,0xEA
.db	0xEE,0x2E,0x2F,0xEF
.db	0x2D,0xED,0xEC,0x2C
.db	0xE4,0x24,0x25,0xE5
.db	0x27,0xE7,0xE6,0x26
.db	0x22,0xE2,0xE3,0x23
.db	0xE1,0x21,0x20,0xE0
.db	0xA0,0x60,0x61,0xA1
.db	0x63,0xA3,0xA2,0x62
.db	0x66,0xA6,0xA7,0x67
.db	0xA5,0x65,0x64,0xA4
.db	0x6C,0xAC,0xAD,0x6D
.db	0xAF,0x6F,0x6E,0xAE
.db	0xAA,0x6A,0x6B,0xAB
.db	0x69,0xA9,0xA8,0x68
.db	0x78,0xB8,0xB9,0x79
.db	0xBB,0x7B,0x7A,0xBA
.db	0xBE,0x7E,0x7F,0xBF
.db	0x7D,0xBD,0xBC,0x7C
.db	0xB4,0x74,0x75,0xB5
.db	0x77,0xB7,0xB6,0x76
.db	0x72,0xB2,0xB3,0x73
.db	0xB1,0x71,0x70,0xB0
.db	0x50,0x90,0x91,0x51
.db	0x93,0x53,0x52,0x92
.db	0x96,0x56,0x57,0x97
.db	0x55,0x95,0x94,0x54
.db	0x9C,0x5C,0x5D,0x9D
.db	0x5F,0x9F,0x9E,0x5E
.db	0x5A,0x9A,0x9B,0x5B
.db	0x99,0x59,0x58,0x98
.db	0x88,0x48,0x49,0x89
.db	0x4B,0x8B,0x8A,0x4A
.db	0x4E,0x8E,0x8F,0x4F
.db	0x8D,0x4D,0x4C,0x8C
.db	0x44,0x84,0x85,0x45
.db	0x87,0x47,0x46,0x86
.db	0x82,0x42,0x43,0x83
.db	0x41,0x81,0x80,0x40
Последний раз редактировалось Jack_A Ср окт 05, 2016 17:57:56, всего редактировалось 3 раза.
Изображение
Реклама
Держит паяльник хвостом
Сообщения: 933
Зарегистрирован: Ср апр 13, 2011 11:09:20
Откуда: Екатеринбург

Сообщение Alkul »

Z_h_e писал(а):Да наверное его даже и не надо останавливать, лишь обновить значение, особенно если использовать прерывание по переполнению.
Я сталкивался с тем, что программа иногда сбоила, если таймер модифицировался без предварительной остановки.

alex38779 писал(а):Начальное значение TCNT = 256-25 = 231
Константу для таймера Вы рассчитали правильно.

А вот это
alex38779 писал(а):

Код: Выделить всё

UCSRB |=  1<<TXCIE; // | 1<<RXCIE;
	UCSRB |=  1<<UDRIE;
неправильно.
Вы пытаетесь одновременно использовать прерывание TXC и UDRE (первое вызывается после завершения отправки байта, второе - когда буфер отправки пустой). Это неверно, нужно использовать что-то одно, поэтому вот это (в случае использования моего примера ниже)

Код: Выделить всё

UCSRB |=  1<<UDRIE;
надо убрать.

Я пользуюсь прерыванием TXC. Оно вызывается сразу после отправки байта, поэтому по нему удобно делать отправку следующего байта. Вот пример отправки пакета. Так как используется интерфейс RS-485, то вывод SEL контроллера (любой удобный) управляет переключением драйвера на прием (на выводе SEL лог.0) или на передачу (на выводе SEL лог.1)

Для начала отправки пакета в основном цикле программы выполнить этот код (перед выполнением этого кода отправляемый пакет должен быть сформирован в ОЗУ начиная с адреса BUFSEND):
Спойлер

Код: Выделить всё

;Инициализировать передачу первого байта пакета. Остальные байты будут переданы автоматически по прерыванию по передаче байта
	sbi PORTD,SEL		;Перевести интерфейс RS-485 в режим передачи
	ldi R16,0x01		;Занести в ячейку SENDBYT, хранящую кол-во отправленных байт пакета,
	sts SENDBYT,R16		;число 1, т.к. отправка первого байта пакета будет инициирована сейчас
	ldi R16,0x07		;Занести в ячейку LONGSEND, хранящую длину отправляемого пакета,
	sts LONGSEND,R16		;число 7(07h) - длину отправляемого пакета 
	lds R16,BUFSEND		;Занести в рег.R16 первый байт пакета)
	sts UDR0,R16		;в регистр порта USART для отправки
Передача остальной части пакета делается в прерывании. Ниже код обработчика прерывания:
Спойлер

Код: Выделить всё

;Подпрограмма обработчика прерывания по передаче байта USART ----------------------------------------------------------------------------
adrUTXC:				; USART Tx Complete
	push R16			;Сохранить 
	lds R16,SREG		;на стеке
	push R16 			;задействованные
	push R17			;
	push YL			;
	push YH			;			
			
	lds R16,SENDBYT		;В рег. R16 занести количество переданных байт
	lds R17,LONGSEND		;В рег. R17 занести длину передаваемого пакета
	cp R16,R17		;Переданы все байты?
	brne UTXC0		;Если нет, то переход на метку UTXC0
;Иначе передача пакета завершена
	cbi PORTD,SEL		;Передача пакета ответа завершена, перевести интерфейс RS-485 в режим приема

	ldi R16,0x00		;
	sts RECVBYT,R16		;Обнулить количество принятых байт
	sts TCCR2B,R16		;Останов таймер-счетчика 2
	sts TIMSK2,R16		;Запрет прерываний по переполнению таймер-счетчика 2
	sbi TIFR2,TOV2		;

	lds R16,UCSR0A		;Сбросить
	andi R16,~(1<<RXC0)		;флаг
	sts UCSR0A,R16		;приема байта (для защиты от возможных сбоев)

	rjmp UTXC5		;Перейти к завершению обработки прерывания

;Передача пакета не завершена
UTXC0:	ldi YH,high(BUFSEND)	;В рег.пару Y занести адрес
	ldi YL,low(BUFSEND)		;начала буфера передаваемого по USART пакета ответа
	add YL,R16		;Прибавить к адресу начала буфера передаваемого пакета количество переданных байт для получения
	brcc UTXC1		;адреса текущего передаваемого
	inc YH			;байта
UTXC1:	ld R17,Y			;Занести в рег.R17 текущий передаваемый байт пакета
	sts UDR0,R17		;Занести передаваемый байт в регистр передатчика USART
	inc R16			;Инкремент количества переданных байт
	sts SENDBYT,R16		;Сохъранить в памяти новое количество переданных байт
			
UTXC5:	pop YH			;Восстановить из стека
	pop YL			;все
	pop R17			;задействованные
	pop R16			;обработчиком 
	sts SREG,R16		;прерывания
	pop R16			;регистры
			
	reti			;
Алгоритм расчета CRC в приложенном файле.
Вложения
CRC ModBUS.txt
(3.75 КБ) 161 скачивание
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 386
Зарегистрирован: Чт окт 31, 2013 10:54:32

Сообщение alex38779 »

Alkul, Jack_A, спасибо за алгоритмы :)) :)

Alkul, глянул ваш код на ассемблере.
Переделал, но все ровно не хочет работать.. В протеусе выводит как на картинке, в логе ошибки. В терминале одно 1 значение массива. И Все. :cry:
Ну и задача этого всего передать просто n раз серию байтов([хотя бы одну, но не выходит), что бы проверить прием на слейве.

Код: Выделить всё

       
	.....
	sei();	

	/*Отсюда начинается передача данных по UART*/
 /*Установили индекс элемента массива*/
	indexByteSend = 0;

	/*поместили байт в буфер UART, после опустошения переход в прерывание*/
	UDR = sendBuffer[0];

	/*Разрешаем прерывание по завершению передачи байта*/
	UCSRB |=  1<<TXCIE;	
 /*Улетаем в прерывание*/	
}

ISR(USART_TXC_vect)
{	
 /*Увеличиваем счетчик*/
	indexByteSend++;
 /*Если передали 5 байтов, то выходим из прерывания*/	
	if (indexByteSend == 5)
	{
		UCSRB &= ~(1<<TXCIE);				
	}
 /*Передаем байты*/
	else 
	{		
		UDR = sendBuffer[indexByteSend];
	}		
}
Изображение

Куда копать? :cry:
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Опытный кот
Сообщения: 804
Зарегистрирован: Чт мар 12, 2009 16:31:05

Сообщение Vov123 »

В реальное устройство.
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Vov123 писал(а):В реальное устройство.
Протеус отлично справится с данной задачей. "Реальное устройство" не есть бог, на который следует молиться, как и симулятор.
alex38779 писал(а):/*Улетаем в прерывание*/ 
Наверное после этого коммента следует бесконечно зависнуть.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Реклама
Друг Кота
Аватара пользователя
Сообщения: 6323
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

Если время не жмет ( во время передачи ничего другого не делаем )
или для просто попробовать - простая, как оглобля, программа передачи, не
использующая прерывания. Работала не по RS485, поэтому
для RS485 битик НАПРАВЛЕНИЕ_ПЕРЕДАЧИ нужно дергать отдельно.
Спойлер

Код: Выделить всё

 ; -----------------------------------
 ; Передача массива. Он лежит
 ; в SndIFC. CRC рассчитана, длина
 ; в XL.
 ;-------------------------------
send:	push	r16
	push	r17
	push	r24
	push	r25
	push	XL
      	push	XH
	in	r16,UCR
      	andi	r16,(0xFF-0x90)
	out	UCR,r16
 ; запретили прием и прерывание на прием
	ldi	r24,0xFF
rs220:	dec	r24
	ori	r24,0
	andi    r24,0xFF
	brne	rs220
; задержка
	ori	r16,8
	out	UCR,r16
;  разрешение передачи
	mov	r25,XL
    ;	LDX	SndIFC  макрос  =
    	ldi  XL,low(SndIFC)
    	ldi  XH,high(SndIFC)
     ;
repka:	ld	r16,x+
	rcall	txsub
	dec	r25
	brne	repka
     ;
 ; После занесения в сдвиговый регистр
 ; последнего байта ждем флага
 ; ПЕРЕДАЧА ОКОНЧЕНА .
wttc:	in	r16,USR
	andi	r16,0x40 ; TxC
	breq	wttc
	ori	r16,0x40
	out	UCR,r16
     ;
	ldi	r16,0x90 ; RxE,RxIE
	out	UCR,r16
	rcall	iniUART
	pop	XH
   	pop	XL
	pop	r25
	pop	r24
	pop	r17
	pop	r16
   	ret

txsub:	in	r17,USR
	andi	r17,0x20 ; URdE
	breq	txsub
	out	UDR,r16
	ret

iniUART:.........
   подготовка  UART к  приему
	  
Изображение
Держит паяльник хвостом
Сообщения: 933
Зарегистрирован: Ср апр 13, 2011 11:09:20
Откуда: Екатеринбург

Сообщение Alkul »

alex38779 писал(а):Куда копать? :cry:
Спокойно, не надо раньше времени отчаиваться :)
alex38779 писал(а):Переделал, но все ровно не хочет работать..
Во-первых, дайте Ваш код полностью. Из того, что вы дали, я не вижу, чтобы передатчик вообще разрешался. Как говорил доктор Хаус, дьявол кроется в деталях.

Во-вторых, Вы, видимо, не совсем верно всех нас поняли. Z_h_e Вам совершенно справедливо указал - Вы не должны из основного цикла программы (из main'а) - кстати, где он у Вас? - "проваливаться" в обработчик прерывания. Вам не надо его самому вызывать, он будет вызван автоматически при установке соответствующего флага прерывания., поэтому после инициализации и отправки первого байта пакета Ваша программа должна крутиться в бесконечном "холостом" цикле. Да, так серию пакетов не получишь, но давайте для начала один пакет отправим, а потом step-by-step'ом доберемся и до серии.

Третье. Не надо постоянно запрещать и разрешать прерывание по отправке байта - не надо постоянно устанавливать и сбрасывать бит TXCIE. Он совершенно не мешает и лишний раз не вызовет обработчик, поэтому смело устанавливайте этот бит при инициализации USART и больше его не трогайте.

Итак, жду код целиком. И еще - Вы в отладчике (в симуляторе) пробовали свою программу "покрутить"?
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 386
Зарегистрирован: Чт окт 31, 2013 10:54:32

Сообщение alex38779 »

После осмысления ночью дошел до истины...

Отправка 1 байта, работает :) 8) :
Спойлер

Код: Выделить всё

#define F_CPU 8000000UL
#define BAUD 19200L

#include <avr/io.h>
#include "stdbool.h"
#include <avr/eeprom.h>
#include <avr/interrupt.h>

unsigned char sendBuffer[5] = 
{
	0x30,0x00,0x01,0x02,0xaa,
};

char indexByteSend = 0;

/*Прерывание*/
ISR(USART_TXC_vect)
{	
	;	
}

int main(void)
{	
	cli();
	/*Настройка скорости передачи*/
	uint32_t ubrrlValue = (F_CPU/(BAUD*16))-1;
	UBRRH = ubrrlValue >> 8;
	UBRRL = ubrrlValue;	
	UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );
	/*Разрешаем передачу*/
	UCSRB |= ( 1 << TXEN );	
	/*Инициализируем передачу*/
	UDR = sendBuffer[0];	
	/*Разрешаем прерывание*/
	UCSRB |=  1 << TXCIE;		
	sei();	
    while(1)
    {
		;
    }
}
Передача пакета байт, работает 8) :
Спойлер

Код: Выделить всё

#define F_CPU 8000000UL
#define BAUD 19200L

#include <avr/io.h>
#include "stdbool.h"
#include <avr/eeprom.h>
#include <avr/interrupt.h>

unsigned char sendBuffer[5] = 
{
	0x30,0x00,0x01,0x02,0xaa,
};

char indexByteSend = 0;

/*Прерывание*/
ISR(USART_TXC_vect)
{	
	indexByteSend++;
	if (indexByteSend == 5)
	{
		/*ставим например флаг окончания передачи пакета*/
	}
	else
	{
		UDR = sendBuffer[indexByteSend];
	}				
}

int main(void)
{	
	cli();
	/*Настройка скорости передачи*/
	uint32_t ubrrlValue = (F_CPU/(BAUD*16))-1;
	UBRRH = ubrrlValue >> 8;
	UBRRL = ubrrlValue;	
	UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );
	/*Разрешаем передачу*/
	UCSRB |= ( 1 << TXEN );	
	/*Инициализируем передачу*/
	UDR = sendBuffer[0];	
	/*Разрешаем прерывание*/
	UCSRB |=  1 << TXCIE;		
	sei();	
    while(1)
    {
		;
    }
}
Изображение
Урра!!!

Alkul, Z_h_e, Jack_A, Большое кошачье спасибо за помощь!!!! :) :tea: :beer:

Jack_A, я делал передачу по UART без прерывания, там в цикле проверяется регистр например как в даташите !(UCSRA&(1<<UDRE)). С этим работало, но хотелось сделать в прерывании. Что бы программа сама автоматически все делала и проверяла.

Пойду кодить дальше)
Контактная информация:
Держит паяльник хвостом
Сообщения: 933
Зарегистрирован: Ср апр 13, 2011 11:09:20
Откуда: Екатеринбург

Сообщение Alkul »

alex38779 писал(а): Передача пакета байт, работает 8)
Поздравляю! :)
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 386
Зарегистрирован: Чт окт 31, 2013 10:54:32

Сообщение alex38779 »

Мяу коты!

Хочу посчитать время в 1 с на attiny13, это мне нужно для получения задержек 1...255с.

Частота 9.6 МГц. Предделитель 256, с другими предделителями как то большие числа выходят.
Получаем:

256/9,6 = 2,66мс

1с = 1000мс
TCNT(100мс) = 100/2,66 = 37,59 = ~38.

Теперь, завожу таймер на переполнение, в нем каждые 100 мс инкрементирую переменную, например х, и проверяю условие если х = 10, то увеличиваю секунды.
Правильно мыслю? :) :))


Не правильноо... нашел ошибку в расчетах, слишком быстро затикало..

Вот правильный расчет
Спойлерпредделитель 1024
частота 9,6МГц
1024/9,6 = 0,106мс
1с = 1000с, 1с = 10с*100
TCNT0(для 10с) = 256 - (10/0,106) = 256 - 94 = 162
Контактная информация:
akl
Друг Кота
Сообщения: 4450
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

СпойлерГрибы умножаем на корзинки, делим на возраст бабушки, получаем число внучек :)
Лихо! У меня получается 366 с гаком переполнений Т0 для 10 секунд. 1'000'000/9'600'000=0,1041мкс*1024*256=27289,1904 период переполнения таймера. 10'000'000/27289,1904=366,4 переполнения.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 386
Зарегистрирован: Чт окт 31, 2013 10:54:32

Сообщение alex38779 »

akl писал(а):Грибы умножаем на корзинки, делим на возраст бабушки, получаем число внучек :)
Ну почему же) Пересчитал по другой частоте и пришел к оптимальному варианту...

Частота мк 9,6МГц, внутренний источник attiny13
Ставим фуз деления частоты на 8, получаем 9,6/8 =1,2МГц

Далее в прерывании по переполнению считается время в секундах и обрабатываются кнопки..

1 тик таймера при предделителе 1024 и частот 1,2МГц будет равен 1024 / 1200000 = 8,533e-4 или 0,0008533с = 0.853мс

Для подсчета времени заведем переменную, которая будет считать переполнения таймера. Таймер настраиваем на 200мс. 5 переполнений таймера = 1 с.
Это 200мс / 0,853мс = ~234
Счетный регистр TCNT0 = 256 -234 = 22.

В проетусе работает, кнопки обрабатываются, время считается.
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Лично я не сторонник понижения тактовой частоты. Зачем себя ужимать без надобности.

Если:
тактовая частота 8МГц
предделитель 1024
режим таймера 0 - СТС
регистр сравнения OCR =194

То прерывание совпадения таймера с регистром будет возникать с периодом 0,04992, ну или 50мс. Так наверное будет проще считать секундные интервалы?

Расчет проверяйте, не верьте мне :)

-------------------
Вот только зачем я для 8МГц посчитал? :facepalm:
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Держит паяльник хвостом
Сообщения: 933
Зарегистрирован: Ср апр 13, 2011 11:09:20
Откуда: Екатеринбург

Сообщение Alkul »

Для расчета констант таймеров и не только удобно пользоваться вот такой, к при меру, программой AVRCalc.
И подобных ей много.
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Изучаю документацию на некий УВП-280. У него максимальаня длина по RS-485 1500 м.
Просто вспомнил, что в этой теме обсуждали максимально возможную длину RS-485. Решил поделится только что прочитанным.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 386
Зарегистрирован: Чт окт 31, 2013 10:54:32

Сообщение alex38779 »

Еще нубский вопрос...


Есть 3 датчика, датчик шлет свой номер по RS-485, когда сработает. Срабатывание очень редкое.
Приемник просто принимает данные и вкл соотв. реле.

И еще сделать вот так, приемник раз допустим в 1 минуту шлет сигнал, со стороны датчика вкл светодиод.

Можно ли соединить их звездой например? Что бы не тянуть провода последовательно к ним, как выше показывали картинку.

P.S.Додумал, использовать мк с 3 юартами, и сделать 3 канала сети. Как вариант.


И еще вопрос, есть adm485. Хочу согласовать ее с 3.3 В логикой мк.
Нашел такую схему через оптопару. Мне развязывать гальванически не нужно.
Изображение

Могу я взять бомжовский pc817 и поставить его? Или он слишком медленный?
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

alex38779 писал(а):Есть 3 датчика, датчик шлет свой номер по RS-485, когда сработает. Срабатывание очень редкое.
А если вместе сработают?
Если Ваш датчик всего-лишь шлет факт сработки, может поставить концевик или его электронный аналог?
alex38779 писал(а):Можно ли соединить их звездой например?
Не очень идея то, но если правильно организовать протокол, то реализовать думаю можно.
alex38779 писал(а):P.S.Додумал, использовать мк с 3 юартами, и сделать 3 канала сети. Как вариант.
Вам должно быть виднее.
alex38779 писал(а):Мне развязывать гальванически не нужно.
Зачем тогда оптопары?
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1368
Зарегистрирован: Вс мар 28, 2010 12:52:22
Откуда: Беларусь

Сообщение dr.doc »

но хотелось сделать в прерывании. Что бы программа сама автоматически все делала и проверяла.
Заполняем буфер N элементами. Отправляем N-ный элемент в регистр UDR, разрешаем прерывания по опустошению буфера, в прерывании проверяем номер передаваемого байта массива. Если не нулевой, то передаем след. байт. Если нулевой - запрещаем прерывания по опустошению буфера и выставляем служебный ( свой программный ) флаг готовности буфера к новым заполнениям.
PS Я запускал по приему таймер с длительностью переполнения в 1,5 длительности передачи байта на принятой скорости. При приеме любого байта сбрасываю счетчик таймера. По прерыванию этого таймера считаю принятое сообщение оконченным. После этого начинаю подсчет CRC суммы. Эти меры отрабатывают протокол Modbus. Делал тестовое ус-во для опробования Scada Trace Mode.
«Еще я хотел бы, чтобы наши ученые изобрели какой-то новый источник энергии, чтобы мы на коленях не ползали даже перед нашими братьями, умоляя их и выпрашивая тонну нефти или кубометр газа», — рассказал белорусский президент.
Встал на лапы
Аватара пользователя
Сообщения: 93
Зарегистрирован: Чт апр 26, 2012 14:30:40
Откуда: под Москвой

Сообщение _dark »

Буду краток :))

девайс на ATmega, 99% ничего не делает, поэтому буфер просто копируется для разбора
- прерывание только на прием, передача в цикле (желающие могут переделать)...
- в RunAnsver есть теоретическая опасность никогда не завершить цикл, но на практике не сильно критично...

Код: Выделить всё

int main(void)
{
....
	while(1){
 		_WDR();
		if(fCmdComplite){  		
		for(i=0;i<CntRxByte;i++){
    		UInBuf[i] = RxBuf[i];			
	    	} 	
	  	if(CntRxByte>2){
			RunCmd();
			}	
	  	fCmdComplite = 0;
	  	CntRxByte = 0;	 
                ......
	  	}
}

Ниже образец modbus-slave
Спойлер#include <avr/io.h>
#include <avr/interrupt.h>
#include "Main.h"
#include "Variable.h"
#include "MODBUS_Tags.h"
#include "CRC16.h"


void RunCmd(void); // выполнение команды
void RunAnsver(unsigned char count); // передача подготовленного буфера в канал
void UART0_RX_int(void);

//---------------------------------------------------
ISR(USART0_RX_vect)
{
// modbus
_CLI();
ERROR = 0;
if(CntRxByte < MaxLenBuf){
if(UCSR0A & (1<<FE0)) ERROR = ERRA3; // ошибка фрейма
if(UCSR0A & (1<<DOR0)) ERROR = ERRA4; // ошибка переполнения
if(UCSR0A & (1<<UPE0)) ERROR = ERRA5; // ошибка паритета
while (UCSR0A & (1<<RXC0)){
RxBuf[CntRxByte++] = UDR0;
// RxErrBuf[CntRxByte-1] = ERROR; // test
}
}
else{
CntRxByte = 0;
fCmdComplite = 0;
ERROR = ERRA6; //ошибка переполнения приемного буфера
}
TC1_Start1A();
_SEI();
}

//---------------------------------------------------
ISR(TIMER1_COMPA_vect)
{
_CLI();
TC1_Stop1A();
fCmdComplite = 1;
_SEI();
}


//---------------------------------------------------
// декодирование команды
void RunCmd(void)
{
uint8_t i, Fun,
uint8_t channel, outLen, inLen;
uint16_t addr_used;
uint16_t calc_crc16;

_BLIK_TR; //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

stCRC16.Byte[0] = UInBuf[CntRxByte-2];
stCRC16.Byte[1] = UInBuf[CntRxByte-1];
calc_crc16 = CRC16((uint8_t*)&UInBuf[0],CntRxByte-2);
stCRC.Word = calc_crc16;

if(stCRC16.Word == calc_crc16){
if(UInBuf[0]==gADDR_DEVICE){
Fun = UInBuf[1];
TxBuf[0] = gADDR_DEVICE;
TxBuf[1] = Fun;
stAddrByte.Byte[0] = UInBuf[3];
stAddrByte.Byte[1] = UInBuf[2];
switch (Fun){
case _cmd_ReadReg: //
switch (stAddrByte.Word){
case _tag_10bRevASCII: //0xAA
TxBuf[2] = 10;
for(i=0;i<10;i++)
TxBuf[3+i] = RevASCII;
stCRC16.Word = CRC16((uint8_t*)&TxBuf[0],13);
TxBuf[13] = stCRC16.Byte[0];
TxBuf[14] = stCRC16.Byte[1];
RunAnsver(15);
break;
default:
// ...
}
break;
//+++++++++++++++++++ Запись +++++++++++++++++++++++
/*/ case Cmd_WriteBit:
// break;
case _cmd_WriteReg:
switch (stAddrByte.Word){
case _tag_4bConfigInput:
break;
default: ERROR = ERR_DATA_ADDR; // ILLEGAL DATA ADDRESS
break;
}
break; //*/
case _cmd_WriteRegs:
switch (stAddrByte.Word){
case _tag_4bConfigDevice: //0x04
DevCfg = UInBuf[8] & DevCfg_mask;
DevOut = UInBuf[10];
DDRC = DevOut;
if(DevCfg & 0x10){
DevCfg &= 0x3C;
PORTC = 0x00;
}
else{ PORTC = 0xFF; }
DevIn = UInBuf[9];
DevIn = ((DevIn & DevOut) ^ DevIn);
if(__eePUT(_ofs_DevCfg+1,DevCfg)) ERROR = ERR_WR_EEP;
if(__eePUT(_ofs_DevIn,DevIn)) ERROR = ERR_WR_EEP;
if(__eePUT(_ofs_DevOut,DevOut)) ERROR = ERR_WR_EEP;
break;
default:
//.....
}
if(!ERROR){
TxBuf[2] = UInBuf[2];
TxBuf[3] = UInBuf[3];
TxBuf[4] = UInBuf[4];
TxBuf[5] = UInBuf[5];
stCRC16.Word = CRC16((unsigned char*)&TxBuf[0],6);
TxBuf[6] = stCRC16.Byte[0];
TxBuf[7] = stCRC16.Byte[1];
RunAnsver(8);
}
break;
}
}
}
else{
ERROR = ERR_CRC_MB;
TxBuf[1] |= 0x80;
TxBuf[2] = ERROR;
stCRC16.Word = CRC16((unsigned char*)&TxBuf[0],3);
TxBuf[3] = stCRC16.Byte[0];
TxBuf[4] = stCRC16.Byte[1];
RunAnsver(5);
ERROR = 0;
}
_BLIK_TR; //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

//---------------------------------------------------
// передача ответа
void RunAnsver(unsigned char count)
{
char i;
int j;
_RS485_OUT;
for(i = 0; i < count; i++){
while((UCSR0A & (1<<UDRE0))!=0x20){_NOP();} //////
UDR0 = TxBuf;
}
for(j = 0; j < Tout2ms; j++){_NOP();}
_RS485_IN;
}


зы
сорри, не знаю, почему схлопываются табы в спойлере, в редакторе тут вижу все нормально

Добавлено after 26 minutes 44 seconds:
И еще вопрос, есть adm485. Хочу согласовать ее с 3.3 В логикой мк.
Нашел такую схему через оптопару. Мне развязывать гальванически не нужно.


выбросьте ее :))
лучше взять драйвер с питанием 3.3v (или проиграите в лишнем стабилизаторе),
ну или согласуйте резистивными делителями, раз гальваноразвязка не нужна,
Ответить

Вернуться в «AVR»