AVR. Учебный курс. Передача данных через UART

Почти каждый микроконтроллер имеет на борту универсальный последовательный интерфейс — UART. AVR тут не исключение и поддерживает этот протокол в полном обьеме полностью аппаратно. По структуре это обычный асинхронный последовательный протокол, то есть передающая сторона по очереди выдает в линию 0 и 1, а принимающая отслеживает их и запоминает. Синхронизация идет по времени — приемник и передатчик заранее договариваются о том на какой частоте будет идти обмен. Это очень важный момент! Если скорость передатчика и приемника не будут совпадать, то передачи может не быть вообще, либо будут считаны не те данные.
 

Протокол
Вначале передатчик бросает линию в низкий уровень — это старт бит. Почуяв что линия просела, приемник выжидает интервал Т1 и считывает первый бит, потом через интервалы Т2 выковыриваются остальные биты. Последний бит это стоп бит. Говорящий о том, что передача этого байта завершена. Это в самом простом случае.
 

В конце байта, перед стоп битом, может быть и бит четности. Который получается если поксорить между собой все биты, для контроля качества передачи. Также может быть два стопа, опять же для надежности. Битов может быть не 8, а 9. О всех этих параметрах договариваются на берегу, до начала передачи. Самым же популярным является 8 бит, один старт один стоп, без четности.
 

Причем с самим протоколом можно не заморачиваться — все реализовано аппаратно. Разве что захочется завести второй UART, тогда придется делать его программно.
 

По такому же протоколу работает COM порт компьютера, разница лишь в разнице напряжений, поэтому именно этот протокол я буду использовать для связи микроконтроллера с компом. Для преобразования напряжений можно использовать RS232-TTL конвертер. Мы же применим встроенный в Pinboard мост USB-UART который образовывает в системе виртуальный COM PORT

Аппаратная часть
Ну тут все просто, соединяем крест-накрест приемник с передатчиком и готово.
 


 

Внутри же вначале находится накопительный сдвиговый регистр, в котором происходит сборка байта из битов и регистры данных UDR, куда этот бит передается. Такая структура исключает возможность считать не до конца полученный байт.
Буфер приема состоит из двух байт, что позволяет ему держать два в уме и один еще засасывать в сдвиговый регистр.
 

Использование UART’a

У AVR есть регистр UDR это UART Data Register (в некоторых контроллерах он может зваться UDR0 или еще как нибудь похоже). На самом деле это два разных регистра, но имеют один адрес. Просто на запись попадает в один (регистр передатчика), а на чтение берет из другого (регистр приемника). Таким образом достаточно просто пихать данные в этот регистр и они улетят приемнику, и, наоборот, считывать их оттуда по приходу.
 

О том, что байт пришел в регистр UDR нам скажет прерывание по завершению приема, которое вызывается сразу же, как приемник засосет в себя все биты (обычно 8, но может быть и 9, в зависимости от настройки).
 

Поскольку передача идет довольно медленно, то бездумно пихать данные в регистр UDR нельзя — нужно дождаться окончания передачи предыдущего байта. О том, что UDR пуст и готов к приему нового байта сигнализирует бит UDRE, он же вызовет аппаратное прерывание по опустошению буфера.
 

Так что постоянно следить за UART вручную не нужно, все обслуживание можно повесить на прерывания и он сам будет все делать. Можно в памяти организовать буфер и туда тупо пихать данные, а на прерывании по опустошению UDR следить за тем есть ли что в буфере и если есть — отправлять.
 

А по приему, не тупить, а также, по прерыванию, пихать данные в ОЗУ, но уже в буфер приема, откуда их считает уже программа.
 

Настройка UART
Все настройки приемопередатчика хранятся в регистра конфигурации. Это UCSRA, UCSRB и UCSRС. А скорость задается в паре UBBRH:UBBRL.
 

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

Регистр UCSRA

Тут нам интересны только биты RXC и TXC это флаги завершения приема и передачи, соответственно. RXC встанет когда непрочитанный байт вылезет в регистр UDR, а TXC встает когда последний стоп-бит прошел, а новое значение в UDR не поступило. Т.е. после прохода всех байтов.
 

Также одновременно с этими флагами вызывается прерывание (если оно было разрешено). Сбрасываются они аппаратно — принимающий после чтения из регистра UDR, передающий при переходе на прерывание, либо программно (чтобы сбросить флаг программно в него надо записать 1)
 

Биты UDRE сигнализирует о том, что регистр UDR приемника пуст и в него можно пихать новый байт. Сбрасывается он аппаратно после засылки в UDR какого либо байта. Также генерируется прерывание «Регистр пуст»
 

Бит U2X — это бит удвоения скорости при работе в ассинхронном режиме. Его надо учитывать при расчете значения в UBBRH:UBBRL
 

Регистр UCSRB
Тут в первую очередь это биты RXEN и TXEN — разрешение приема и передачи. Стоит их сбросить как выводы UART тут же становятся обычными ножками I/O.
 

Биты RXCIE, TXCIE, UDRIE разрешают прерывания по завершению приема, передачи и опустошении буфера передачи UDR.
 

Регистр UCSRC
Самый прикол тут — это бит селектора URSEL дело в том, что по неизвестной причине создаетели решили сэкономить байт адреса и разместили регистры UCSRC и UBRRH в одной адресном пространстве. А как же определять куда записать? А по старшему биту! Поясню на примере. Если мы записываем число у которого седьмой бит равен 1, то оно попадет в UCSRC, а если 0 то UBRRH. Причем этот бит есть не во всех AVR в новых моделях его нет, а регистры имеют разные адреса. Так что надо смотреть в даташите этот момент — есть там бит URSEL или нет.
 

Остальные биты задают число стопов, наличие и тип контроля четности. Если оставить все по дефолту то будет стандартный режим. Надо только выставить формат посылки. Делается это битами UCSZ0, UCSZ1 и UCSZ2 (этот бит тусуется в регистре UCSRB). Для стандартной 8ми битной посылки туда надо записать две единички.
 

Скорость обмена.
Тут все зависит от пары UBBRx
 

Вычисляется требуемое значение по формуле:

UBBR=XTAL/(16*baudrate)-1 для U2X=0
UBBR=XTAL/(8*baudrate)-1 для U2X=1

 

Где:
XTAL — рабочая тактовая частота контроллера.
baudrate — требуемая скорость (я люблю 9600 :) — чем медленней тем надежней. 9600 в большинстве случаев хватает)
 

Ошибки передачи
К сожалению мир наш не идеален, поэтому возможны ошибки при приеме. За них отвечают флаги в регистре UCSRA
 
FE — ошибка кадрирования. Т.е. мы ждали стоп бит, а пришел 0.
OR — переполнение буфера. То есть данные лезут и лезут, а из UDR мы их забирать не успеваем.
PE — не совпал контроль четности.
 

Примеры кода
 

Простая приемка и отправка байт. Без использования прерываний.
Для начала инициализация UART в ATMega16 (А таже в Mega8535/8/32 и многих других).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
; Internal Hardware Init  ======================================
		.equ 	XTAL = 8000000 	
		.equ 	baudrate = 9600  
		.equ 	bauddivider = XTAL/(16*baudrate)-1
 
 
uart_init:	LDI 	R16, low(bauddivider)
		OUT 	UBRRL,R16
		LDI 	R16, high(bauddivider)
		OUT 	UBRRH,R16
 
		LDI 	R16,0
		OUT 	UCSRA, R16
 
; Прерывания запрещены, прием-передача разрешен.
		LDI 	R16, (1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<TXCIE)|(0<<UDRIE)
		OUT 	UCSRB, R16	
 
; Формат кадра - 8 бит, пишем в регистр UCSRC, за это отвечает бит селектор
		LDI 	R16, (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
		OUT 	UCSRC, R16
1
2
3
4
5
6
; Процедура отправки байта
uart_snt:	SBIS 	UCSRA,UDRE	; Пропуск если нет флага готовности
		RJMP	uart_snt 	; ждем готовности - флага UDRE
 
		OUT	UDR, R16	; шлем байт
		RET			; Возврат
1
2
3
4
5
6
7
8
9
;Посылка байта:
		RCALL 	uart_init 	; вызываем нашу процедуру инициализации.
 
Main:		LDI 	R16,'E'		; загоняем в регистр код буквы «E»
		RCALL	uart_snt	; Вызываем процедуру отправки байта. 
 
		NOP			; Любой дальнейший код
		NOP
		NOP
1
2
3
4
5
6
;Ожидание байта
uart_rcv:	SBIS	UCSRA,RXC	; Ждем флага прихода байта
		RJMP	uart_rcv	; вращаясь в цикле
 
		IN	R16,UDR		; байт пришел - забираем.
		RET			; Выходим. Результат в R16

 

В главном цикле это выглядит так (я выбросил из главного цикла все что там было для краткости, чтобы в глазах не рябило. Оставил только работу с USART):
 

1
2
3
4
5
6
7
8
; Main =========================================================
Main:		RCALL	uart_rcv	; Ждем байта
 
		INC	R16		; Делаем с ним что-то
 
		RCALL	uart_snt	; Отправляем обратно.
 
		JMP	Main

Если зальешь эту программку в Pinboard и подключишься терминалкой, то на каждый байт контроллер тебе вернет байт следующий по величине. Например на 1 (код 0x31) возвращается 2 (код 0x32). И так далее.
 

Данный метод прост и работает в лоб. Но имеет одну большую проблему — цикл ожидания прихода байта. В этот момент ничего работать не может. Конечно, можно не зацикливаться наглухо, а просто проверять флаг RxC и крутиться в основном цикле. Но тогда, если передача идет на большой скорости, можно запросто прозевать байт.
 

Работа на прерываниях
Решение тут одно — использование прерываний хотя бы на прием. А в идеале и на передачу тоже. Сейчас покажу тебе пример буферизированной работы с приемопередатчиком на прерываниях.
 

Во первых инициализация, она теперь другая — мы разрешаем прерывания:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
; Internal Hardware Init  ======================================
		.equ 	XTAL = 8000000 	
		.equ 	baudrate = 9600  
		.equ 	bauddivider = XTAL/(16*baudrate)-1
 
 
uart_init:	LDI 	R16, low(bauddivider)
		OUT 	UBRRL,R16
		LDI 	R16, high(bauddivider)
		OUT 	UBRRH,R16
 
		LDI 	R16,0
		OUT 	UCSRA, R16
 
; Прерывания разрешены, прием-передача разрешен.
		LDI 	R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(0<<UDRIE)
		OUT 	UCSRB, R16	
 
; Формат кадра - 8 бит, пишем в регистр UCSRC, за это отвечает бит селектор
		LDI 	R16, (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
		OUT 	UCSRC, R16
		RET

 

Используем все три прерывания. По приему, по опустошению буфера и по окончании передачи. Но пока разрешаем только два — по приему и по передаче. Иначе при старте мы сразу же ускачем на обработчик UDRIE — буфер то пуст!
 

В таблицу векторов впишем наши переходы:

1
2
3
4
5
6
        .ORG $016
        RJMP	RX_OK	  	; (USART,RXC) USART, Rx Complete
        .ORG $018
        RJMP	UD_OK       	; (USART,UDRE) USART Data Register Empty
        .ORG $01A
        RJMP	TX_OK      	; (USART,TXC) USART, Tx Complete

А в секцию обработчиков прерываний добавим нашу процедуру приема:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
RX_OK:		PUSHF			; Макрос, пихающий в стек SREG и R16
 
		IN	R16,UDR		; Тут главное забрать байт из UDR иначе 
					; флаг прерывания не снимется
					; Дальше, если время позволяет, можно и обработать
 
		CPI	R16,Value	; Например, разобрать по байтам и выполнить действие
		BRNE	NXT		; Обычным CASE оператором.
		Action1			; Делаем что нибудь ценное. Главное не забыть в стеке
					; Регистры попрятать. А то будет тебе упячка. 
NXT:		CPI	R16,Value2
		BRNE	RX_Exit
		Action2			; Делаем что нибудь ценное2
 
Rx_Exit:		POPF			; Достаем SREG и R16
		RETI

 

Передача делается побайтно, на вызовах прерываний. Например, надо нам отрыгнуть текстовую строку из флеша:

1
String:		.db	"Hello Interrupt Request",0

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

1
2
		.DSEG
StrPtr:		.byte	2

Далее, в главной программе, в любом нужном нам месте, загружаем адрес текстовой строки в этот указатель, не забыв умножить его на два. Т.к. компилятор флеш адресует в словах, а контроллер оперирует только байтами:
 

1
2
3
4
5
6
7
8
9
Main:		NOP				; Любой произвольный код главной программы
		NOP
		NOP
 
		LDI	R17,low(2*String)	; Берем младший байт
		LDI	R18,High(2*String)	; Берем старший байт
 
		STS	StrPtr,R17		; Сохраняем Младший байт
		STS	StrPtr+1,R18		; Сохраняем Сташрий байт

И сразу же запускаем передачу, путем разрешения прерываний по UDRE. Так как UDR у нас пуст, то прерывание стартует мгновенно.
 

1
2
3
4
5
6
7
		LDI 	R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(1<<UDRIE)
		OUT 	UCSRB, R16
 
;После чего спокойно выполняем любой другой код. 
		NOP
		NOP
		NOP

А дальше все сделает наш обработчик прерывания события UDR Empty. На который ссылается вектор прерывания по опустошению регистра UDR:
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
UD_OK:		PUSHF			; Макрос, сохраняющий SREG и R16
		PUSH	ZL		; Сохраняем в стеке Z
		PUSH	ZH
 
		LDS	ZL,StrPtr	; Грузим указатели в индексные регистры
		LDS	ZH,StrPtr+1
 
		LPM	R16,Z+		; Хватаем байт из флеша. Из нашей строки
 
		CPI	R16,0		; Если он не ноль, значит читаем дальше
		BREQ	STOP_RX		; Иначе останавливаем передачу
 
		OUT	UDR,R16		; Выдача данных в усарт.
 
		STS	StrPtr,ZL	; Сохраняем указатель 
		STS	StrPtr+1,ZH	; обратно, в память
 
Exit_RX:	POP	ZH		; Все достаем из стека, выходим.
		POP	ZL
		POPF
		RETI
 
; глушим прерывание по опустошению, выходим из обработчика
STOP_RX:	LDI 	R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(0<<UDRIE)
		OUT 	UCSRB, R16
		RJMP	Exit_RX

Все, по прерыванию обработчик сам выгребет данные из флеша и сам автоматом себя забанит когда дойдет до нуля в строке.
 

А зачем нужно прерывание TX_OK? Ну мало ли зачем. Еще какое-нибудь событие повесить. Оно сработает когда произойдет две вещи — UDR кончится и в сдвиговом регистре USART все биты улетят в провод. В принципе, многие юзают его вместо UDRE, но тогда между двумя уходящими байтами будет промежуток периодом в 1 байт. Что не очень кошерно.
 

Буферизация
Но далеко не всегда можно успеть обработать данные с большой скоростью. В этом случае их приходится куда-то складывать. А передавать/обрабатывать попозже. Особенно это касается отправки. Отправка медленная, а нам бы все сразу в буфер свалить и уйти по своим делам. А USART пускай там постепенно со всем этим разбирается.
 

Поэтому мы заведем два кольцевых буфера. На прием и на передачу (можно и только на передачу, это чаще требуется). А также ряд служебных переменных:
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
; RAM ========================================================
		.DSEG
 
		.equ MAXBUFF_IN 	=	10	; Размер в байтах
		.equ MAXBUFF_OUT 	= 	10
 
IN_buff:	.byte	MAXBUFF_IN	; Буфер приема
IN_PTR_S:	.byte	1		; Указатель начала
IN_PTR_E:	.byte	1		; Указатель конца
IN_FULL:	.byte	1		; Флаг переполнения
 
OUT_buff:	.byte	MAXBUFF_OUT	; Буфер передачи
OUT_PTR_S:	.byte	1		; Указатель начала
OUT_PTR_E:	.byte	1		; Указатель конца
OUT_FULL:	.byte	1		; Флаг переполнения.

Указатели начала показывают откуда мы будем буфер читать, указатель конца нацелен на точку записи. В результате конец убегает, а начало его догоняет. Когда они сравняются — буфер пуст. При достижении максимальных границ буфера (10 байт) указатель переносится в начало, т.к. у нас буфер закольцован.
 

При этом может быть перехлест указателей между собой. Это даст ошибку переполнения и некорректную работу. Поэтому введены флаги переполнения, возникающие если конец пошел по второму кругу и догнал начало.
 

Сами указатели я сделал не классическими адресами, а смещениями относительно начала каждого буфера, Так получается оптимальней, не приходится сравнивать двубайтные числа. Но при этом длина буфера ограничена 255 байтами.
 

Прерывание по приему данных пишет в буфер IN_buff

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
RX_OK:		PUSHF				; Макрос, пихающий в стек SREG и R16
		PUSH	R17
		PUSH	R18
		PUSH	XL
		PUSH	XH
 
		LDI	XL,low(IN_buff)		; Берем адрес начала буффера
		LDI	XH,high(IN_buff)
		LDS	R16,IN_PTR_E		; Берем смещение точки записи
		LDS	R18,IN_PTR_S		; Берем смещение точки чтения
 
		ADD	XL,R16			; Сложением адреса со смещением
		CLR	R17			; получаем адрес точки записи
		ADC	XH,R17
 
		IN	R17,UDR			; Забираем данные
		ST	X,R17			; сохраняем их в кольцо
 
		INC	R16			; Увеличиваем смещение
 
		CPI	R16,MAXBUFF_IN		; Если достигли конца 
		BRNE	NoEnd
		CLR	R16			; переставляем на начало
 
NoEnd:		CP	R16,R18			; Дошли до непрочитанных данных?
		BRNE	RX_OUT			; Если нет, то просто выходим
 
 
RX_FULL:	LDI	R18,1			; Если да, то буффер переполнен.
		STS	IN_FULL,R18		; Записываем флаг наполненности
 
RX_OUT:		STS	IN_PTR_E,R16		; Сохраняем смещение. Выходим
 
		POP	XH
		POP	XL
		POP	R18
		POP	R17
		POPF				; Достаем SREG и R16
		RETI

Если возникнет переполнение, то данные начнут затирать предыдущие, произойдет перехлест указателей, но перед этим поднимется флаг IN_FULL и диспетчер, или кто у нас там будет следить за всякими исключениями, может спешно среагировать и выгрузить буфер.
 

IN_FULL занимает целый байт, но можно было бы в один бит сделать. Я просто не стал усложнять. При чтении и записи в буфер в фоновой программе надо соблюдать атомарность, т.е. запрещать возможность записи в буфер из прерывания. А то будет трудно уловимый косяк.
 

Чтение из буфера приема делается тоже просто:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
; Read from loop Buffer
; IN: NONE
; OUT: 	R17 - Data,
;		R19 - ERROR CODE
 
Buff_Pop: 	CLI 				; Запрещаем прерыания. 
						; Но лучше запретить прерывания конкретно  от 
						; UART, чем запрещать вообще все.
		LDI	XL,low(IN_buff)		; Берем адрес начала буффера
		LDI	XH,high(IN_buff)
		LDS	R16,IN_PTR_E		; Берем смещение точки записи
		LDS	R18,IN_PTR_S		; Берем смещение точки чтения			
		LDS	R19,IN_FULL		; Берм флаг переполнения
 
		CPI	R19,1			; Если буффер переполнен, то указатель начала
		BREQ	NeedPop			; Равен указателю конца. Это надо учесть.
 
		CP	R18,R16			; Указатель чтения достиг указателя записи?
		BRNE	NeedPop			; Нет! Буффер не пуст. Работаем дальше
 
		LDI	R19,1			; Код ошибки - пустой буффер!
 
		RJMP	_TX_OUT			; Выходим
 
NeedPop:	CLR	R17			; Получаем ноль
		STS	IN_FULL,R17		; Сбрасываем флаг переполнения
 
		ADD	XL,R18			; Сложением адреса со смещением
		ADC	XH,R17			; получаем адрес точки чтения
 
		LD	R17,X			; Берем байт из буффера
		CLR	R19			; Сброс кода ошибки
 
		INC	R18			; Увеличиваем смещение указателя чтения
 
		CPI	R18,MAXBUFF_OUT		; Достигли конца кольца?
		BRNE	_TX_OUT			; Нет? 
 
		CLR	R18			; Да? Сбрасываем, переставляя на 0
 
_TX_OUT:	STS	IN_PTR_S,R18		; Сохраняем указатель
		SEI				; Разрешаем прерывания
		RET

Тут только один момент хитрый. Если у нас буфер переполнился, но не перехлестнулся, то его начало и конец совпадают, что как бы сигнализирует о том, что буфер пуст. Но на самом деле он полон непрочитанных данных и об этом сигнализирует флаг переполнения. Поэтому надо вначале проверять его, чтобы при не обходимости игнорировать признак равенства указателей.
 

На выходе функции Buff_Pop у нас в регистрах идут данные (R17) и код ошибки (R19). Если функция вернула 1, значит буфер пуст и регистр с данными можно игнорировать.
 

Запись в буфер задача более востребованная и чаще встречающаяся. И делается по аналогии с прерыванием RX
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
; Load Loop Buffer 
; IN R19 	- DATA
; OUT R19 	- ERROR CODE 
Buff_Push:	CLI 				; Запрет прерываний. 
		LDI	XL,low(OUT_buff)	; Берем адрес начала буффера
		LDI	XH,high(OUT_buff)
		LDS	R16,OUT_PTR_E		; Берем смещение точки записи
		LDS	R18,OUT_PTR_S		; Берем смещение точки чтения
 
		ADD	XL,R16			; Сложением адреса со смещением
		CLR	R17			; получаем адрес точки записи
		ADC	XH,R17
 
 
		ST	X,R19			; сохраняем их в кольцо
		CLR	R19			; Очищаем R19, теперь там код ошибки
						; Который вернет подпрограмма
 
		INC	R16			; Увеличиваем смещение
 
		CPI	R16,MAXBUFF_OUT		; Если достигли конца 
		BRNE	_NoEnd
		CLR	R16			; переставляем на начало
 
_NoEnd:		CP	R16,R18			; Дошли до непрочитанных данных?
		BRNE	_RX_OUT			; Если нет, то просто выходим
 
 
_RX_FULL:	LDI	R19,1			; Если да, то буффер переполнен.
		STS	OUT_FULL,R19		; Записываем флаг наполненности
						; В R19 остается 1 - код ошибки переполнения
 
_RX_OUT:	STS	OUT_PTR_E,R16		; Сохраняем смещение. Выходим
		SEI 				; Разрешение прерываний
		RET

Пользоваться просто:
в R19 пихаем данные, вызваем функцию. НА выходе, в регистре R19 у нас код ошибки. Если там 1, то следующий байт писать нельзя — будет переполнение и перехлест указателей.
 

После загона данных в буфер надо запустить передачу. Я под это дело написал макрос
 

1
2
3
4
	.MACRO	TX_RUN
		LDI 	R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(1<<UDRIE)
		OUT 	UCSRB, R16	
	.ENDM

Мы просто разрешаем прерывание UDRIE, а так как буфер пуст, то оно выполнится немедленно.
 

Из буфера данные забирает прерывание по опустошению UDR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
UD_OK:		PUSHF						
		PUSH	R17
		PUSH	R18
		PUSH	R19
		PUSH	XL
		PUSH	XH
 
 
		LDI	XL,low(OUT_buff)	; Берем адрес начала буффера
		LDI	XH,high(OUT_buff)
		LDS	R16,OUT_PTR_E		; Берем смещение точки записи
		LDS	R18,OUT_PTR_S		; Берем смещение точки чтения			
		LDS	R19,OUT_FULL		; Берм флаг переполнения
 
		CPI	R19,1			; Если буффер переполнен, то указатель начала
		BREQ	NeedSend		; Равер указателю конца. Это надо учесть.
 
		CP	R18,R16			; Указатель чтения достиг указателя записи?
		BRNE	NeedSend		; Нет! Буффер не пуст. Надо слать дальше
 
		LDI 	R16,1<<RXEN|1<<TXEN|1<<RXCIE|1<<TXCIE|0<<UDRIE	; Запрет прерывания
		OUT 	UCSRB, R16					; По пустому UDR
		RJMP	TX_OUT			; Выходим
 
NeedSend:	CLR	R17			; Получаем ноль
		STS	OUT_FULL,R17		; Сбрасываем флаг переполнения
 
		ADD	XL,R18			; Сложением адреса со смещением
		ADC	XH,R17			; получаем адрес точки чтения
 
		LD	R17,X			; Берем байт из буффера
		OUT	UDR,R17			; Отправляем его в USART
 
		INC	R18			; Увеличиваем смещение указателя чтения
 
		CPI	R18,MAXBUFF_OUT		; Достигли конца кольца?
		BRNE	TX_OUT			; Нет? 
 
		CLR	R18			; Да? Сбрасываем, переставляя на 0
 
TX_OUT:		STS	OUT_PTR_S,R18		; Сохраняем указатель
 
		POP	XH
		POP	XL
		POP	R19
		POP	R18
		POP	R17
		POPF				; Выходим, достав все из стека
		RETI

В Случае переполненного буфера мы сбрасываем флаг переполнения, а когда данные все выгребем и указатели вновь сравняются, то мы запрещаем прерывание по UDRE. Тем самым, остановив передачу.
 

И кратенький пример на работу с буфером. Ничего не показывает и не доказывает. Просто гоняет данные не напрямую, а через буфера:
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
		...
		RCALL	Buff_Pop	; Берем данные из буфера
		CPI	R19,1		; Они там есть?
		BREQ	LOOPS		; Нет? Ну еще раз
 
		INC	R17		; Данные увеличили на 1, просто так.
 
		MOV	R19,R17		; Переложили в R19
NewTry:		RCALL	Buff_Push	; Пихнули в буфер
 
		CPI	R19,1		; Буфер не переполнился? 
		BRNE	RUN	
 
		TX_RUN			; Ecли да, то запускаем передачу	
		RCALL	Delay		; Ждем или передаем управление другой задаче
		RJMP	NewTry		; А потом снова пробуем положить в буфер. 
 
RUN:		TX_RUN			; Если все ок, запускаем передачу
 
		...

Примерно так.
 

Исходник с примером на буфере
 

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

Пожалуй самый распространенный баг это циклическая инициализация. Т.е. когда инициализация USART засунута в главный цикл и каждую итерацию происходит его переинициализация. Разумеется он от такого затраха офигевает и отказывается работать. Поэтому сразу запомните раз и навсегда — все инициализации делаются только один раз.
Нет, разумеется можно потом что-нибудь подправить и заново переиницализировать, но не тогда когда идет передача. Сначала пусть устройство завершит свои дела, а потом уж его можно трогать за регистры.
 

Второй популярный косяк — аппаратные проблемы. Т.е. пыжишься ты с кодом, перебираешь биты настроек, а он не работает. И код уже проверен на десяток раз, а все никак. А проблема вполне может быть и с зависшим COM портом или FTDI. Либо подключил что-то неправильно. Самый простой способ проверить интерфейс это отключить от контроллера передающую линию (на Pinboard надо первести переключатель USART в положение OFF)
 

Замкнуть RX на TX на выходе FTDI (или MAX232). Подключиться терминальной программой, например Terminal v1.9b. И отправить в порт байт. Он должен вернуться обратно. Если возвращается что-то не то, значит проблема в интерфейсе.
 

Третьими граблями (а может даже и первыми) являются проблемы со скростями и тактовыми частотами. Ведь передача то асинхронная, а значит если скорость не та на которую мы запрограммированы, то коннекта у нас не выйдет. Самая засада в том, что скорость контроллера так сразу и не определишь. Чуть ошибся в фуз битах и запустил, например, контроллер не на 8мгц, а на 1мгц. Либо выставил тактовый генератор, да завелся он не на частоте кварца, а на какой нибудь из гармоник (редко, но бывает). А еще в некоторых контроллерах есть фуз бит CKDIV8 который делит тактовую частоту на 8. В общем, если UART не работает или работает с ошибками, то узнайте ТОЧНО на какой реально скорости у вас работает контроллер. Сделать это можно, например, с помощью таймера. Настроив его так, чтобы он мигал раз в секунду. Если мигает так как положено — значит частота верная. А если нет, то искать почему — фузы, биты делителя, кварцы и тды.
 

По этой же причине, кстати, если в готовом устройстве написано, что надо кварц на 12мгц, то ставить надо на 12мгц! Не на 10, не на 16 или 7.32, а именно на 12. Т.к. скорей всего от этого кварца зависят тайминги протоколов.
 

Четвертые грабли — бездумное копирование инициализации из всяких обучалок. Внимательно смотрите что у вас включено и на что настроено! Какие прерывания включены/выключены. Если прерывание есть, а обработчика нет, то вы получите неслабый глюк! Особенно это касается прерывания по RXC, которой пока из UDR не считаешь не успокоится.
 

Ну и в пятых — следите за совпадением скорости передатчика и приемника. Т.е. если передача идет на 9600, 1старт, один стоп, без четности. То и принимать надо ее на тех же параметрах. И никак иначе.
 

645 thoughts on “AVR. Учебный курс. Передача данных через UART”

  1. Добавлю, что бит UDRE после сброса МК находится в установленном состоянии — это логично, т. к. регистр данных UART в начале работы пуст. Вообще, логика установки UDRE и TXC принципиально отличается. Это важно помнить при реализации буферизации вывода: прерывание надо обрабатывать не по UDRE, а по TXC. Иначе, при отсутствии выходных данных процессор будет впустую делать на каждый такт прерывание.

    1. Не не не не :) По ТХС буфер нельзя пропихивать. Нам ведь его надо пропихивать фоном, по прерываниям, а не вручную. Байты уходят медленно, а ТХС генерится толкьо один раз — в конце посылки. Кто будет совать байты посылки из буфера в UDR? А нет таких героев.

      Поэтому делаем так:
      Вешаем прерывание по UDRЕ которое пропихивает все данные из буфера в UDR, а когда буфер опустошится само себя отключает.

      А когда мы вручную пихаем что либо в буфер, то мы это прерывание включаем.

      В результате полный автомат, отстутсвие тормозов главной программы из-за медленной передачи и никаких левых сработок. Ну бит сбросить/установить это можно в три команды сделать IN AND/OR OUT

      1. Фак мой мозг! :-) А я так всегда и пропихиваю! Обработчик всегда удаляет из очереди байт и, если очередь не пуста, пропихивает в UDR следующий. Если пуста — все автоматически останавливается (прерывание отключать не нужно). В процедуре отправки, если буфер пуст, то первый байт отправляется в UDR. Т. е., конфигурацию UART я вообще не трогаю после инициализации (не считая полудуплексного RS-485 — там бывает полезно отключать приемник во время передачи).

        В принципе, разницы особой нет — можно делать и так, и так :-)

        1. Поготь, а как так происходит то? Т.е. у тебя отправляется один байт, потом стоп. Потом тупняк на основании которого генерится TXC который пихает в UDR еще один байт и так далее? Так? Т.е. байты у тебя идут с разрывами.

  2. Пеленки ассемблерного кода =) Кстати не понял зачем ты запрещаешь прерывания? В твоем примере идет просто запихивание нужного байта в UDR. Да это будет работать, но только если в программе нету больше ничего что занимает процессорное время. А если там висит еще ШИМ на 100Мгц? Вот тут и начинаются проблемы. Поэтому лучше делать прерывания по приему и передаче символа. Так удобней и не будет потери данных.

    1. Да тут я просто для примера. Чтобы не мешалось. Т.е. делаю пример гарантированно рабочий, что бы там не было накосячено выше и ниже по коду — но как дойдет до сюда, то сработает на 100%. А так конечно, еще с буфером в памяти. Главное на самом деле тут процедура инициализации UART =) Остальное все фигня.

  3. Ладно, уболтал =) А насчет инициализации вставлю свою палку в колесо песне ;)

    Вот мои инит:
    // USART initialization
    // Communication Parameters: 8 Data, 2 Stop, No Parity
    // USART Receiver: On
    // USART Transmitter: On
    // USART Mode: Asynchronous
    // USART Baud Rate: 115200
    UCSRA=0x00;
    UCSRB=0xD8;
    UCSRC=0x8E;
    UBRRH=0x00;
    UBRRL=0x03;

    А вот твой:
    uart_init: LDI R16, low(bauddivider)

    OUT UBRRL,R16

    LDI R16, high(bauddivider)

    OUT UBRRH,R16

    LDI R16,0

    OUT UCSRA, R16

    LDI R16, (1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<TXCIE)

    ; Прерывания запрещены, прием-передача разрешен.

    OUT UCSRB, R16

    LDI R16, (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)

    ; Формат кадра — 8 бит, пишем в регистр UCSRC, за это отвечает бит селектор

    OUT UCSRC, R16

    RET

    (да мой сделан на сях =) я это к тому что надо приводить примеры как у атмел =) )

    1. К слову, в Си использование символических констант тоже никто не отменял. А некторые примеры у Atmel — чистейшей воды «индусский код» (например, AVR452 — код явно избыточен и только путает, т. к. контроллер CAN и так довольно загадочно документирован).

      1. Код атмел тем и хорош, что если нужно быстрое «индусское» решение. Ты его получаешь сразу. Хочешь сделать лучше/быстрее/красивше (нужное подчеркнуть) берешь за основу код атмел и дорабатываешь.

  4. Низнаю куда запихнуть вопрос, но вобщем он связан с UART!
    Значит спаял я программатор и решил освоить UART, почитал, полистал вроде ничего сложного. Быстренько написал прогу на C в Code VisionAVR.
    ———-
    #include
    #include

    void Send(flash unsigned char buf[])
    {
    int i = 0;
    while (i != 6)
    {
    UDR = buf[i];
    i++;
    }
    }

    void main()
    {
    // BEGIN INITIALIZATION BLOCK

    // USART initialization
    // Communication Parameters: 8 Data, 1 Stop, No Parity
    // USART Receiver: On
    // USART Transmitter: On
    // USART Mode: Asynchronous
    // USART Baud rate: 9600

    UCSRA=0x00;
    UCSRB=0xD8;
    UCSRC=0x06;
    UBRRH=0x00;
    UBRRL=0x81;

    // END INITIALIZATION BLOCK
    while (1)
    {
    Send(«Hello!»);
    }

    }
    ———————-
    Перед этим спаял платку, куда впихнул кварц на 8 Mgz, и вывел RxD и TxD как я понял нужно отредактировать фьюзы что бы кварц заработал, попытался и больше проц не определялся)) ну ладно был второй!
    Так вот спаял я по вашей схеме переходничёк на микросхеме ST232BN.
    Все соеденяю вывод МК TxD к Rx а RxD к Tx, оба устройства запитываю от одного БП,
    Запускаю Serial Port Monitor, и ничего ни одного байтика! Тыкаю щуп осциллографа к ноге у МК, видно что данные идут, тыкаю на ногу ST232BN, таже картина, прохожусь по контактам COM тишина. В чем может быть проблемма?

  5. Вот по поводу скорости она зависит от частоты, к плате припаян кварцевый ркзонатор, но я так понял, без вьюзов настройки фьюзов он не работает, на какой частоте работает тини2313 по умолчанию? вообщем и в программе и в прошивке стоит 9600, а стопы и четности совпадают.
    Значит поменял, местами, появился сигнал на 2 разъеме ком порта, сейчас проснифим)

    1. Эм.. щас даташит гляну. По дефолту Tiny2313 работает на частоте 8Мгц с предделителем на 8. т.е частота 1Мгц. Если сбросить фуз CKDIV8 то предделитель отключится и частота станет 8мгц. Если работаешь от кварца, то предделитель тоже надо учитывать. Я так его обычно сразу отрубаю нахрен, чтобы воду мне не мутил.

      Предделитель можно отрубить и программно вот так:
         OUTI CLKPR,1<<CLKPCE
         OUTI CLKPR,0b10000000
         OUTI CLKPR,0<<CLKPCE

  6. Да не айс… проторчал, по перепрошивал и так и сяк, не отпраляет он ничего кроме 0 )) в статьях все просто инициализировал, и шли, хочешь байт, хочешь массив, шлю а принимаю нули. Может раскажете поподробней как слать и принимать байты на С.
    Вот допустим выше указанный код неработает, если просто в бесконечный цикл поставить например UDR = 0x01, такая же картина, сегодня конечно ещё почитаю, но может кто здесь даст совет.

    1. У тебя скорей всего проблема с тактованием. Разберись вначале на какой частоте у тебя работает Тини :) Спорю на 5 баксов, что про предделитель ты не знал и его не отрубил. А значит все твои расчеты UBBR пошли прахом. Порт принимаеть то принимает, но не понимает что там ему послали. Т.к. синхронизация по скорости идет.

      1. У меня было то же самое. atmega128 принимала от 0 до 95% передаваемых данных другой такой же. hex-ы 1:1, кварцы тоже, оказалось случайно по ошибке на одном из процессоров был выставлен флаг m103c, так что стоит проверить все. :) (Даже зарегистрировался специально чтобы задать это вопрос, ну теперь может моя шишка кому-то поможет.)

  7. Нашел заветную фразу, что фьюзы по стандарту настроенны на внутренний RC-генератор с частотой 1 МГц переправил программу все начал нормально передавать и принимать. Засунул все в цикл, только превые два байта, читаются)), впринципе думаю подправить чуток цикл, что б он, проверял прошол ли байт и только потом посылать второй, или задержку установить.

    Кстати ещё где про фьюзы для моего МК нормально можно почитать, а то везде по разному такая билиберда, вот по одному мануалу установил, и усе, больше МК не определяется))

    1. Как это где? В Даташите! :) Там все написано! У тебя какой программатор то? А то смотри, у разных прошивающих прог интерпритация фузов разная. Одни рисуют их инверсными, другие нет. А перепутешь — лочится кристалл :)

      По дефолту не на 1мгц, а на 8мгц деленое на предделитель который по дефолту выставлен в 8! Так и получается 8/8 = 1МГц. Предделитель отключается либо сбросом фуза CKDIV8 либо программно, записью в регистр определенный — код я тебе выше уже дал. На ассемблере правда;) Но думаю ты найдешь в шите что там за бит я тебе указал и на сях его выставишь.

  8. Все ясно, спасибо!!! Программатор я спаял c этого сайта котрый на Com порт, прошиваю прогой Uniprof.
    Млин а у меня даташит не полный, там этого нет, где то на дисках должен быть полный будем искать ))

    1. В унипрофе все фузы один в одни как в даташите. В сторонних мануалах может быть указано для понипрога или авр дудовской оболочке — там инверсны. Так что я обычно не гляжу что пишут в мануале, а выставляю исходя из смысла.

  9. Все нормально принимает и отправляет, DI HALT у тебя в планах нет написание статьи о связи МК с внешней EEPROM, было бы интересно почитать, а пока погружусь в эту тему сам ;)

  10. DI HALT извиняюсь за оффтоп но больше незнаю куда задать вопрос, а так как тема про EEPROM затронулась, прошу мне помочь!
    Вообщем подключил я к своей тиньке AT24C256, все работает, записывал байт в ячейку по адресу 0xAA(тобишь в десятичной 170), так вот пытаюсь записать в меньше 170 не пишет, больше пробовал только 1000 тоже не пишет.
    Так вот вопрос в память можно записать 32768 байт, какой у них диапазон адресов?
    В даташите написано:
    «Память размером 256 кбит внутренне разделена на 512 стр. по 64 байта в каждой. Для доступа к памяти по произвольному адресу требуется 15-разрядное слово адреса.»
    Сегодня целый день проковырялся, и результат 0;
    Надеюсь на вашу помощь!

    1. Толком с АТ24 я толком не работал, так что вот так сходу не подскажу как там адресуется. Мне обычно всегда хватало внутренного епрома встроенного почти во все AVR. У меги8 пол килобайта епрома.

      Насколько я знаю АТ24С256 не бьется на банки. У нее линейная память, а адрес задается двумя словами.

      формат вроде бы такой:
      ST:адрес девайса:RW:A:старший байт адреса:А:младший байт адреса:А:байт данных:А:SP это для одного байта. Для последовательности вместо стопа гонишь аскноледж и следующий байт.

      1. Вот допустим запись у меня

        void write_byte_eeprom (unsigned int address, unsigned char data)
        {
        unsigned char sSREG;
        sSREG=SREG
        #asm(«cli»)
        i2c_start(EEPROM_BUS_ADDRESS);
        i2c_write((unsigned char)address>>8);
        i2c_write((unsigned char)address);
        i2cwrite(data)
        i2c_stop()
        SREG = sSREG;
        delay_ms(11)
        }
        Вызываю функцию как в примере
        write_byte_eeprom(0xaa,0x55) или тот же результат write_byte_eeprom(170,0×55)
        Читаю от туда тоже нормально.
        Я то думал что к аддресу ячейки просто нужно обратиться по её номеру)) но не тут то было!

  11. полазив по нэту ничего вразумительного не нашел, даже банально как вообще организована память, вот допустим что такое старший и младший байт адреса, что такое 15-разрядное слово адреса, хочу тут копнуть чуть поглубже и разобраться в этом досканально, но незнаю даже с чего начать. Формулы бы как высчитывать следующий адрес, так я даже первого незнаю)) ладно утро вечера мудренее, буду завтра мозг себе ломать) может есть какие ссылки на эту тему, или статьи или книжки буду очень признателен ;)

    1. Адресована она линейно должна быть. Т.е. адреса от 000000000000000 до 111111111111111 bin. Вот тебе и старший/младший разряды.
      старший байт адреса шлется первым. За ним младший. Т.к. адрес 15 разрядный, то 7 бит старшего адреса роли не играет.

      А запись тут страничная. Т.е. писать надо не более чем 64 байта за раз.

      1. по 64 байта за раз это при страничном режиме записи, пока ещё это не освоил и хочу научиттся читать и писать в произвольную ячейку.
        Значит первая ячейка это 15 нулей, а последняя 15 едениц, а как допустим получить доступ ко второй, третей, сто сорок первой ячейке, как высчитать их адресс?

          1. либо я чего то не панимаю… либо… я вообще ничего не понимаю…))
            Как я понял что бы обратиться допустим к ячейке №24576, мне нужно обратиться к адресу 110000000000000, те в Hex 0x6000.
            Так вот задаю write_byte_eeprom(0x6000,0×55) и ничего не проиходит.
            Что не так???

            1. Все досканально просто, только вот низнаю почему до этого, это чудо не работало, просто вызывается
              write_byte_eeprom(N,0×55)
              где N, любое число от 0 до 32767, чего сним случилось, до этого то писал то не писал, спасибо, и извини что тут столько воды намутил ;)
              Я не волшебник… я только учусь…

            2. Бля, адрес передается двумя байтами. Вначале старший байт адреса потом младший. Я Не знаю чо там у тебя творит эта Сишная функция, поэтому пиши на ассемблере :)

              КОроче, суешь вначале в i2c служебный байт. Потом шли туда число 0x60 (старший байт адреса) потом 0x00 (младший байт адреса) потом только байт 0x55. ФОрмат кадра я тебе уже писал.

              Кстати какой тип данных у write_byte_eeprom(0×6000,0×55)? Я чето сильно сомневаюсь что первый аргумент это двубайтное число.

              1. Я тут по ошибке грохнул твой коммент который с кодом. Он дважды застрял на модерации и я случайно два раза кликнул ;)

                Там в первой строке:
                i2c_start(EEPROM_BUS_ADDRESS); //Передача адреса памяти она задана как константа 0xa0(по даташиту)

                Адрес у микросхемы памяти задается еще выводами А1 А2 и А0 так что проверь на каком реальном адресе пасется твоя ПЗУшка.

                1. 0xa0 это для записи а 0xa1 это для чтения, я там писал что функция уже сама делает все преобразования. Вот:
                  i2c_write((unsigned char)address>>8);
                  Это старший байт.
                  i2c_write((unsigned char)address);
                  Это младший байт.
                  i2cwrite(data);
                  а это запись символа. (см. код функции выше)
                  а не работало это все из за банльной ошибки.
                  Я не указал инициализацию I2C функцией
                  i2c_init() )))
                  Вот так все просто)
                  Сейчас, уже написал функцию котрая записывает и читает и всё это дело по UART передает, все отладил все работает ;)

  12. А в чём может быть проблема. Написал обработчик прерывания
    Interrupt [USART_RXC] void usart_rx_isr(void)
    {
    if (UDR == ‘C’)
    {
    eeprom_clear(); //Это моя функция
    }
    }
    установи UCSRB=0x98;

    У меня ещё есть обработчик, по изменению уровня на PB5.
    Так вот, отправляю я к МК символ ‘С’, и все то ли подвисает, то ли что? незнаю, но больше МК ни на какие прерывания не реагирует.

      1. Я если честно в асме не силён, а что такое оттрасировать по прерыванию, да и можно ли в AVR Studio вызвать прерывание по USART, обычные получается, а в этот обработчик никак зайти не могу?

        1. Ну так а кто виноват? Я же предупреждал, что микроконтроллеры изучать надо с ассемблера :)

          В студии можно. Достаточно поставить флаг этого прерывани вручную и обработчик на него перейдет.

          Кроме того, ничто не мешает вызывать тебе прерывание как обычную функцию RJMP inettrrupt_name из любой точки программы :) И опа!

          1. Фигня там вообщем какя то происходит, обработчик прерывания работает, но как выходит из перывания, все регистры портов, регистры eeprom меняют свое значение, из за изменений на порту, вызывается другое прерывание, ну а дальше в цикл, и регистры портов постоянно меняются. А авр студио пишет
            мол
            Stack Underflow at 0x0106
            Unintialized stack pointer used 0x0036

            1. Гы. Косяк со стеком. Где то что то пошло не так :) Ты вручную в стек ничего не толкал?

              Скорей всего переполнение. Проследи за регистром SP до вызова прерывания и что пихается в стек во время вызова прерывания и работы с программой епрома.

              1. А как проследить за этим регистром, и что я должен там увидеть.
                Во время вызова прерывания, записываются какие 3 байта (смотрю в окне (Memory->Data), задача функции eeprom просто прервнять двум переменным, котрые там записаны, 0. В принципе это и происходит, а вот при выходе из обработчика, начинаются лаги, перебрасывает в другой обработчик и там виснет, на стек весь заполняется какой то фигнёй и все байты становятся красными.

                1. SP показывает на адрес в стеке (в конце ОЗУ) где находится в данный момент указатель. При вызове прерывания, по идее, в стеке должен ныкаться только адрес возврата (он там должен быть по любому) а еще возможно сныкиваются регистры которые модифицируются в обработчике прерывания (чтобы по возврату фоновая программа не свихнулась) Вот тока почему у тебя весь стек заполняется ??? ПО идее должно быть максимум на 32 (регистры)+2(адрес возврата) байт загрузка стека. И то далеко не всегда все регистры нычутся, компилятор должен быть умным и не сохранять тупо все. Посколькоу это прерывание, то параметры в него не передаются, а значит стек особо не нагружается. Но что там вытворяет компилятор я хз. Открывай листинг ассемблерный и смотри от вектора прерывания и до RETI из прерывания. Что у тебя пихается в стек и каким образом.

                    1. Вообщем было две ошибки, неправильно сконфигурировал USART, по этому и не работало, а фигня всякая в стек лезла из за того что прерывание вызывал #asm («rjmp _usart_rx_isr»), незнаю почему, но именно из за этого.

                    2. Бугагагага, жжошь!

                      Почемуто именно изза этого… не почему то, а именно иззза этого! Ты входишь в прерывание по переходу (естественно в стек ничего не грузится, т.к. по простому jmp) а при выходе из стека выгружаешь то, что туда не было положено — как итог у тебя указатель стека смещается и показывает невесть что, за выходящий адрес берется первый попавшийся в стеке мусор, программа выходит хрен знает куда и начинается кавардак.

        2. Попробуй отключить нахрен оптимизацию. Изза нее часто много бед. Когда с виду рабочие программы перестают работать, т.к. компилятор повыкидывал из них наиболее значимые блоки в угоду оптимизации =)

  13. Господа, позвольте вопросик!
    А как соединить 3-5 контроллеров между собой, да еще если растояние между ними от 2 до 25 м? Можно ли вместо RS232 применить RS485? И как это лучше сделать, преобразовывать: UART->RS232->RS485-> линия <-RS485<-RS232<-UART, или можно прямо с контролера?

    1. Есть микросхемы которые из стандартного UART делают RS485 точно не подскажу какая (MAX485???), но точно знаю что есть — видел в одном девайсе. А там либо по 4м проводам полнодуплекс, либо по двум односторонняя связь.

      Но! 25м это не так уж много. Думаю хороший витой телефонный кабель и схема

      UART-MAX232——-MAX232-UART будет работать на отлично.

      А 2 метра так и обычный 5ти вольтовый уарт тащит на раз без помех.

      1. как 25 метров? RS-485 (магистраль) он по своей природе разрабатывался для БОЛЬШИХ растояний. уменя связка: устройство адаптер RS232/RS485 витая пара 900метров адаптер RS485/RS232 комп, работает на скорости 57600
        правда блоки питания (12V) для адаптера долго подбирал, чтобы падения напряжения было минимальное.

  14. Здравствуйте. Есть задача также соединить два устройства и думаю что все-таки через сеть 485. Управляемое устройство (это коммутатор) имеет вход RS232. Подскажите новичку несколько контроллеров, которые имеют UART. Хотелось бы точное наименовани, чтоб не ошибиться потом при приобретении. Какие-нибудь простые.

    1. А зачем 485, если устройств всего 2? RS485 разрабатывался для того, чтобы простейшим способом, близким к RS232, обьединить НЕСКОЛЬКО устройств. Для этого их включают параллельнно, на одну линию с общим токозадающим резистором. Помехоустойчивость ее гораздо хуже, чем у 2х полярного сигнала на RS232.(При 2х полярном сигнале не происходит накопления заряда на емкости линии, аналогичном сглаживанию пульсаций выпрямителя). Для 2х устройств в 485 нет никакой небходимости, RS232 будет гораздо лучше. А UART сейчас есть практически в каждом PIC или ATMEL (кроме самых старых или примитивных, вроде PIC12C508). MAX232 — тоже везде полно.
      Из простых PIC UART есть, например, в PIC16F628, (в 84 еще не было). Из Атмел — уже в AT90S2313.(в 1200 еще не было).

  15. Да, для большого расстояния мне нужно было. Я предполагаю, что 485 как раз более защищеннее будет. Здесь я имею ввиду метров 30. По стандарту, кстати, сеть до 1200м.
    По поводу UART — я глянул на сайт атмеля и там в номенклатуре контроллеров только у одного в описании увидел «UART» :) Как раз в 2313. Да, скорей всего во многих он есть, но качать асе даташиты, чтоб узнать в каких есть, а в каких нет как-то не очен… :)
    От готового куска кода не отказался бы — будет пример мне как работать с ним

  16. Возникло несколько вопросов.
    Есть RXC и TXC — флаги завершения приема и передачи. Есть прерывания. Причем последние можно запретить, а можно и разрешить.
    Предположим мне нужно сделать отправку нескольких байт. Если у меня прога только этим и занимается, то я так понял в UDR можно писать и по TXC, и по флагу UDRE, так? Используя проверку командой SBIS. Т.е. отправка прошла — суем в UDR следующий байт. Или, UDRE свободен — опять же суем в UDR следующий байт. Не важно — с разравами или нет там будет процесс.
    Но этот механизм нельзя же назвать «прерыванием» ? Прерывания это же «толчёк» процессору перейти на некоторый кусок кода, где что-то будет выполнятся. Т.е. не _мы_ что-то в цикле проверяем, а при возникновении прерывания во время выполнения основной программы происходит переход на этот маленький кусок кода. А остальное время в проце выполнялнялась бы основная программа. Так? Или как раз в основной программе и есть где-то строчка проверки? Ведь если мы вдруг попадаем в процедуру отсылки байтов с использованием UART, а он может долго отправлять, то пока все запланированное не отправим — не вернемся в основную программу. Так и застрянем в этой процедуре.
    Я же себе представляю работу так — допустим пока идет отправка одного байта — крутимся в основной программе, что-то считаем, смотрим состояния пинов портов и т.д. Как только байт отправлен, переходим по прерыванию опять в процедуру отправки следующего байта и пока он там отправляется опять же продолжаем крутиться в основной программе.
    Так ли это обычно реализовывают и возможно ли сделать именно так. А то прочитал статью про прерывания и не понял как все-таки их использовать.

    1. Как обычно делается, если надо отправлять данные по медленному уарту фоном.

      1)Делается буфер в памяти куда мы эти данные пишем. Ну или тем или иным способом даем понять, что у нас есть данные на отправку.

      2) делаем обработчик прерывания на завершение отправки данных (TXCIE=1)
      в обработчике делаем проверку нашего буфера на наличие данных под отправку. Если есть — отправляем первый байт в регистр уарта UDR и щелкаем счетчиком. Выходим. На все про все только время выполнения дестяка команд проверки. Остальное сделает аппаратный блок уарта.

      Отправка:
      Сознательно, в фоновой программе, закидываем в буффер данные (или просто подготавливаем данные к отправке, например указатель нужный заполняем) и !!!сами отправляем первый байт!!! по выдаче этого байта сработает обработчик прерывания из пункта 2 и отправит следующий байт, потом еще и еще, до тех пор пока не поймет, что слать больше нечего. На этом произодет выход из прерывания без отсылки байта и посылалка встанет до тех пор, пока мы снова не наполним буффер и не запустим вручную первый байт.

  17. Да, я это читал. То что можно перепрыгнуть в подпрограмму отправки данных по появлению флага — это понятно. Я так понимаю это надо делать в цикле основной программы — вставить проверку на наличие флага, так? Но в статье есть фраза «…биты RXC и TXC это флаги завершения приема и передачи, соответственно. RXC встанет когда непрочитанный байт вылезет в регистр UDR, а TXC встает когда последний стоп-бит прошел, а новое значение в UDR не поступило. Т.е. после прохода всех байтов. Также одновременно с этими флагами вызывается прерывание…» Получается, что есть два механизма, как я понимаю: отслеживание флагов и «прерывание». Если с первым вроде понятно, то как же использовать «прерывание»?. Или оно это и есть как раз проверка соответствующих флагов?

    1. А вот как:

      В прерывании, по UDRE делаешь отправку следующего байта в UDR. Так? Проверку на его пустоту делать уже не нужно — прерывание тому доказательство. Там же, в прерывании, делаешь проверку на то был ли этот байт последний. И если был — запрещаешь прерывание UDRE ее же обработчиком.

      ТЕперь тебе надо отправить несколько байт. Ты в главной программе разрешаешь прерывание по UDRE. И шлешь первый байт. Все! Можно идти по своим делам дальше, прерывание само все сделает.

      Дальше по опустошении буффера проц уходит на прерывание UDRE сам загружает в себе в UART следующий байт. И по его отправке снова сюда возвращается. Если все байты отправил, то запрещает вызов себя (чтобы на каждом такте не срываться по флагу пустого регистра, который будет стоять).

      Автоматика!

    2. Отправку можно сделать и по TXC это будет чуть проще в плане кода, но идеологически менее правильно. (об этом я и втолковывал Маддеву в самом начале комментов). Хотя я обычно делаю отправку следующего байта по Tx — лениво каждый раз UDRE запрещать. :)

  18. Да да, это все я понял. Единственное, здесь и вообще везде где вырабатывается прерывание использование его — это циклическая проверка соответствующего флага? Одной командой? Если есть — перепрыгиваем на процедуру соответствующую, нет — выполняем дальше осн. программу до следующей проверки. Просто проверка всех прерываний ложится на плечи программиста. А ведь есть еще и команда запрета прерывания. Что она может запретить тогда? Перескок на процедуру в случае возникновения прерывания какого-нибудь?.

  19. А ведь я про это писал!
    Прерывание ведет себя похоже, но несколько иначе. При возникновении прерывания текущий адрес аппаратно суется в стек, а в программный счетчик запихивается адрес вектора прерываний. У каждого прерывания есть свой фиксированный адрес-вектор куда будет отправлен процессор при возникновении прерывания. Все эти вектора собраны в кучу в самом начале памяти программ и составляют таблицу векторов прерываний.
    Вектора указывают на ячейки друг за другом. Так как у нас в одну ячейку можно сунуть только одну команду, а обработчик прерывания обычно занимает куда больше места, то выбора у нас особого нет — в эту ячейку мы пишем RJMP и упрыгиваем туда, где просторней и можно писать много, долго и счастливо. Если какое то прерывание нам не требуется, то от греха подальше лучше его заглушить командой RETI.

    После обработки прерывания возврат к основной программе осуществляется командой RETI

  20. Это то я понял. Это процессы уже в самом МК после того как программа написана, скомпилированна, зашита и работает. А в коде как надо писать?
    «RXC встанет когда непрочитанный байт вылезет в регистр UDR, а TXC встает когда последний стоп-бит прошел, а новое значение в UDR не поступило. Т.е. после прохода всех байтов.
    Также одновременно с этими флагами вызывается прерывание».
    Мне надо в коде поставить проверку битов RXC (или TXC) и в зависимости от условий перепрыгнуть на процедуру? А там в конце нее не забыть еще прописать команду RETI.
    «…Также одновременно с этими флагами вызывается прерывание» ну вот вызвалось оно. Дальше что. Само оно толкнет проц куда надо, или я контролирую этот момент?

    1. Нет не надо тебе делать проверку этого бита. У меня он проверяется потому что ПРЕРЫВАНИЯ ВЫКЛЮЧЕНЫ ВООБЩЕ. Т.е. я вручную все делаю.

      Тебе же надо:
      1) Заполнить вектор прерывания по UDRE или RXC, записав туда RJMP куда нибудь.
      Таблицу векторов прерываний смотри в даташите. Она у каждого АВР различается и расположена в первых 20-40 байтах от начала памяти программ.

      2) Где нибудь написать обработчик прерывания.

      И когда произойдет сработка прерывания проц сам прыгнет на адрес вектора, оттуда по твоему RJMP уйдет на обработчки, а вернется оттуда только по команде RETI которую ты сам же должен ему и прописать.

      Типичная таблица прерываний из одного моего проекта:
      ;=========== Interrupt Vectors =====================================================================================

      .ORG INT0addr ; External Interrupt Request 0
      RETI
      .ORG INT1addr ; External Interrupt Request 1
      RETI
      .ORG ICP1addr ; Timer/Counter1 Capture Event
      RETI
      .ORG OC1Aaddr ; Timer/Counter1 Compare Match A
      RETI
      .ORG OC1Baddr ; Timer/Counter1 Compare Match B
      RETI
      .ORG OC0Aaddr ; Timer/Counter0 Compare Match A
      RETI
      .ORG OC0Baddr ; Timer/Counter0 Compare Match B
      RETI
      .ORG OVF1addr ; Timer/Counter1 Overflow
      RJMP t1int
      .ORG OVF0addr ; Timer/Counter0 Overflow
      RETI
      .ORG URXCaddr ; USART, Rx Complete
      RETI
      .ORG UDREaddr ; USART Data Register Empty
      RETI
      .ORG UTXCaddr ; USART, Tx Complete
      RETI
      .ORG ACIaddr ; Analog Comparator
      RETI
      .ORG PCIaddr ; Pin Change Interrupt
      RETI
      .ORG USI_STARTaddr ; USI Start Condition
      RETI
      .ORG USI_OVFaddr ; USI Overflow
      RETI
      .ORG ERDYaddr ; EEPROM Ready
      RETI
      .ORG WDTaddr ; Watchdog Timer Overflow
      RETI

      .ORG 0x0013
      Вот тут уже пошел код основной программы.

  21. DI HALT, тебе вопрос, вроде детский ,но трепит мне нервы уже которую неделю. Мой AVR ATtmega8515 спит. Причина не ясна. Таковы симптомы: на кварце(4 МГц) имееться стабильная частота 4 МГц. Запитую свой AVR с 4х пальчиковых батареек т.е. около 6 В. Программу написаную на асемблере в АВР-студио от кампилированную через ПониПрог прошиваю в микруху. Все проходит удачно без ошибок. Но! при самой эллементарной программе, например, запись в порт С кода 0х41, реакции нет. AVR- спит! Тоже и с USART. ничего нет на пине TxD. Фьюзы устанавливал на свое усмотрение с книжкой в руке, т.е. лишнего ничего нет, только частота кварца на 4 МГц.
    Думал микруха — брак, купил новую и все равно ничегою. Может у тебя идея есть поэтому поводу? Возможно где-то в фьюзах есть секрет.
    Я даже начаю подумывать , что сама ATtmega8515 какие-то особенности имеет.

    1. Запитую свой AVR с 4х пальчиковых батареек т.е. около 6 В.

      Даташит надо было читать внимательней. Есть вероятность, что ты ее спалил нах — напряжение питания не выше 5 вольт.

      Код программы в студию!

    2. «Фьюзы устанавливал на свое усмотрение с книжкой в руке»

      Ты в курсе, что в Пони прог фьюзы инверсные? Т.е. если в даташите написан 0, то в понипрог надо ставить галочку?

    1. А вот это не факт. Генератор и порты это разные блоки. Ну и в коде могут быть косяки.

      Код из микрухи читается обратно?

      З.Ы.
      Я понипрог на дух не переношу. На работе стояла у меня. Крайне странно работает эта софтина. Бррр…

          1. Как высказался один камрад на форуме: «Пони прог — самый простой и эффективный способ заблокировать МК!»

            Еще она была замечена в симуляции. Т.е. вроде прошивка прошла, но на самом деле нифига не было. На работе пару раз с таким сталкивался.

          1. Ну предположим вот эта программа пресылки одного байта в цикле по последовательному передатчику:

            .include «m8515def.inc»
            .list
            .def temp=R18

            .cseg
            .org 0

            lds temp,low(RAMEND)
            out SPL,temp

            M: clr temp
            ldi R17,0x33
            ldi R16,0
            USART_Init: ;Установка скорости передачи
            out UBRRL,R17 ;изменяя данные в R16,R17 вариируеться скорость передачи данных
            out UBRRH,R16
            ;Включения приемника и передатчика
            ldi R16,(1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<TXCIE);Разрешение приема и передачи, запрет прерываний
            out UCSRB,R16
            ;Установка формата кадра: 8 бит данных, 1 стоповых бита
            ldi R16,(1<<URSEL)|(0<<USBS)|(1<<UCSZ0)|(1<<UCSZ1)
            out UCSRC,R16

            ;Пасылка кадра данных длиной 5-8 бит
            ldi R19,0x41 ;Тут хранится данное, которое нужно передать
            USART_Transmit:
            sbis UCSRA,UDRE ;Ожидание, пока буфер передачи очистися
            rjmp USART_Transmit
            ;Помещаем данные (из R16) в буфер. Начинается передача
            out UDR,R19
            rjmp M

            А вот ее код в хексе 20 91 5f 00 2d bf
            22 27 13 e3 00 e0
            19 b9 00 bd 08 e1
            0a b9 06 e8 00 bd
            31 e4 5d 9b fe cf
            3c b9 f2 cf

            1. Вроде ничего в глаза не бросается. Только зря ты в общий цикл включил иницализацию уарта. Она должна быть всего один раз. Может в этом косяк — при переинициализации может сбиваться передача, но не уверен — не проверял

              В самой студии, в отладчике, как код себя ведет? Прогоняется по всем правилам? Если да, и в UDR данные пихаются, то они обязаны в той или иной форме вылезти через UART. Осциллографом бы поглядеть на линии уарта, что там происходит.

              З.Ы.
              Проводки RX и TX не перепутал? Стандартная и самая частая ошибка :)

              З.З.Ы.
              Сама схема на МАХ232 гарантированно работает? Вход на выход проверял?

              1. В общем программа в симуляции работает как нужно. Согласен, это тупо включать инициализацию при каждом цикле, ну это просто не рационально. Исправлю.
                А вот на осцилограмме все молчком :-(
                МАХ232 проверял, коратил выходы Txd — Rxd и засылал данные. Они без искажения возвращались. То есть это штучка работает. Верно?
                Вот что я еще заметил: на одной ноге кварца 4Мгц и 4Вольт, а на второй 4МГц и 1Вольт относительно корпуса. Это нормально?

                1. Макс у тебя работает верно.

                  На кварце так и должно быть. Но можешь попробовать переключить на внутренний генератор.

                  А что у тебя с ресетом? Подтянул его к +5вольтам через резистор в 10к?

                    1. Наивный. В результате у тебя МК от случайных наводок ресетится как попало и когда попало.

                      Сопротивление внутреннего подтягивающего резистора ресета порядка 100кОм. Это слишком много, можно считать вход RESET входом Hi-Z Сброс происходит при поднесении руки на расстояние до 5см.

                      Отказаться от ресета можно только на тини, где есть фуз бит ресетдизабл (при этом ресет нога превращается в ногу порта), но тогда отваливается последовательное программирование.

                    2. то есть, ты говоришь, мой МК спит именно по этому? Или есть еще какие-нибудь предположения? Если это именно по этой причине,мне прийдеться перетравливать всю плату:-( , а там уже налеплено…..

                      Эту подсказку я обязательно учту.

                    3. Напаяй ресет сверху на микруху. ПРоверишь предположение. Но должно быть оно. Кстати, ты программатор отключаешь от МК? Некоторые программаторы не дают стартовать МК, зажимая ресет в ноль

                    4. Программатор, я отключаю. Я считал так, что прошитый МК после того как всунул штерек питания начинает работать в соответствии с программой. После прошивки, я обязательно выдергивал программатор из платы с МК. Ну чтож попробую.

  22. здравствуйте! на основе вашего (и многих других) написал такой код для атмеги 16-ой:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    BEGIN:
    ;UART setup
    ldi  R16, 0x0C
    out  UBRRL,R16
    ldi  R16, 0
    out  UBRRH,R16
    ldi  R16,2
    out  UCSRA, R16
    ldi  R16,(1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<TXCIE)
    out  UCSRB, R16	
    ldi  R16, (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
    out  UCSRC, R16
    ldi  R16, 0x41 ;буковка A
     
    UART_SNT: sbis UCSRA,UDRE
    rjmp UART_SNT ;æä¸ì ãîòîâíîñòè - ôëàãà UDRE
    out  UDR, R16 ;øë¸ì áàéò!
     
    ; Insert nop for synchronization
    nop
     
    rjmp BEGIN

    проблема в чём… пишет в порт всякую чушь! по спекам этого мк его дефолтовая частота RC=1MHz, по таблице на 1 МГц для 9600 (которые и необходимы) с ошибкой в +0.2% U2X=1 и UBRR=12 (делал и по вашим настройкам, но там -7%). COM сконфигурен вроде бы верно (на 9600), работу ST232 проверял замыканием Rx на Tx — идут правильные байты.
    прошивается uniprof’ом верно, потом его закрываю — убирается reset и в порт начинает валиться чешуя.
    не подскажете, в чём проблема может быть? заколебался уже тыкаться… :(

    1. Во первых убери многократную инициализацию. У тебя цикл зачем то постоянно перенастраивает UART. Причем во время отправки байта :).

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      
      BEGIN:
      ;UART setup
      ldi  R16, 0x0C
      out  UBRRL,R16
       
      ldi  R16, 0
      out  UBRRH,R16
       
      ldi  R16,2
      out  UCSRA, R16
       
      ldi  R16,(1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<TXCIE)
      out  UCSRB, R16	
       
      ldi  R16, (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
      out  UCSRC, R16
       
      ldi  R16, 0x41 ;буковка A
       
       
      UART_SNT: sbis UCSRA,UDRE
      rjmp UART_SNT 
      out  UDR, R16 
       
      ; Insert nop for synchronization
      nop
       
      rjmp UART_SNT  ;<<<<<<<<<<<<<<<<<<<<<<<!

      Код инициализации верный. Но ты уверен, что у тебя 1МГц? Проверь фузы на предмет этого. Проверь терминалку тоже :) Чтобы везде было 8бит, 1стоп и прочие мелочи. Рекомендую программу Terminal v1.9b

      З.Ы.
      тэг для кодооформлятора:
      <pre lang=»AVR» line=»1″> код </pre>

          1. ммм… снова затык) суть задачи — сделать некий параллелайзер… т.е., UART>параллельный и параллельный>UART. с UART’а в портА байты идут очень красиво, а вот если кинуть ногу INT0 на землю, то из Tx валится 0xFF. за исключением того, что прерывание надо делать по фронту, а не по низкому уровню (втупил я чего-то), что здесь не так?

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            31
            32
            33
            34
            35
            36
            37
            38
            39
            40
            41
            42
            43
            44
            45
            46
            47
            48
            49
            50
            51
            52
            53
            54
            55
            56
            57
            58
            59
            60
            61
            62
            63
            64
            65
            66
            67
            68
            69
            70
            71
            72
            73
            74
            75
            76
            77
            78
            79
            80
            81
            82
            83
            84
            85
            86
            87
            88
            89
            90
            91
            92
            93
            94
            95
            96
            97
            98
            99
            100
            101
            102
            103
            104
            105
            106
            107
            108
            109
            110
            111
            112
            113
            114
            115
            116
            117
            
            .include "m16def.inc"
            rjmp RAM_Flush
             
            .ORG  INT0addr    ; External Interrupt Request 0
            rjmp  write2uart
            .ORG  INT1addr    ; External Interrupt Request 1
            RETI
            .ORG  OC2addr      ; Timer/Counter2 Compare Match
            RETI
            .ORG  OVF2addr    ; Timer/Counter2 Overflow
            RETI
            .ORG  ICP1addr    ; Timer/Counter1 Capture Event
            RETI
            .ORG  OC1Aaddr    ; Timer/Counter1 Compare Match A
            RETI
            .ORG  OC1Baddr    ; Timer/Counter1 Compare Match B
            RETI
            .ORG  OVF1addr    ; Timer/Counter1 Overflow
            RETI
            .ORG  OVF0addr    ; Timer/Counter0 Overflow
            RETI
            .ORG  SPIaddr      ; Serial Transfer Complete
            RETI
            .ORG  URXCaddr    ; USART, Rx Complete
            rjmp  read_from_uart
            .ORG  UDREaddr    ; USART Data Register Empty
            RETI
            .ORG  UTXCaddr    ; USART, Tx Complete
            RETI
            .ORG  ADCCaddr    ; ADC Conversion Complete
            RETI
            .ORG  ERDYaddr    ; EEPROM Ready
            RETI
            .ORG  ACIaddr      ; Analog Comparator
            RETI
            .ORG  TWIaddr      ; 2-wire Serial Interface
            RETI
            .ORG  INT2addr    ; External Interrupt Request 2
            RETI
            .ORG  OC0addr      ; Timer/Counter0 Compare Match
            RETI
            .ORG  SPMRaddr    ; Store Program Memory Ready
            RETI
             
            .ORG  INT_VECTORS_SIZE
            ;======================================
            write2uart:
            in   R31, SREG ;storing state
             
            in   R16, portB ;reading from portB
            UART_SNT: sbis UCSRA,UDRE ;ждём готовности - флага UDRE
            rjmp UART_SNT
            out  UDR, R16 ;шлём байт!
             
            out SREG, R31
            reti
            ;======================================
            read_from_uart:
            in   R31, SREG ;storing state
             
            uart_rcv: sbis UCSRA, RXC
            rjmp UART_RCV
            in R16, UDR
            out portA, R16
             
            out SREG, R31
            reti
            ;======================================
            ; инициализация памяти
            RAM_Flush:
            ldi  ZL,Low(SRAM_START)  ; Адрес начала ОЗУ в индекс
            ldi  ZH,High(SRAM_START)
            clr  R16      ; Очищаем R16
            Flush:
            st   Z+,R16      ; Сохраняем 0 в ячейку памяти
            cpi  ZH,High(RAMEND)    ; Достигли конца оперативки?
            brne  Flush      ; Нет? Крутимся дальше!
            cpi  ZL,Low(RAMEND)    ; А младший байт достиг конца?
            brne  Flush
            clr  ZL      ; Очищаем индекс
            clr  ZH
             
            ;начальные настройки
            ;настройки UART
             
            BEGIN:
            ;UART setup
            ldi  R16, 0x0C
            out  UBRRL,R16
            ldi  R16, 0
            out  UBRRH,R16
            ldi  R16,2
            out  UCSRA, R16
            ldi  R16,(1&lt;&lt;RXEN)|(1&lt;&lt;TXEN)|(1&lt;&lt;RXCIE)|(0&lt;&lt;TXCIE)
            out  UCSRB, R16
            ldi  R16, (1&lt;&lt;URSEL)|(1&lt;&lt;UCSZ0)|(1&lt;&lt;UCSZ1)
            out  UCSRC, R16
             
            ;portA &amp; portB setup
            ldi    R16, 0xFF ;A - output
            out    DDRA,R16
            ldi    R16, 0x00 ; B - input
            out    DDRB,R16
            ldi    R16, 0xFF ; enabling pull-ups on B
            out    PORTB, R16
             
            ;configuring interrupts
            ldi  R16,(0&lt;&lt;ISC00)|(0&lt;&lt;ISC01) ;interrupt by low level
            out  MCUCR,R16
            ldi  R16,(1&lt;&lt;INT0)          ;INT.Enable=1 Разрешаем прерывание INT0
            out  GICR,R16
            sei  ;enabling all interrupts
             
            ;==================================
            waiting: rjmp waiting ; awaiting interrupts
            ;==================================
            nop
            1. Вроде нигде ничего бросающегося в глаза не вижу. Кроме того:

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

              Попробуй поставить после отправки в UART тупнячок, конструкцию вида

              ;Debug
              CLI
              Label: RJMP Label

              На ней проц встанет и дальше не пойдет. А ты будешь точно знать правильно у тебя шлется из порта в UART или нет и поганит уже дальше. Ну и ресетом несколько раз проверь сработку.

              Хинт: Отправку в уарт, имхо, лучше сделать по прерыванию на отправку, т.е. как только один байт отправился, прерывание на отправку инициализиуется снова и само в себя загружает еще один байт. Таким образом, у тебя и туда и обратно байты будут слаться непрерывно.

              Обработчик будет вида:

              1
              2
              3
              4
              5
              
              in   R31, SREG ;storing state
              in   R16, portB ;reading from portB
              out  UDR, R16 ;шлём байт!
              out SREG, R31
              reti

              Проверять готовность уже не придется — т.к. прерывание вызывается по готовности.

              Можно сделать еще умней, фиксировать только изменения. Делаешь прерывание от таймера. И помнишь состояние порта в прошлой итерации. В обработчике прерывания сравниваешь с прошлым значением. Если оно изменилось — загружаешь в уарта. Если подобрать длительность опроса так, чтобы он был медленней уарта, то тоже проверка флага не потребуется.

  23. Всем привет! Уперся в проблему — Мега16, байты принимаю по прерыванию и отправляю, все ок. Не могу добиться стабильной работы счетчика байтов. Через Terminal v1.9b посылаю 12345678 (программа на каждый посланный байт должна возвращать его порядковый номер) После включения и первой посылке должна вернуть
    01 02 03 04 05 06 07 08
    после второй
    09 0A 0B 0C 0D 0E 0F 10 и т.д.
    Проблема в том, что работает это очень нестабильно — вот типичный ответ на отправку 2х посылок по 8 байт:
    01 02 03 04 05 06 07 08
    09 0A 0B 0C 01 02 83 01
    или так
    01 02 03 04 82 81 01 02
    03 04 05 06 07 08 09 0A

    Т.е. оно как бы пытается работать правильно, но что-то мешеет.
    Привожу код:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    
    .nolist
    .include "m16def.inc";
    .list
    .equ PUART = PortD	;Выходной порт для последовательного интерфейса 
    .equ DUART = DDRD	;Регистр направления передачи данных
    .equ TxD = PD1		;Выход передатчика – разряд 1 порта D 
     
    .equ LED2 = PB2		;Светодиод2 – разряд 2 порта B 
    .equ LED3 = PB3		;Светодиод3 – разряд 3 порта B 			
    .equ Bd96 = 47		;UBRR для 9600 бод при 7.3728 МГц
    .equ USART_RXC_ADR=$016
     
    ;***** Регистры-переменные
    .def t0 = r16		;Временный рабочий регистр 0
    .def t1 = r17		;Временный рабочий регистр 1
    .def t2 = r24		;Временный рабочий регистр 2
    .def t4 = r22		;Временный рабочий регистр 4
    .def byte_res = r18	;регистр где хранится байт, принятый через com порт
    .def FLAG = r19		;Различные флаги
    .def byte_count = r30	;счетчик байтов
     
    .cseg	
    .org 0
     
    ;****************** Таблица векторов прерывания 
    jmp RESET 			;1 $000 External Pin, Power-on Reset, Brown-out Reset, Watchdog Reset, and JTAG AVR Reset
    reti; rjmp INT0 		;2 $002 External Interrupt Request 0
    reti; rjmp INT1 		;3 $004 External Interrupt Request 1
    reti; rjmp TIMER2_COMP 		;4 $006 Timer/Counter2 Compare Match
    reti; rjmp TIMER2_OVF 		;5 $008 Timer/Counter2 Overflow
    reti; rjmp TIMER1_CAPT 		;6 $00A Timer/Counter1 Capture Event
    reti; rjmp TIMER1_COMPA 	;7 $00C Timer/Counter1 Compare Match A
    reti; rjmp TIMER1_COMPB		;8 $00E Timer/Counter1 Compare Match B
    reti; rjmp TIMER1_OVF		;9 $010 Timer/Counter1 Overflow
    reti; rjmp TIMER0_OVF 		;10 $012 Timer/Counter0 Overflow
    reti; rjmp SPI_STC 		;11 $014 Serial Transfer Complete
     
    .org USART_RXC_ADR
    jmp USART_RXC
     
    reti; rjmp USART_UDRE 		;13 $018 USART Data Register Empty
    reti; rjmp USART_TXC 		;14 $01A USART, Tx Complete
    reti; rjmp ADCC  		;15 $01C ADC Conversion Complete
    reti; rjmp EE_RDY 		;16 $01E EEPROM Ready
    reti; rjmp ANA_COMP 		;17 $020 Analog Comparator
    reti; rjmp TWI 			;18 $022 Two-wire Serial Interface
    reti; rjmp INT2 		;19 $024 External Interrupt Request 2
    reti; rjmp TIMER0_COMP 		;20 $026 Timer/Counter0 Compare Match
    reti; rjmp SPM_RDY 		;21 $028 Store Program Memory Ready
    .org $40
     
    RESET:
    ldi t1,Low(RAMEND)	
    out SPL,t1		;Инициализируем младший байт указателя стека
    ldi t1,High(RAMEND)	
    out SPH,t1		;Инициализируем старший байт указателя стека
    ldi t1,1<<TxD		;Конфигурируем направление передачи данных 
    out DDRD,t1		;через порт D (разряд TxD порта D работает на передачу)
    ldi byte_count, $00	;Устанавливаем счетчик принятых байтов в 0
    InitUSART:		;Настраиваем USART 
    ldi t1,High(Bd96)	;Старший байт для скорости передачи данных 9600 бод
    ldi t0,Low(Bd96)	;Младший байт для скорости передачи данных 9600 бод
    out UBRRH,t1		;Устанавливаем скорость передачи данных
    out UBRRL,t0			
    ldi t1,(1<<RXEN)|(1<<TXEN);Включаем передатчик и приемник
    out UCSRB,t1		;Устанавливаем формат передачи: 8 бит данных, 1 стоп-бит
    ldi t1,(1<<URSEL)|(3<<UCSZ0)
    out UCSRC,t1
    cbr Flag,1<<Run		
    SBI UCSRB, UDRIE
    SBI UCSRB, RXEN
    sbi	UCSRB,RXCIE
    sei	
     
     
    MAIN:   ; Основная программа 
    ; пустой замкнутый цикл :)
    rjmp MAIN
     
     
    ;********* Обработка прерывания по событию "ПРИЕМ БАЙТА" через USART 
    USART_RXC:						
    in byte_res,UDR		;Сохраняем полученный символ (Чтение порта)
    cbi UCSRB, RXCIE	;Запрещаем прерывание
    inc byte_count		; Увеличиваем на 1 счетчик принятых байтов
    MOV	t0,byte_count	;Отладка
    rcall SendByte		;Отладка
    SBI UCSRB, RXCIE
    SBI UCSRB, UDRIE	; Разрешаем прерывание
    reti
     
     
    SendByte:			;Передача символа на ПК
    sbis UCSRA,UDRE		;Пропустить следующую команду если бит в порту установлен
    rjmp SendByte		;Ожидаем освобождения буфера передачи
    out UDR,t0			;Передаем символ
    ret

    Если есть у кого идеи — буду очень признателен

    1. Смотри как такое дерьмо вычислять.

      У тебя случайная плавающая бага — такое в 90% случаев изза срыва стэка, 5% вачдог, остальные — кривой алгоритм. Но тут алгоритм прост, поэтому думаем дальше.

      У тебя счетчик Т0 задает счет байтов. Он же R16. Запоганиться в процедуре и прерывании он не может, он там не используется. Только для счета. А где он активно используется? В инициализаци!!! То есть у тебя почему то самопроизвольно сбрасывается МК. Если бы это было прерывание, то характер бы был более периодически. А тут случайная фигня какая то.

      Поставь сброс вачдога в главный цикл и в начало каждого прерывания (команда WDR)
      у тебя ресет подтянут резистором на 10кОм к +5? если нет — подтяни, без этого мк будет перегружаться как ему вздумается.

      Далее, разные мелочи.
      У тебя какая то запутанная и длинная инициализация уарта. Упрости, у меня например позырь как делается. Там реально в четыре команды все.

      Не нужно выставлять направление в DDR на ноги UART. Это все будет сделано автоматически.

      Не обязательно запрещать прерывание в обработчике прерывания. При вызове обработчика автоматически, аппратно идет cli а по выходу аппаратно же sei.

  24. Сенкс, сделал, как ты говоришь —
    1. припаял 10к на ресет прямо на микросхему (у меня на STK500 теститься)
    2. инициализацию твою скопипастил
    3. WDR вставил и в главный цикл и в обработчик прерывания
    4. убрал команды запрета/разрешения прерываний в обработчике прерывания

    Пока эффекта нет — вот что имею
    81 01 02 03 04 05 06 07
    08 09 0A 0B 0C 8D 01 02
    03 04 05 06 07 08 09 0A
    01 02 03 01 02 03 01 82

    1. Да, копипастить инит уарта это черевато. Надо не копипастить, а делать по аналогии. Дело в том, что в разных моделях мег он чуток отличается (бит урселект то есть то нет) и тупой копипаст работает далеко не всегда :)

  25. C уартом согласен, но видно обошлось — работает.
    Продолжаю разбираться — пробовал вместо автоинкрементного счетчика отдавать на выход фиксированное число, типа
    ldi byte_count, $AA
    MOV t0,byte_count
    rcall SendByte
    Та же фигня — иногда вместо AA возвращает 82 или еще что-нибудь.
    Начал было грешить на шнур USB-COM, но вспомнил, что при передаче значения счетчика, он после сбоя как бы начинает считать заново, т.е. нарушается порядок счета — значит ошибка происходит до передачи — еще в контроллере. Но где именно?

  26. Еще один эксперемент — Возвращаю то же, что и принимаю (эхо)
    Если делаю через прерывание, как выше, то имею ту же проблему — регулярно байт искажается. Если делаю без прерываний в главном цикле, как показано ниже — то все ок, никаких сбоев.

    MAIN:
    WDR
    rcall GetByte ;Принимаем символ в t2
    rcall Echo ;Возвращаем символ на ПК
    rjmp MAIN

    GetByte: ;Прием символа
    sbis UCSRA,RXC ;Ожидаем ASCII-символ от ПК (Пропустить следующую команду если бит в порту установлен)
    rjmp GetByte ;Переход в начало цикла ожидания
    in byte_res,UDR ;Сохраняем полученный символ (Чтение порта)
    ret

    SendByte: ;Передача символа на ПК
    sbis UCSRA,UDRE ;Пропустить следующую команду если бит в порту установлен
    rjmp SendByte ;Ожидаем освобождения буфера передачи
    out UDR,t0 ;Передаем символ
    ret

    Echo: ;Передача сохраненного символа обратно на ПК
    mov t0,byte_res ;Копируем символ в t0
    rcall SendByte ;Передаем символ
    ret

    Похоже, что это какие то тонкости работы прерываний

  27. Да, кстати вопрос уже идеологический — а как правильней работать с УАРТом — через прерывания или в цикле. Если верить книгам, то через прерывания нужно делать максимум и в то же время в тех же книгах примеры с УАРТом все, как один идут через цикл.

    1. Вообще исходя из текущей задачи. Как эффективней так и делать. В реалтаймовой системе тупить на уарте нельзя, там на прерываниях все. Если же пофигу, то в цикле можно сделать. Я делаю под настроение, обычно в прерываниях.

  28. Все, разобрался, мораль такая — если принимаешь в УАРТ по прерыванию, то и выдавать в него нуна по прерыванию (UDRE). Смешанные варианты вот так странно глючат.
    Спасибо DI HALT! :)

  29. Обычно, если байты поштучно или в цикле поступают реже, чем время передачи байта, прерывания не нужны. Если же быстрее или сразу пачками, — тогда по прерываниям. Также и по приему: если цикл опрашивает приемник через интервалы меньше времени передачи байта — прерывания от UART не нужны.
    Например, в моем роботе есть прерывание 1мс, от которого вертится куча программных таймеров. При 9600 бод байт передается за время чуть больше 1 мс, поэтому я в прерывание 1мс вставил также проверку готовности приемника и передатчика UART. (Наличие принятого байта или готовности буфера передатчика принять следующий). При одноранговой системе прерываний проще в одно прерывание заложить кучу проверок, чем разбираться с их источниками. В приоритетной многоранговой — дело другое… Хорошая была штука.

  30. Уважаемый DI HALT,
    вновь обращаюсь к Вам за помощью.
    Теперь по поводу UART.
    На плате, которая попала ко мне, стоит AT90CAN128 и MAX232A.
    Кварц на 3,6864м.

    Пытаюсь принимать и передавать данные по USART1. Но передача то идет, однако полная чушь. Какое бы значение я не передавал, всегда приходит хрень, за исключением $FF — это значение всегда приходит верно.

    Кварц специально выбран по USART…
    Проверял MAX232A — как у Вас указано, все ОК.

    Помогите советом, что может быть?

    Вот инициализация:

    // USART1 initialization
    // Communication Parameters: 8 Data, 1 Stop, No Parity
    // USART1 Receiver: On
    // USART1 Transmitter: On
    // USART1 Mode: Asynchronous
    // USART1 Baud Rate: 9600
    UCSR1A=0x00;
    UCSR1B=0x18;
    UCSR1C=0x06;
    UBRR1H=0x00;
    UBRR1L=0x17;
    
    void send(char p)
    {UDR1 = p;
    }

    Заранее благодарен.

      1. Да, пробовал передавать FF потом другие — то же самое.
        Интересная вещь: у меня MAX232 стоит рядом с МК на плате, так вот если я соединяю оба сигнальных вывода MAX, то через терминал все отправляется и принимается правильно (ноги МК при этом я отключал).
        В то же время, если, восстановив все соединения после указанного выше эксперимента, и закоротив сигнальные провода на разъеме, который идет в РС, то МК передает и принимает все прекрасно. В смысле что передал, то и получил.

        Как только соединяю МК и РС, так полная хрень. Может какая-нибудь кодировка? Кстати в терминалке я могу видеть эту жуть только в НЕХ формате. При переключении в любой другой ничего нет.

        1. Проверь установки (длину Стоп-бита, наличие бита паритета, количество бит в передаваемом и принимаемом символе — может быть 5, 7, 8, скорость на обоих концах). Посмотри осциллографом длину битовой посылки на обоих концах. Может, неправильно посчитал коэффициенты деления. Проверь частоту кварцевого генератора контроллера — может, кварц возбудился на гармонике или вообще нихрена не стабилизирует.

          1. Спасибо за совет.
            Да, действительно, CKSEL фузы стояли совсем не так, как надо по даташиту при моем кварце. Переделал и все заработало правильно.
            Спасибо всем.

  31. При работе с COM портом была необходимость организовать счетчик принятых байтов. При приеме очередного байта устанавливался флаг разрешения прерывания счетчика T0

    ldi t1,0b00000001
    out TIMSK,t1

    Предварительно в регистрах этого таймера устанавливался коэф. деления и начальное значение счетчика (соразмерно скорости обмена). Если до прихода нового байта срабатывает прерывание по переполнению таймера, то принятый байт идентифицируется, как относящийся к следующему кадру и счетчик байтов соответственно обнуляется.
    Так вот обнаружилась такая хрень — установка этого флага автоматом вызывакт прерывание и как следствие потерю первого байта.
    Это можно обойти если вместо флага разрешения прерывания в регистре TIMSK использовать регистр TCCR0

    ldi t1, 0b00000101 ; Устанавливаем коэф. деления на входе таймера T0 — 1024
    out TCCR0,t1

    ldi t1, 0b00000000 ; Запрещаем счет
    out TCCR0,t1

    При этом все работает как надо. Но возникает вопрос — А нахера вообще этот бит в регистре TIMSK? И что еще он такого делает, о чем забыли написать в доках?

    1. Ну во первых, прерывание ты просто разрешаешь, генерироваться оно сразу же не должно. Если сгенерилось, значит есть переполнение (в чем отлаживаешь? В Какой среде?)

      Как можно обойти TIMSK через TCCR0??? это вообще из разной оперы. ТИМСК — это вкл/выкл прерываний а ТССР- прескалер таймера.

      С какой радости
      ldi t1, 0b00000101
      out TCCR0,t1
      Запрещает счет?

      Запретить счет можно так.
      ldi t1,0
      out TCCR0,t1

      Если ты хочешь отрезать пачки по таймауту. То тебе надо:
      Заинициализировать счетчик
      Разрешить прерывание таймера
      Запустить таймер на нужном предделителе.

      Ждать прерывание, которое тебе там что нибудь обнулит. Прерывание, если правильно задать начальное значение счетчика, сразу же не вскочит.

      Посре прерывания нужно заново загрузить в счетчик начальное значение
      сбросить прескалер

      и ждать следующего прерывания.

  32. to DI HALT
    Да, конечно это я в письме ошибся, на самом деле запрещал так
    ldi t1, 0b00000000
    out TCCR0,t1
    В счетчик таймера я загружал вполне вменяемые значения (например $01). Да я тоже считал что «прерывание ты просто разрешаешь, генерироваться оно сразу же не должно». Я просто стал запрещать прерывания при помощи другого управляющего регистра, если бы был какой нибудь баг с неправильной предустановкой счетчика или делителя и слишком ранним срабатыванием прерывания, то он бы проявился и в таком решении. Но в этом случае все работает нормально.

    1. Ну это как в том анекдоте получается — нет ножек нет мультиков. Разумеется когда счетчик стоит прерывания не будет.

      Скинь мне проект в студии, погляжу чо у тебя за фигня там с прерываниями творится. Любопытно.

  33. to DI HALT
    «Разумеется когда счетчик стоит прерывания не будет.» Ну да пришлось использовать такую фичу, т.к. прямым путем — откудать выпрыгивало это прерывание. Да, проект пришлю — почищу от хлама и пришлю.

  34. Плиз помогите начинающему.
    Канал таков: ATMega16-16PI — ST232BN — СОМ

    Сперва неработало передавало белиберду. Почитал выши коменты, скачал даташип Отредактировал UBRR. Начало мои байты передавать, но только первые 2. После передачи второго баата URDE спадает и больше не восстанавливается, а у меня передача следующего идет после проверки URDE.

  35. ПИШУ НОВИЧКАМ(как я).При передачи с МК AVR на комп к примеру буквы «G» — у меня тоже сперва неработало передавало белиберду .Ошибка моя была — не переключил на компе шрифт с «RU» на «EN».Сейчас самому смешно за убитые полдня, зато ДШ по UART наизусть выучил (хоть ночью разбуди-спроси -)).

  36. Прога обычная с Виндой XP которая идет, так в том-то и дело невнимательность: пошлеш букву и тут же пялиться на светодиоды(STK500). Смотриш что-то не то ,пошел код в AVRstudio смотреть,после ДШ изучать,потом на форумах аналогичные вопросы смотреть,а уж в последнюю очередь(как всегда,блин!)…..доперло….
    Терминал 1.9 пробовал — понравилось.Нутром чую есть у проги куча скрытых для меня возможностей ,но при скудном знании английского в хелпе непрочитанных.

    P.S(оффтоп): ДШ на русском для Attiny2313 это книга Белова «Миероконтроллеры AVR в радиолюбительской практике»(моя настольная книга,чего и любому новичку желаю).

  37. У меня стоит задача организовать обмен компа и МК с квитированием, то есть комп после отправки байта должен дождаться квитанции от МК. С МК разберусь, а вот можно ли использовать HyperTerminal для работы в этом режиме и как использовать — не могу понять.Или может кто подскажет другую доступную прогу для передачи файлов с квитированием?

  38. Добрый день. ПОМОГИТЕ!
    Мне необходимо прочитать 80 байт с софтового юарта и передать их в железный юарт.
    Они читаются , но первые 2-3 байта всегда кривые. Остальные все нормальные.
    Проверял осцилом, на входе в пик, все байты правильные.
    В чем может быть ошибка??
    вот код:
    include
    #fuses INTRC,NOBROWNOUT,NOWDT,NOMCLR
    #use delay (clock=4000000)
    #use rs232(baud=9600, rcv=PIN_B3,stream=SOFTUART,INVERT,TIMEOUT=5)
    #use rs232(baud=9600, xmit=PIN_B2,rcv=PIN_B1,stream=HARDUART,TIMEOUT=5)

    #INT_CCP1
    void ccp(void)
    { for(i=0;i<80;i++)
    {
    f=fgetc(SOFTUART); // читаем с софтового юарта
    //CAN[i]=f;
    fputc(f,HARDUSART); // кладем в железный юарт
    }
    clear_interrupt(INT_CCP1);
    }

    main()
    {
    enable_interrupts(INT_RDA);
    enable_interrupts(INT_CCP1);
    setup_ccp1 (CCP_CAPTURE_RE);

    }

    ВОт собственно и все.
    Очень буду благодарен за помощь!!!

    1. Из этого кода непонятно ничего. Как работает этот софт уарт непонятно. ВОзможно первые два байта кривые изза того, что софтовый уарт не может ухватить синхронизацию четкую. Попробуй вначале передавать FF два раза.

      1. Вобщем есть устройсто , которое уже нельзя перепрошить и т.д.
        С RS232 этого устройства исходит ровно 80 байт каждую секунду.

        Мне эти 80 байт надо отправить смской.
        Для этого железный юарт пика смотри на модем.
        А софтовый юарт пика использую для приема, тех самых 80 байт с внешнего устройства.
        Использую для этого прерывания.
        При попадании в прерывание, программа считывает 80 байт и заполняет ими массив.
        А дальше передаю их модему (т.е. кладу в железный юарт). И все.

        Проблема: искажает первые байты… не могу решить.

        1. Сделай тогда наоборот — прием аппаратным УАРТОМ, а передача софтовым. Передвать куда проще чем принимать. Пусть принимает специально обученная железка, нежели куско чьего то кривого кода.

  39. Очень правильная мысль!!! Но к сожалению применить нельзя. Так как устройства законченные. Т.е. доработать их нельзя.
    Все что можно сделать — это перепрограммировать ПИК. :(

    Теперь о наблюдениях:
    Припаял кварц 12мгц на ножки пика, указал в директиве частоту 12мгц и использование внешнего генератора — ВСЕ РАБОТАЕТ!!!! И больше не искажает первые символы. НИКОГДА!!!
    НО ВОТ НЕЗАДАЧА — НЕЛЬЗЯ допаивать дополнительные радиоэлементы (кварц с емкостями например).
    Задача состоит в решении проблемы именно программным путем.

    ПОКА НЕ МОГУ ДОБИТЬСЯ ТАКОГО РЕЗУЛЬТАТА!!! Кто хорошо владеет компилятором ccs и знает микроконтроллеры.. помогите пожалуйста

  40. А как это сделать? Я новичек в программировании.
    В текущий момент, оптимизировал код по времени исполнения… уже искажает стабильно один символ (раньше стабильно 3 ,редко 2).
    Более лучших результатов добиться не смог.

    1. Ну смотри. У тебя есть программа которая на 12мгц работает нормально. А на тех что есть плохо. Это естественно, т.к. уарт асинхронный и все тайминги там ЖЕСТКО ЗАВЯЗАНЫ на тактовую частоту. Соответственно в коде у тебя (или у того кто делал библиотеку) просто неправильно расчитанные временные интервалы для считывания битов при приеме. Может ты где нибудь частоту неправильно в дефайнах указал и тайминги неправильно посчитались, может где то частота отдельно задается и там стоит 12мгц, а не та что есть. В общем, разберись как ведет себя софтовый уарт, откуда он берет тайминги. Для новичка задачка сложновата :) Тем более если дальше стандартных библиотек не лазал.

  41. Незнал куда написать, но т.к. все началось с UARTа, то решил написать сюда.
    UART ATmega16 через FT232RL соеденил с USB. Проверил (терминальными прогами). Все работает с обоими видами драйверов (VCP, D2xx). Но вот столкнулся с проблемой написания программы со стороны компа. FTDI с D2xx дали библиотеку работы с их микросхемой, да все работает (принимает, отправляет), но куда засунуть эти процедуры, т.е. например как у UARTа закинул упровление на прерывание, а у USB чет я такого немогу найти. Подскажите как организовать теневой опрос моего USB устройства?

    P.S. сформулировал проблему как смог(

        1. Вообще — то стандарт RS232 предусматривает скорости максимум 56000 бод. Все остальное — это уже не RS232, хоть некоторые производители в последние годы и делали COM — порты на 115000 и даже 256000 бод. Для больших скоростей есть другие протоколы. Для мегабит и кабель другой нужен, и порты, и прочее, и прочее. Нужны мегабиты — используй USB, ITHERNET, TCP/IP, SFX/SPX и прочее… Не нужно ездить по дороге на реактивном самолете, даже если он у тебя есть. Дорога не для того создавалась.

  42. написал программку что контроллер шлет постоянно 0х42, типа звездочку ‘*’. чаще всего доходит правильно, но иногда начинает идти бред в перемешку со звездами, а потом и вовсе останавливается, видимо идет уже такой бред что terminal не понимает совсем. Это почему может быть? наводки?

  43. подскажите можно ли в процессе выполнения программы на микроконтроллере поменять скорость работы USARTa. просто изменение значений UBRRH:UBRRL непомогает, хотя может я некоренктно их меняю, вот код:

    #asm(«cli»)
    UBRRL=0xA0;
    UBRRH=0x01;
    UCSRB=0b00111000;
    #asm(«sei»)

    после сброса микроконтроллера USART работает на скорости 9600. меняю скорость на 4800 и разрешаю прерывания для DRE

  44. Что-то и у меня бред какой-то получается. Правда я на Си пробывал. Если выводить какую-нибудь строку из флеш в цикле, то она идет пока есть питание. Если же значение с АЦП преобразованную в строку, а потом ее через функцию puts() и также в цикле пускать п порт, то сначала идет нормально, а потом вывод останавливается и в УАРТ шлется только символ 0A, наверное здесь проц подвисает. Другой вариант моей проги сначала то что надо, потом почему-то нули, потом опять то что нано и опять нули и т.д. Вывод в УАРТ идет не через прерывание, а через конторль освобождения регистра UDR. По скоростям все согласовано.

    1. Чо я могу посоветовать. Вскрывай ассемблерный листинг и зырь что у тебя там происходит. Сразу поймешь что там нагородило.

      Да, попробуй еще оптимизацию отрубить.

  45. Здравствуйте DI HALT.
    Помогите пожалуйста.
    Схемка на ATMega8535, он отправляет команду телефону, а телефон молчит.
    Подключал к компу (схемку) работает, телефон с компом тоже общается (правда только на 19 200). Думаю что дело в скорости.
    Почитал на форумах, говорят что кварц надо ставить на 7,3728 Мг. Может можно без него?
    Если да, то как?
    Если нет, то скажите пожалуйста, что нужно в коде изменить, что бы контроллер от кварца работал?

    \\\\\\\\\\\\\\\\\\\\\\\\\
    uart_init:;
    .equ XTAL = 8000000; частота процессора.
    .equ baudrate = 19200
    .equ bauddivider = XTAL/(16*baudrate)-1
    outi UBRRL , low(bauddivider)
    outi UBRRH , high(bauddivider)
    outi UCSRA , 0
    outi UCSRC , 1<<URSEL|3<<UCSZ0
    outi UCSRB , 1<<RXEN|1<<TXEN|1<<RXCIE|1<<TXCIE
    \\\\\\\\\\\\\\\\\

    1. А к компу, к КОМ порту подключать не пробовал? А то может у тебя шлется все нормально, а просто ты с командами накосячил.

      Во вторых с какой точно частотой у тебя работает Мега? Точно с 8мгц? Уточни в даташите на мегу на какой частоте она работает по дефолту (вроде бы там 1мгц, но я не уверен)

      В третьих, программно ты МК на кварц не переключишь, надо через программатор менять Fuse биты. Если не знаешь что такое, то лучше не лезь — ошибешься и МК В помойку. Почитай сначала даташиты и мануалы. У меня все в Учебном курсе АВР есть.

      Кварц на 7.37 ставят когда хотят получить очень точный UART на высоких скоростях, с минимумом ошибок. Тогда да, обычно на внутреннем все работает. Я сколько с АВР работал — ни разу еще не приходилось юзать внешний кварц.

      1. Телефон подключал к компу, все работает (только на 19 200).
        Если в МК ставлю 8 Мг, и скорость 19200 то в комп приходит на скорости 2400,
        а если ставлю 4Мг то прихадит на скорости 4800. Так и должно быть?

        Пробовал ставить 1Мг и 9600. В комп приходит как раз на 9600, но когда поменял на 19200 (при 1Мг) то приходят ироглифы всякие разные(. На любой скорости приема.
        Как мне быть?

        Частоту по дефолту обязательно посмотрую.

        ЗЫ. Огромное спасибо за ответ!

        1. Да, там еще бит есть U2X он удваивает скорость обмена. Я обычно его не юзаю. Если ты взял мою инициализацию, то его там тоже нет (и не надо)

          По дефолту (если фузы не менять) скорость Мега8535 — 1Мгц.

          Так что:
          .equ XTAL = 1000000
          .equ baudrate = 19200
          и все заработает

  46. Здравствуйте DI HALT. Это опять я:)
    Вот смотрите, кусочек кода:
    .org 0x0000
    rjmp Reset ; на метку Ресет
    ;сдесь должна быть табличка прерываний

    Reset: outi SPL,low(RAMEND) ; инициализация стека.
    outi SPH,high(RAMEND)
    rcall uart_init ; вызов подпрограммы инициализации UART

    ;———-настройка портов

    CLI ; запрещаем прерывания,

    Main: WDR ; Пинаем собаку.
    ;————сдесь ожидаю, а если что не так, то на Alarm0
    RJMP ALARM0

    rjmp main

    ALARM0:
    ;——-сдесь идет передача по uart

    STOP0: WDR
    SBIC PINA,0
    RJMP STOP0 ;сдесь зацикливаемся,
    ret

    Так вот, при выполнении команды ret программа возвращается на метку reset, а там происходит инициалицация стека, мне интересно, а если у меня там данные какие были (в стеке), они удалятся?

    А вот если вместо ret поставить команду RJMP main, то после ее выполнения повторно отправляются данные по UART, почему?

    И еще скажите пожалуйста, где меньше погрешность при передаче по UART
    если я сделаю тактовый генератор (RC) приблизительно на 7,3Мг
    или если выставлю тактовую частоту контроллера 8Мг?

    С уважением, Михаил.

    1. Бегом учить матчасть! В процедуры уходить надо по RCALL и выходить по RET. А не по RJMP который просто переход. Соответственно у тебя срывает стек и проц перезагружается.

      А по поводу того, что у тебя после перехода обратно на Main идет повтор отправки по UART так ты где то с условием в «;————сдесь ожидаю, а если что не так, то на Alarm0″ что то накосячил, что оно у тебя пускает дальше.

  47. di_halt приветствую, вобщем трабл с кодесом контроллер АТмега16 с кварцем на 12 МГц и CKSEL3..0 = 1111 вобщем вроде бы на 12 МГц работает… проблема вот в чём — отправляю в контроллер 1 байт.. приходит 3 байта мусора, вот код:

    .equ fosc = 12000000
    .equ baudrate = 9600
    .equ ubrr= fosc/16/baudrate-1
    init_uart:
    ldi R16, low(ubrr)
    out UBRRL,R16
    ldi R16, high(ubrr)
    out UBRRH,R16
    ldi R16,0
    out UCSRA, R16
    ldi R16, (1<<RXCIE)|(1<<RXEN)|(1<<TXEN)
    out UCSRB, R16
    ldi R16, (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
    out UCSRC, R16
    ret

    … получаю данные так

    RXcomplete:
    in R16, UDR
    cli
    call out_data ; тут вывожу полученный байт через свою либу на ЖКИ — вобщем получаю 3 байта вместо одного
    sei
    reti

    ну на компе в проге разумеется выставляю 9600 / 8 / 1 в чём проблема никак не пойму =(

    1. out_data взываная вручную работает правильно? Шлет то что надо? Если нет, то разбирайся с бодрейтом и настройками уарта.

      По поводу прерывания RXcomplete
      Тут грубейшая ошибка

      1) ты входишь в прерывание и меняешь R16, мало того, ты вызываешь out_data которая тоже наверняка использует какие то регистры.
      Соотвественно, перед тем как что то менять в прерывании нужно все регистры которые меняют свое значение сохранить в стеке, а перед RETI достать их оттуда. Так что PUSH и POP тебе в помощь. Также не забуlь про SREG его тоже надо сохранить. Это связано с тем, что ты не знаешь где будет вызвано прерывание и не можешь предугадать ход какой части кода затронет изменение регистров.

      Ну и второе, при входе в прерывание не обязательно запрещать их командой cli — это происходит автоматически, аппаратно.

      1. благодарю за дельные советы, кстати out_data норм работает — она пока только приветствие выводит… проблема видимо действительно в настройках уарта — закоротил TX-RX и всё что отправляем — возвращается, в прерываниях не сохранял реги — да действительно прога может в любой момент прерваться для сношений с компом и логика основной проги будет нарушена — как-то не подумал, кстати насчёт частоты кварца для обмена по RS232 — я где то слышал там «специальные» кварцы какие то — так вот это актуально или на чистых 12.0 МГц тоже будет нормалёк?

        1. Спец кварцы нужны для очень высоких частот и когда шлешь на большие расстояния. В общем, там где критично. Обычно же на чем угодно, главное УББР правильно рассчитать.

  48. Привет DI HALT
    Есть такая задачка: необходимо принять два 4х значных двоичных числа, дополненных кодами возврата каретки и перевода строки, через уарт, микропроцессор их сложит и вернет результат туда же через уарт. МК ATmega16 с кварцем 14.318 МГц
    Вот что я навоял, раскритикуй =)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    
    .INCLUDE "m16def.inc"
    .equ 	XTAL = 14318000 	
    		.equ 	baudrate = 9600  
    		.equ 	bauddivider = XTAL/(16*baudrate)-1
    .MACRO putc
    ldi R16,@0   
    rcall uart_snt ; вызываем подпрограмму передачи данных по UART.
    .ENDMACRO 
     
     
    .org 0
    rjmp reset_handler
    .org URXCaddr 
    rjmp uart_rcvd ;принимать данные будем по прерыванию
     
     
     
    reset_handler: 
     
    ldi r16,high(RAMEND) ; инициализация стека
    out SPH,r16
    ldi r16,low(RAMEND)
    out SPL,r16
    sei
     
    LDI R16, low(bauddivider) ;инициализация уарта
    OUT UBRRL,R16
    LDI R16, high(bauddivider)
    OUT UBRRH,R16		
    ;LDI R16,0
    ;OUT UCSRA,R16		
    LDI R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(0<<TXCIE)		
    OUT UCSRB, R16		
    LDI R16,(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
    OUT UCSRC,R16
     
    putc 'H'  ;Приветствие, мол коннект успешен и усарт работает
    putc 'e'
    putc 'l'
    putc 'l'
    putc 'o'
    putc ','
    putc ' '
    putc 'm'
    putc 'y'
    putc ' '
    putc 'n'
    putc 'a'
    putc 'm'
    putc 'e'
    putc ' '
    putc 'i'
    putc 's'
    putc ' '
    putc 'A'
    putc 'T'
    putc 'm'
    putc 'e'
    putc 'g'
    putc 'a'
    putc '1'
    putc '6'
    putc '!'
    putc 0x0D
    putc 0x0A
     
    ldi r16,0 ;сброс рабочих регистров, а то малоли
    ldi r17,0
    ldi r18,0
    ldi r19,0
    ldi r20,0
     
    start:  ;зацикливаем на распознание первого числа, число должно прийти двоичным и дополнено возвратом каретки и переводом строки (!!!)
    cpi r16,48 ; это нолик
    BREQ label1
     
    cpi r16,49 ; это единичка
    BREQ label2
     
    cpi r16,13 ;а вдруг возврат каретки? =)
    BREQ label3
     
    cpi r16,10 ;a вдруг перевод строки? =)
    breq label4
     
    jmp start
     
    loop:
    ldi r16,150; это нужно чтоб цифирка, пришедшая не так давно, не определялась много миллионов раз =)
    jmp start
     
     
     
    label1:
    ;putc 'a'
    lsl r17  ; r17 будет накапливать двоичное число, если нолик пришел, то просто сдвиг влево
    jmp loop
     
    label2:
    ;putc 'b'
    inc r17   ; а если единичка, то увеличим на 1 и сдвинем влево
    lsl r17
    jmp loop
     
    label3:
    ;putc 'c'
    ldi r18,1 ; r18 будет в качестве переключателя, т.к. как в условии было, что сперва идет возврат каретки, потом переход
    jmp loop
     
    label4:
    cpi r18,1 ; ура нам повезло, каретка уже вернулась, да еще и переход строки подоспел, а это значит, что мк получил первое число
    BREQ start2 ; можно идти за вторым
    ;putc 'e'
    jmp loop
     
    start2:  ;тут уже всё зациклино на получение второго числа, все так же как и в первом
    cpi r16,48 ; это нолик
    BREQ label21
     
    cpi r16,49 ; это единичка
    BREQ label22
     
    cpi r16,13 ;а вдруг возврат каретки? =)
    BREQ label23
     
    cpi r16,10 ;a вдруг перевод строки? =)
    breq label24
     
    jmp start2
     
    loop2:
    ldi r16,150;
    jmp start2
     
     
     
    label21:  ;число собираем в r19
    lsl r19
    jmp loop2
     
    label22:
    inc r19
    lsl r19
    jmp loop2
     
    label23:
    ldi r20,1  ; регистр р20 будет переключающим
    jmp loop2
     
    label24:
    cpi r20,1
    BREQ finish ; ура у нас есть два числа можем переходит к их обработке
    jmp loop2
     
    finish:
    lsr r17  ; регистры сдвинулись влево из-за приема последней цифры надо поправить
    andi r17,0b00001111; так как число должно быть 4х значным, то убиваем лишние биты
     
    lsr r19 ; здесь тоже самое что и с r17
    andi r19,0b00001111;
     
    add r17,r19 ; складываем их
    mov r16,r17 ; и подготавливаем к отправке
     
    ldi r21,48  ; это опционально, перевод в систему ASCII перед отправкой
    add r16,r21 ;
     
     
    rcall uart_snt ;отправляем результат пользователю
     
    putc 10  ;чтоб покрасевей было переведем строку
    putc 13  ;
     
    ldi r16,0 ; всё сбрасываем и начинаем сначала
    ldi r17,0 ;
    ldi r18,0 ;
    ldi r19,0 ;
    ldi r20,0 ;
     
     
     
    jmp start
     
     
     
    uart_snt:
    sbis UCSRA,UDRE; Ждем пока бит UDRE в регистре UCSRA станет 1, что означет готовность УАРТА к передаче.
    rjmp uart_snt
    out UDR,R16 ; бросаем число из регистра 16 в регистр приемопередатчика.
    ret        
     
     
     
    uart_rcvd:
    in r16,udr
    reti
    1. почему вы в обработчике прерывания не хотите выполнять главный цикл, а делаете только прием одного байта?

      а в главном цикле например усыпить проц, что-бы не грелся.

      1. Ну во первых усыплять проц смысла тут нету. Он и так мало ест. А во вторых в обработчике надо делать быстро и сваливать оттуда. Обработчик должен быть коротким.

        1. @Обработчик должен быть коротким.@

          это я усвоил, просто интересно если например 4 байт прейдет и запишется в R16
          например когда выполняется строка

          80: cpi r16,13 ;а вдруг возврат каретки? =)

          может там и был возврат каретки но теперь там 1

          и программа в ауте?

          1. Для этого обработка символов в программе должна быть быстрей чем они поступают из интерфейса. Если это невозможно, то юзать приемный буффер и в прерывании нажирать его, а потом медленно жевать в главном цикле

        2. Уважаемый Di Halt.Принимаю из МК на ПК 10 байт.Почему-то последние
          2 байта приходят искажёнными.Если повторно принимаю последние 4
          байта,то нужные 10 байт принимаются нормально,а последние 2 байта
          т.е.13 и14 снова искажённые.Объясни пожалуйста в чём дело.Заранее
          спасибо. Frolov/

  49. Вот возник такой вопрос, подскажите,пожалуйста, кто знает.

    Допустим при передаче по UART’у возникла ошибка четности/фрейма при этом возводятся флаги PE/FE. Они сбрасываются после чтения из UDR или их нужно вручную сбрасывать?

    UPD: Отмена. Нашел в даташите.

  50. И у меня вопрос подскажите пожалуйста существуют ли какие нибудь стандартные байты начала и конца посылки для uart?
    Например в протоколе rs485 признак начала 3A («двоеточие»), а признак конца 0D и 0A («СR» возврат каретки), а как правильно делать для uart?

  51. Привет)
    Мне хотелось бы реализовать Режим многопроцессорного обмена.Поскольку у меня много устройств работают на одной частоте, связаны между собой и их нельзя загружать парсингом того что пришло в уарт.Мне бы простой пример генерации байта адреса))
    в датащите всё это описанно вскользь.Думаю так : 1.пришёл байт адреса (байты данных идут лесом) проверяем со своим адресом. 2. если совпало ловим данные пришёл $FF ставим MPCM в еденицу и снова фильтруем данные тоесть прыг на 1.Контроллер ATTiny2313 но пример можно и на меге разберусь)
    Туплю в написании кода .Заранее СПС!

    1. > DI HALT июня 14, 2009 at 19:51
      > Для многопроцессорного режима куда лучше использовать I2C.
      > ТАм и адресация и разрешение коллизий.
      > У USART можно только на базе архитектуры Token Ring что то сделать.
      > Что медленно и неуклюже. В промышленности ЕМНИП на этом принципе работает RS485

      Ну если Мастер на линии один, то UART (в режиме 9-ти битовых посылок) наверное лучше всё-таки. И код попроще, и скорость побольше. И тем более, если планируется только передачи Мастер -> Слейв. Тут ещё и одним проводом меньше.

    2. Ну тогда гнать по вначале байт адреса, потом байты данных. Слейвы сидят и снифферят. Кто свой адрес узнал — обрабатывает. Остальные байты просто сливают в dev/null (надо чтобы не прозевать стоп). Как получено определенное количество байт — слушаем дальше. Данные само собой надо слать пакетами, чтобы все слейвы знали длинну пакета.

      1. > Остальные байты просто сливают в dev/null

        Дык остальные байты просто игнорируются аппаратно. В смысле прерывание не генерируется. Это ж специальный режим такой, когда 9-й бит является признаком адрес/данные. Multi-processor Communication Mode. Включается битиком MPCM в регистре UCSRA.

        > Данные само собой надо слать пакетами, чтобы все слейвы знали длинну пакета.

        И даже это не обязательно в простейшем случае.
        1. Ждём байт адреса.
        2. Если адрес наш, то все следующие байты наши до того момента, пока не будет получен адресный байт (9-й бит = 1).
        3. Если адрес не наш, то Goto 1.

        ps: Да кстати я не знаю почему назвал байт адресным, правильнее наверное назвать его служебным и кроме адреса содержать он может ещё много чего, например команду, общую для всех или признак конца передачи, …это уже от фантазии зависит.

  52. Здравствуйте уважаемый Di_Halt. У меня в контроллер по Uart (мега 8535) приходить строка символов, мне ее надо сравнить с константой. Никак не могу придумать, как это можно реализовать, подскажите пожалуйста!
    Ну или хотябы на мысль наведите:)

    1. А кстати, хороший вопрос. DI HALT, может в курсе опишешь какую-нить элегантную
      процедцрку (на асме естественно) по декодированию команд принятых УАРТом.
      На входе — принятая строка символов и таблица с командами во флеши, на выходе — код команды.
      Может там какие хитрые приёмы есть, а то чё-то как мозги не напрягал, всё как-то громоздко/топорно получается. (Даже на Си стал посматривать…)

        1. Думаю проще чем XOR тут ничего не придумать. Зажираешь строку в буффер. Дальше берешь и начинаешь этот буффер циклично XORить на ту строку с которой сравниваешь. Прям побайтно. Если в результате всей операции у тебя получился 0 значит строка та самаяи в бит в бит совпадает со сравниваемой.

  53. Да я понимаю что получится неуклюже но я начинающий поэтому I2C для меня тёмный лес.
    Куча устройств в доме работает по радиоканалу.Я это чтобы небыло мешанины в эфире.Никак не пойму как собрать до кучи байт с адресом там ведь первый стоп бит должен быть 1 а у данныйх как обычно 0.Вот тут я и туплю у меня постояно данный прут(( если не сложно покажите на асме как это сделать . Ещё раз спасибо

    1. Мешанина у тебя будет в любом случае. Тут либо применять модули с адресацией RC1240 например, либо делать адресацию самому. В любом случае должен быть тогда хотя бы опрос, т.е. мастер выдает адрес и сразу же команду, потом слушает. ВСе остальные слушают и если поймали свой адрес, то отвечают.

      1. Вот я и хочу замутить адресацию сам.. У нас в лабазах ничего приличного не продаётся(( Единвенное что нашёл на рынке какието китайские модули на 433MHz
        но работают они норм правда тупые ничего не умеют.Таких модулей я накупил много а теперь пытаюсь всё упорядочить. Вот и хочу применить такой режим а ума не хватает (( А «мешанина» это всмысле чтобы разные устройства друг дружку не пинали.

  54. DI_HALT, а вы не знаете сколько у меге 8535 логическая единица. Я вот телефон подключаю, на него данные нормально уходят а ответ (от телефона) не приходит.
    RX меги и TX телефона цепляю на прямую.
    Причем если с компа посылаю (через дата кабель от этого же телефона Simens C45) то все приходит

    1. На память не помню, в даташите погляди, что то около 2.5 вольт и выше (для 5 вольтового питания). По крайней мере 3.3 вольта с сименса 65го вполне нормально доходили.

      А ты мегу каким напряжением питаешь? Когда у меня было 5ти вольтовое питание, то я
      TX меги заводил на RX телефона через стабилитрон.
      А обратно просто напрямую

  55. Все, проблема решилась, только не понятно как)
    Возникла другая проблема.
    Если я сначало посылаю байты, например:

    putc ‘A’
    putc ‘D’
    putc ‘C’
    putc ‘D’
    putc ‘E’

    а потом сразу же начинаю принимать:

    in_com: ;прием байта в r16
    WDR
    sbis UCSRA,RXC
    rjmp in_com
    in R16,UDR

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

    putc-марос, который кладет байт в r16 и вызывает процедуру:

    uart_snt: sbis UCSRA,UDRE
    rjmp uart_snt
    out UDR,R16
    ret

    1. Прием байта надо делать по прерываниям. Т.е. активизируешь UART, разрешаешь прерывания. Потом от вектора прерывания по RxC делаешь RJMP на обработчик прерывания.

      В самом обработчике
      1) Сохраняешь регистры которые используешь в стек
      2) Сохраняешь в стек SREG
      3) Делаешь нужный экшн
      4) Достаешь из стека сохраненное
      5) RETI

      Примеров работы с прерываниями у меня было описано, в том числе и на RxC

      1. Мужики!!Помогите!!Работаю с STK-500 и STK-501 , на ATmega128.Настроил UART на приём , всё чётко принимает с Hiper Terminal.А на передачу не могу , передаёт ,но какую-то хрень,понимаю что похоже проблема в синхронизации по скорости , стоит кварц 16 мГц , UBRR=51 это 19200 и нифига((( что терминал хрень получает что если на второй ATmega128 посылаю.Зато в ISIS Proteus всё работает.Помогите очень нужно.

  56. Добавлю и я свои 5 копеек ;-)
    Хотелось подключить Atmega16 к компу по UART, max232 под руками не было, нашел в инете схему согласования на транзисторах.
    сайт http://www.qsl.net/pa3ckr/bascom%20and%20avr/rs232/index.html
    схема http://www.qsl.net/pa3ckr/bascom%20and%20avr/rs232/rs2324.gif
    собрал, закоротил, эхо-ответ идет, ну думаю «щас как все заработает».
    подключил к МК, нашел самую простую прогу в инете, скомпили ….. нифига
    по терминалу плзут ёжики
    стал читать про настройки, таблицы в даташите смотреть — ага, не те константы в настройках
    частота кварца у меня 14МГц, таблица 70, на стнарице 170, тааак, UBRR=95
    ёжики
    блин, короче что я только не читал уже, часов с 15 до 21 мучался — нифига
    все, задолбался
    пора завазывать
    у тут на последок мысль
    (я компилируюсь в CodeVisionAVR V2.03.4, там есть визард который сам делает начаьный код)
    запустил визард, частота процессора, как всегда, 14МГц, используем UART и туда и сюда, прерывания запретить, поехалиииииииии
    опа ??????? UBRR=5a
    запускаем, оооооооооооооооооооооооооооо !!!!
    почти, почти, чуть чуть срывает сигнал, ану UBRR=59
    запускаем, совсем сорвало
    UBRR=5B
    УРААААА, заработало (с) Кот Матроскин
    все стабильникько так…. Чудесно.
    ану посчитаем частоту?
    UBRR=FOSC/16/BAUD-1
    5B = ??? / 16 / 9600 — 1
    ??? = 14131200 Гц
    а визардами пользуються только «или аристократы или ….» — сами знаете, и тем не мение
    оказалось что кварц у меня не такой как в даташите, а я забыл

  57. Мужики!!Помогите!!Работаю с STK-500 и STK-501 , на ATmega128.Настроил UART на приём , всё чётко принимает с Hiper Terminal.А на передачу не могу , передаёт ,но какую-то хрень,понимаю что похоже проблема в синхронизации по скорости , стоит кварц 16 мГц , UBRR=51 это 19200 и нифига((( что терминал хрень получает что если на второй ATmega128 посылаю.Зато в ISIS Proteus всё работает.Помогите очень нужно!!

    1. А он точно то принимает? Т.к. если принимает то и передавать должен. Настройки то там едины.

      Кварц начинает влиять на качество только на скорости свыше 100кбит, там да, нужен специально заточеный кварц. На 19200 можно на чем угодно, хоть на RC — проблем быть не должно, не та скорость.

      Может у тебя уарт захлебывается или инициализация уарта в общем цикле крутится и он у тебя офигевает от постоянных перезагрузок контрольных регистров.

      1. Принимаю точно.Пишу число или букву в терминале отправляю и смотрю на лампочках STK этот символ в ascii коде всё совпадает.
        А вот мой код программы , без всяких циклов , просто передаю число и конец.
        Я разные скорости пробовал , уже голову сломал , не пойму в чём дело((

        .include «m128def.inc»

        INIT:
        ldi r16,0x31
        ldi r17,(1<<TXEN0)
        out UCSR0B,r17
        ldi r17,51
        out UBRR0L,r17
        ldi r17,(1<<UCSZ00)|(1<<UCSZ01)
        clr r26
        ldi r27,0x95
        st X,r17
        WAIT_START_TRANSMIT:
        sbis UCSR0A,UDRE0
        rjmp WAIT_START_TRANSMIT
        TRANSMIT:
        out UDR0,r16
        END: rjmp END

  58. Помагите пожалуйста разобраться, где то есть ошибка!!!
    Mega 2561. Пересылаю 2 байта (передатчик отправляет, приемник получает все ок!) но второй раз, по истечению времени, передача не идет причем пошагово (AVR Studio) все как бы работает

    ; инициализция переменных
    .def ST1 = r24 ; Флаги передатчика приемника(нулевого №0):

    .equ ST1_PAC_OK = 0 ; Флаг прием пакета ок
    .equ ST1_STABLE = 1 ; Флаг устойчивого соединения по ( … )
    .equ ST1_RXD_ERR = 2 ; Флаг ошибки приема пакета
    .equ ST1_TXD_ERR = 3 ; Флаг ошибка передачи пакета
    .equ ST1_RXD = 4 ; Флаг окончания приема данных
    .equ ST1_RXD_OK = 5 ; Флаг приема блока ОК
    .equ ST1_TXD = 6 ; Флаг окончания передачи данных
    .equ ST1_TXD_OK = 7 ; Флаг передачи блока ОК
    .def TMP_X = r16 ; буфер задержеки
    .def TMP_Y = r17 ; буфер при запуске конфигураций
    .def TMP_D = r18 ; буфер коротких процессов (прерыв) если нехват то как ТМР_4
    .def TMP_1 = r19 ; буфер данных не пересекается с конфигурацией
    .def TMP_2 = r20 ; буфер данных не пересекается с конфигурацией
    .def TMP_3 = r21 ; буфер данных не пересекается с конфигурацией
    ;————————————————————————————
    ; инициализация UART
    ldi TMP_Y,0xA0 ; загружаем во временный регистр младший байт
    sts UBRR1L, TMP_Y
    ldi TMP_Y,0x01 ; загружаем во временный регистр старший байт
    sts UBRR1H,TMP_Y
    ldi TMP_Y,0x00
    sts UCSR1A,TMP_Y
    ldi TMP_Y,0x88 ;(1<<RXEN1)|(0<<RXCIE1)|(1<<TXEN1)|(0<<TXCIE1)
    sts UCSR1B,TMP_Y
    ldi TMP_Y,0x86 ;(1<<UMSEL11)|(1<<UCSZ11)|(1<<UCSZ10)
    sts UCSR1C,TMP_Y

    sbr ST1,S_BIT4 ; прием ЗАПРЕЩЕН изначально

    ;………………………
    main:
    rcall REREDACHA
    rcall OUT_DATA
    rcall AUDIT_ANSWER
    rjmp main
    ;————————————————————
    REREDACHA:
    sbrs ST1, ST1_RXD
    rjmp EXIT_REREDACHA
    rjmp COMANDA_TXD ; п/п подготовки данных для передачи в ней шифруем данные кодом Мончестера и устанавливаем sbr ST1,S_BIT6
    EXIT_REREDACHA:
    ret
    ;————————————————————-
    //*************************************************************************************
    // Передача данных
    //*************************************************************************************
    OUT_DATA:
    sbrs ST1,ST1_TXD ; проверка оканчания передачи
    rjmp EXIT_OUT_DATA
    //———-ПРОВЕРКА КОЛИЧЕСТВА ПЕРЕДАЧ———————————————-
    REREDACHA_KOL:
    lds TMP_1,CountTXD
    cli
    cpi TMP_1,KOL_TXD
    brlo START_TXD_
    sei
    rjmp REREDACHA_KOL_OK

    START_TXD_:
    rjmp START_TXD

    //———-ПЕРЕДАЧА ОКОНЧЕНА—————————————————-
    REREDACHA_KOL_OK:
    //———РАЗРЕШАЕМ ПРИЕМ——————————————————
    cli
    ldi TMP_Y,0x90 ;(1<<RXEN1)|(1<<RXCIE1)|(0<<TXEN1)|(0<<TXCIE1)
    sts UCSR1B,TMP_Y
    sei
    //——————————————
    cbr ST1,S_BIT4
    cbr ST1,S_BIT6

    //———ЗАПУСКАЕМ ВРЕМЯ ДЛЯ ОЖИДАНИЯ ПОВТОРНОЙ ПЕРЕДАЧИ—————————————
    NUMBER_ZERO TimeANSWER_L, TimeANSWER_M, TimeANSWER_H
    MACROS_NUMBER T_N_ANSWER_L,T_N_ANSWER_M,T_N_ANSWER_H,TimeANSWER
    TRANSVER_NUMBER TimeANSWER_L, TimeANSWER_M, TimeANSWER_H
    rjmp EXIT_OUT_DATA
    //—————НАЧАЛО ПЕРЕДАЧИ—————————————————
    START_TXD:
    sei
    //—————ПЕРЕДАЧА ДАННЫХ—————————————————-
    UART_SNT:
    sei
    //————ЗАДЕРЖКА ВРЕМЕНИ НА ПЕРЕДАЧУ——————————————
    lds TMP_1,Time10ms
    cli
    cpi TMP_1,ZERO
    brne DELAY_10_
    sei
    rjmp UART_OUT_

    DELAY_10_:
    sei
    DELAY Time10ms_L, Time10ms_M, Time10ms_H,Time10ms
    rjmp EXIT_OUT_DATA

    UART_OUT_:
    rcall GO_10msec_
    lds TMP_3, CountBateTXD
    cli
    cpi TMP_3,ZERO
    breq ADR_5
    cpi TMP_3,1
    breq ADR_6
    cpi TMP_3,2
    breq ADR_7
    cpi TMP_3,4
    breq ADR_8
    sei
    clr TMP_3
    sts CountBateTXD, TMP_3
    rjmp EXIT_OUT_DATA

    //———————————————————————————
    ADR_5:
    sei
    ldi ZH,high(ADR_5_TXD) ; загрузка адреса
    ldi ZL,low(ADR_5_TXD) ; загружаем адрес
    ld TMP_2,Z ; увеличиваем значение адр на 1
    rjmp UART_OUT

    ADR_6:
    sei
    ldi ZH,high(ADR_6_TXD) ; загрузка адреса
    ldi ZL,low(ADR_6_TXD) ; загружаем адрес
    ld TMP_2,Z ; увеличиваем значение адр на 1
    rjmp UART_OUT

    ADR_7:
    sei
    ldi ZH,high(ADR_7_TXD) ; загрузка адреса
    ldi ZL,low(ADR_7_TXD) ; загружаем адрес
    ld TMP_2,Z ; увеличиваем значение адр на 1
    rjmp UART_OUT

    ADR_8:
    sei
    ldi ZH,high(ADR_8_TXD) ; загрузка адреса
    ldi ZL,low(ADR_8_TXD) ; загружаем адрес
    ld TMP_2,Z ; увеличиваем значение адр на 1
    rjmp UART_OUT
    //———————————————————————————
    UART_OUT:
    lds TMP_1,UCSR1A
    sbrs TMP_1,N_BIT5 ; UDRE1
    rjmp UART_OUT
    sts UDR1,TMP_2 ;
    lds TMP_3, CountBateTXD
    inc TMP_3 ; разрешаем передачу следующего байта
    sts CountBateTXD,TMP_3 ; сохранем содержимое регистра в переменной
    lds TMP_3, CountTXD
    inc TMP_3 ; разрешаем передачу следующего байта
    sts CountTXD,TMP_3 ; сохранем содержимое регистра в переменной

    rjmp EXIT_OUT_DATA
    //———————————————————————————
    GO_10msec_:
    NUMBER_ZERO Time10ms_L, Time10ms_M, Time10ms_H
    MACROS_NUMBER T_N_10ms_L,T_N_10ms_M,T_N_10ms_H,Time10ms
    TRANSVER_NUMBER Time10ms_L, Time10ms_M, Time10ms_H
    ret
    //———ВЫХОД————————————————————————
    EXIT_OUT_DATA:
    ret

    Первая передача проходт все ок и ответная часть присылает ответ который распознаеться, но повторную передачу не как не могу организовать
    Допустим что от ответной части не пришла команда, а время ожидания на ответ истекло
    посылаем повторно
    //*******************************************************************************
    // п/п ПРОВЕРКА ВРЕМИНИ НА ОТВЕТ
    //*******************************************************************************
    AUDIT_ANSWER:
    //——-ПРОВЕРКА РАЗРЕШЕН ПРИЕМ——————————————
    sbrc ST1,ST1_RXD
    rjmp EXIT_AUDIT_ANSWER
    //——-ПРОВЕРКА ОКОНЧАНИ ВРЕМЕНИ ДОПУЩЕННОГО НА ОТВЕТ——————
    DELAY TimeANSWER_L, TimeANSWER_M, TimeANSWER_H,TimeANSWER
    lds TMP_1,TimeANSWER
    cli
    cpi TMP_1,ZERO
    brne EXIT_AUDIT_ANSWER
    sei

    //——-Время ИСТЕКЛО——————————————————
    sbr ST1,S_BIT4 ; прием ЗАПРЕЩЕН
    //—————РАЗРЕШЕНИЕ ПЕРЕДАЧИ—————————————————
    ;!!!! Вот тут на асцилографе нет передачи но уровень ТХD устанавиливаеться в 1
    cli
    ldi TMP_Y,0x88 ;(1<<RXEN1)|(0<<RXCIE1)|(1<<TXEN1)|(0<<TXCIE1)
    sts UCSR1B,TMP_Y
    sei
    //————————————————————————————
    clr TMP_1
    sts CountTXD,TMP_1
    sts TimeTXD,TMP_1
    sts ComandaOldTXD,TMP_1
    sts CountBateTXD,TMP_1

    //——-ВЫХОД————————————————————
    EXIT_AUDIT_ANSWER:
    sei
    ret

    1. Проще самому заново написать чем в чужом коде разобраться.

      По моему ты просто гдето намудрил. Вначале добейся чтобы у тебя данные летели потоком. Чтобы гарантировано видеть что уарт у тебя инициализирован правильно. Потом усложняй помаленьку.

  59. Спасибо за ответ, данные летят потоком, и пачками, но вот после запрета передачи — разрешения приема и снова разрешения передачи они лететь не хотят ни как, а TXD (тыкаюсь на осцилографом) переходи в единицу

          1. Да в принципе все просто, но ведет себя по странному
            при инициализации установлено

            ldi TMP_Y,0×88 ;(1<<RXEN1)|(0<<RXCIE1)|(1<<TXEN1)|(0<<TXCIE1)
            sts UCSR1B,TMP_Y
            Далее после того как передача прошла n-кол-во раз выставляю запрет пеедачи/разрешение према
            cli
            ldi TMP_Y,0×90 ;(1<<RXEN1)|(0<<RXCIE1)|(1<<TXEN1)|(0<<TXCIE1) тут поставил 0х98 и все ОК
            sts UCSR1B,TMP_Y
            sei
            Потом так как ответ не пришел овторяю передачу и переключаю UART

            //————–РАЗРЕШЕНИЕ ПЕРЕДАЧИ—————————————————
            ;!!!! Вот тут на асцилографе нет передачи но уровень ТХD устанавиливаеться в 1
            cli
            ldi TMP_Y,0×88 ;(1<<RXEN1)|(0<<RXCIE1)|(1<<TXEN1)|(0<<TXCIE1)
            sts UCSR1B,TMP_Y
            sei

            1. 1) Ты разрешил Передачу, прием и запретил прерывания по передаче и приему

              2) Ты разрешил Передачу, прием и запретил прерывания по передаче и приему

              3) То же самое..

              эээээ а где ты запрещаешь передачу? Или ты мне скинул тот вариант который работает?

            2. Кстати, совет. Раз пишешь на ассемблере то рекомендую раскурить мою RTOS — с ней написание программ становится в десятки раз легче. Прошивка становится очень структурированной и ее легко понять, модернизировать, добавлять новые функции.

              Порой даже до удивительного доходило — пишешь 6 часов прогу. Потом компилишь. Исправляешь все синтаксические ляпы и прога с первого прогона работает идеально, как задумано.

              1. Извеняюсь в коментах ошибки
                1. инит — разрешение приема запрет передачи (прерывание по приему разрешено)
                ldi TMP_Y,0×88 ;(0<<RXEN1)|(1<<RXCIE1)|(1<<TXEN1)|(0<<TXCIE1)
                sts UCSR1B,TMP_Y
                2. передача закончалась
                ldi TMP_Y,0×90 ;(1<<RXEN1)|(1<<RXCIE1)|(0<<TXEN1)|(0<<TXCIE1)
                sts UCSR1B,TMP_Y
                я тут щас написал в рабочем варианте
                ldi TMP_Y,0×98 ;(1<<RXEN1)|(1<<RXCIE1)|(1<<TXEN1)|(0<<TXCIE1)
                sts UCSR1B,TMP_Y
                3. прием окончан
                ldi TMP_Y,0×88 ;(0<<RXEN1)|(1<<RXCIE1)|(1<<TXEN1)|(0<>Раз пишешь на ассемблере то рекомендую раскурить мою RTOS
                Re: Интересую вещь предлогаешь, что это где можно взять?

                1. 1
                  2
                  3
                  4
                  5
                  6
                  7
                  
                  "1. инит - разрешение приема запрет передачи (прерывание по приему разрешено)
                  ldi TMP_Y,0×88 ;(0<<RXEN1)|(1<<RXCIE1)|(1<<TXEN1)|(0<<TXCIE1)
                  sts UCSR1B,TMP_Y"
                   
                  Гхм.... 
                  0<<RXEN1 - запрещение приема
                  1<<TXEN1 - разрешение передачи. Думаю ты опять опечатался :)

                  По идее должно работать, а почему не пашет.. хз, может там флаг какой не встает как надо, а у тебя на него циклится прога. Тут надо почитать внимательно даташит в том числе errata.

                  Вообще, по хорошему, проверять флаги уарта и на основании этого делать отправку моветон. Куда продуктивней слать по прерываниям все.

                  Т.е. у тебя етсь буффер. Ты туда сгружаешь свою посылку как есть, целиком. И засовываешь в UDR первый байт. Он отправляется и прерывание UDRE берет из буффера второй байт, потом третий пока все не кончится. А в конце всей этой бодяги у тебя выскочит прерывание TXCIE и чо нибудь еще сделает. Например загрузит в буффер следующую посылку если надо.

                  При этом прога выполняется сама по себе. Получается этакая многозадачность :)

                  Про RTOS написано в последних главах учебного курса AVR. Ну или поиском по сайту по слову RTOS

                  Еще где может быть косяк. У тебя если проверка на отправку сделана на бите TxC, а этот бит, ЕМНИП, снимается когда происходит уход на этот вектор. Либо вручную (что ты вряд ли делаешь). Но я вижу что ты там маниакально включаешь выключаешь глобальные прерывания (ИМХО перебор, Но тебе видней) и может получиться, что флаг просто зажует и твое условие переклинит.

  60. Приемник GPS несет 42 байта по протоколу РС-232, дальше мая мега принимает их … Предположим мне для полного счастья нужно выбрать 5, 16, 18, 31 байты . Посоветуйте как это можно более рационально и правильно зделать? как отфильтровать эти байты и выкинуть остальные? Заранне спасибо!

    П.С. Код этих байтов это время и координаты, то есть они все время меняються в большом интервале

    1. Ловишь байты от начала пакета. И по счетчику выкидываешь те которые тебе не нужны. Как пакет весь собрал — счетчик сбрасываешь. Сделай все на прерываниях.

  61. DI HALT, доброе время суток. Очень заинтересовал труд выложенный на твоём сайте. Нашел для себя много интересного и нового. Очень впечатлен твоими знаниями и оптимизмом и стермлением поделиться ими с другими. Как мне узнать твоё мыло? Хотел бы по теме взаимодействия м/контроллеров и COM портов посоветоваться. Моё мыло glass34(собака)mail(dot)ru. Заранее благодарю за ответ.

  62. DI HALT, привет :) Решил разобраться с УАРТом (да и заодно закрепить уже познанное), но.. не работает! Код приведённый снизу (должен выводить на портД принятое), в качестве преобразователя — нечто втыкающиеся в USB (граунд к граунду на схеме, Тх к Rx на схеме), контроллер от внутреннего гена 8/8=1 МГц
    Как не тыкал в терминалке — ничего не светит, не реагирует! (скорости и т.п. — совпадают)
    Проверял лог. уровни на выходе преобразователя: 3.4 — высокий, 0.4 — низкий
    Уже загнался искать ошибку, помоги пожалуйста!

    Код:

    .org 0x0000
    rjmp RESET

    .org 0x0007
    rjmp USART0_RX

    Reset:
    ldi r16,RamEnd
    out SPL, r16

    ldi r16, 0b11111111
    out ddrb, r16

    ldi r16, (0<<pinD)
    out ddrD, r16

    ldi r16, (1<<SE) ;спящий режим разрешён
    OUT MCUCR, R16

    .equ XTAL = 1000000
    .equ baudrate = 9600
    .equ bauddivider = XTAL/(16*baudrate)-1

    LDI R16, low(bauddivider)
    OUT UBRRL,R16
    LDI R16, high(bauddivider)
    OUT UBRRH,R16
    ; LDI R16,0
    ; OUT UCSRA, R16
    LDI R16, (1<<RXEN)|(1<<RXCIE)
    OUT UCSRB, R16
    LDI R16, (1<<7)|(1<<UCSZ0)|(1<<UCSZ1)
    OUT UCSRC, R16

    main:
    rjmp main

    USART0_RX:
    lds r16, UDR
    out portb, r16
    reti

          1. Хм.. наверное в панике кромсал код и пробовал всё подряд
            Попробовал заменить на in — не работает.. :(
            Предделитель убрал — не работает..
            Буду пробовать с кварцем, может не синхронизируется

            1. Поставил кварц, схема заработала (!) ровно один раз! Существует ли какая-либо последовательность подключения (например, сначала питание схемы, потом ком-порт, или наоборот)?
              ЗЫ столкнулся с паразитным питанием от.. TxD :)) там всё (почти) время высокий уровень, и на питании контроллера — 2.5 в, чего достаточно ))

  63. Уважаемый DI HALT!
    Что-то эта тема очень туго идет. Много вопросов, много ответов, много комментариев, а народ все никак не может разобраться…
    У меня вот проблемка с приемом-передачей пакетов в 2-14 байт. Ну не доходит до меня, как сделать буфер…
    А слабо Вам написать универсальную библиотечку, как для LCD?
    С Вашим талантом и уникальным умением объяснять доходчиво все вопросы сразу отпадут…

      1. На «С» НЕ надо и вникать в проблему- всё уже написано. НЕ надо делать никакой буфер- его уже сделали за Вас программисты CodeVision!!!
        #include // Standard Input/Output functions — Великая Сила!!!

  64. DI HALT, у меня стала задача использовать интерфейс CAN с аврами. сложно ли это реализовать? пока не представляюю как. что нужно програмнно реализовать в авре для работы CAN интерфейса?

      1. Тогда, может подскажите, какой интерфейс в авре можно использовать для ответственных команд? То есть интерфейс с помехоустойчивый, надежный и дальность приличная была. И еще я вот подумывал,можно ли RS-232 использовать в качестве канала которого оптоволокно? Тогда его дальность и помехоустойчивость увеличится. Правда ведь?

  65. Подскажите, пожалуйста, как сделть синхронный режим uart на примере tiny2313 или tiiny25?

    Задача сделать температуро-устойивую телеметрию, а для кварца нет места на плате. Поэтому хочу использовать синхронизацию внешним сигналом.

    Например для tiny25 пробую сделать программно и вырисовываются такой вариант:
    Один из выводов конфигурируется как вход с подтяжкой. А другой как выход uart.
    По изменению логического уровня на входе в процедуре прерывании передается бит из кадра UART.
    Т.е если ко входу подключен внешний генератор на 19200 телеметрия есть, не подключен, телеметрии нету.

    А как правильно сделать по науке?

    1. ЕМНИП уарт может работать и в синхронном режиме. Правда там вроде как не дуплекс получается, а полудуплекс т.к. один провод синхронизация другой данные. Почитай даташит или Евстифеева.

      Можешь сделать по SPI он от природы синхронный.

  66. В чём разница между USART и UART? По спецификации, благодаря синхролинии как бы скорость можно больше поставить.
    Ну, а если скорость не главное, есть ли ещё смысл добавлять сихронизацию?

    1. вот только что хотел спросить почти то же самое: UART ведь один в контроллере. если надо два — то только программно? Ди, ты кодил программный UART? как посоветуешь это сделать?

      1. На AVR нет, на С51 приходилось. Комментом ниже я дал ссылку на библиотеку ассемблерных исходников. Там же целых три вида софтверных уарта.

        Обычно его на INT прерывании делают Т.е. чуем старт бит, запрещаем прерывание и начинаем по таймеру пропалывать (от poll-опрос) линию по бодам. Есть бит — 1, нет бита 0. Настройка таймера по бодрейту, число тыков опроса обычно хватает и одного на бит, но надежней несколько. АВР емнип каждый бит три раза проверяет.

  67. UART может быть и больше максимум чтоя встречал 8 просто еслиб был специализованый МК… а так ставить монстра с сотней ножек когда просто нада 4 порта не очень охота…

  68. кстати вот нашол:
    http://depositfiles.com/files/qopdpg67j
    и вобще советую: http://radiojurnals.xan.su/buki/mk.htm
    Правда пока нет времени читать но завтра надеюсь почитаю… Если кто ещо знает книги по програмной реализации буду оч благодарен за ссылки… (Также оч интерисует мнение за против, подводные камни, грабли… а то и так тяжело асм ток 2 неделю изучаю:)

  69. >> LDI R16, (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
    error: Undefined symbol: URSEL

    Судя по описанию он равен семёрке. Но и с ней ничего не работает — в терминале Putty я получаю кучу символов «-» (это горизонтальная линия, которыми в DOS’e рисовали рамки).

    Кстати, HyperTerminal вообще не хочет работать, при замыкании Rx на Tx ничего в ответ не получает. Поэтому работают с Putty.

        1. Вот тебе инициализация для тиньки2313

          1
          2
          3
          4
          5
          6
          7
          8
          9
          
          	.equ XTAL = 8000000	; частота процессора. 
          	.equ baudrate = 19200	; уарт будет работать на 19200 бод. 
          	.equ bauddivider = XTAL/(16*baudrate)-1	; тут просто вычисляем необходимую задержку для таймера.
           
          	OUTI UBRRL, low(bauddivider) 	; и загружаем ее в регистры уарта вначале младший байт
          	OUTI UBRRH, high(bauddivider) 	; потом старший байт
          	OUTI UCSRA, 0 			; выставляем режим асинхронной приемопередачи, с обычной (не двойной) скоростью.
          	OUTI UCSRC, 3<<UCSZ0 		; настраиваем протокол на стандартный. 8 бит, один старт и стоп биты. 
          	OUTI UCSRB, 1<<RXEN|1<<TXEN|1<<RXCIE|0<<TXCIE 	; разрешаем прием и передачу
          1. Ни эта, ни та которая в даташите (собственно это почти одно и то же) ничего не изменяют — в терминал сыпятся эти чёрточки вместо изменяющихся символов (каждый цикл с задержками 1 сек. передаю R20, который каждый раз уменьшаю на 1).

            Я уже подумываю, что проблема в MAX232-переходнике, но странно что при замыкании Rx-Тх я получаю байты обратно.

              1. Ураааа. Наконец-то я сделал что-то, что работает :)

                Большое спасибо за этот злосчастный CKDIV8 бит, я бы еще много времени потратил пока обнаружил его.

                Имею в терминале периодически уменьшающийся R20, то есть ЧЩЭШЗЫЬВЖУТСРЯПОНМЛКЙИХГФЕДЦБАЮъчщэшзыьвжутсряпонмлкйихгфедцбаю©╬Ґ╪╩╨╧╦ЇІ╣ЄЁ╡╠╟╞╝ґ╛╚╙╘╗їі╔єё╒║═÷·²°⌡ ≥≤≈√•■⌠▓▒░▐▌█▄▀┼┴┬┤├┘└┐┌│~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:987654321

                Еще раз спасибо :)

  70. Так, теперь хочу и принимать байты. И снова проблемы :)

    Во-первых, UCSRA & (1<<RXC) почему-то всегда 0. Всегда.

    Во-вторых, прерывания не работают ни при получении, ни при отправке данных. Хотя я их разрешал при инициализации UART (OUTI UCSRB, 1<<RXEN|1<<TXEN|1<<RXCIE|0<<TXCIE). Может где-то еще надо глобально их разрешить каким-нибудь SEI? если даю команду SEI перед основным циклом программы, то данные через UART начинают идти с гигантской скоростью, я в терминале за полосой прокрутки не успеваю…

    В-третьих, для создания обработчика прерывания на языке Си этого кода достаточно? больше ничего не надо?

    ISR(USART_RXC_vect)
    {
    switch(UDR)
    {
    case ‘1’: LED_PORT = 0<<LED1; break;
    case ‘0’: LED_PORT = 1<<LED1; break;
    default: break;
    };
    };

    1. Да надо глобально разрешить. SEI

      то что у тебя данные полетели в усарт бесконтрольно это значит что утебя программа дала где то сбой.

      Для си этого достаточно, но надо создать обработчики для всех разршенных прерываний иначе будет краш. Либо не разрешать то что ты не испльзуешь.

  71. Вот часть кода
    LDI XH,high(String) ; некий буфер, из который потом будет отправляться по UART
    LDI XL,low(String)
    ST X+,R15 ; в регистах результат, заполняю String
    ST X+,R14
    ST X+,R13
    ST X+,R16
    ST X+,R11
    LDI R19,0x0A ; признак конца буфера
    ST X+,R19

    LDI XH,high(String)
    LDI XL,low(String) ;ставлю указатель на начало буфера
    LD R21,X
    OUT UDR, R21 ; вручную забрасываю в регистр первый элемент буфера на отправку
    SBI UCSRB,UDRIE ; установить бит в РВВ ; разрешить прерывания

    Смотрю всё в Студии в режиме отладки. После предпоследней команды UDR обнуляется (что вроде логично, так как данные из него перебросились в сдвиговый регистр), соответственно возник флаг опустошения буфера, но прерывания пока нет, так как они ещё запрещены.
    Последней командой я их как раз и разрешаю. И как положено возникает прерывание.

    _UDREempt: LD R21,X+ ; в этой строчке не совсем правильно, будет повтор первого символа, плохо что нет LD R21,+X :)
    CPI R21, 0x0A
    BRNE _snt
    OUT UDR, R21
    CBI UCSRB,UDRIE ; очистить бит в РВВ; запретить прерывания по опустошению
    RETI

    _snt: OUT UDR, R21
    RETI

    Но вот в чём вопрос: после _snt: OUT UDR, R21 UDR тутже опять обнуляется. Почему? Ведь ещё первый байт не ушёл. На скорости 9600 (у меня) он будет уходить чуть более 1мс, почему он обнулился, если сдвиговый регистр ещё занят, пихать некуда. Хотя Студия показывает что UDRE ещё 0, т.е. не пуст. Может это всего лишь особенности отладки Студии. Следующий символ отправляется вроде бы правильно — через 1мс, тоже по появлению флага опустошения.
    И ещё вопрос — насколько правильно или оптимально реализованн процесс вывода в UART с использование прерываний? В этом моём случае

  72. Я сломал голову.
    В AVR studio регистр UBRRH меняется вместе с UCSRC.
    Бит в URSEL не помогает.
    Это баг симулятора?

    //uart init
    UBRRH=0b00000000;
    //всё хорошо
    UBRRL=0b01100111;
    //всё хорошо
    UCSRB=0b00011000;
    //всё хорошо
    UCSRC=0b10000110;
    //всё плохо!!! UBBRH тоже изменил своё значение

    Я бы всё простил, но при загрузке байта в UDR — флаг UDRE не падает.
    И мир во всём мире не наступает.
    Что делать?

    1. По поводу регистров UBRRH и UCSRC в студии. Сам долго не вкуривал тему, менял очередность и так и так. А он писал в оба регистра одинаковое значение (даже при явном указании URSEL 1 или 0). Выкурил даташит (на мегу16) проверил кода от DI, а вкумекал, только тогда, когда по совету DI выкурил инклюдник — там четко написано что UBRRH и UCSRC имеют адрес $20, а в коменте написано «; Note! UCSRC equals UBRRH», что не противоречит даташиту. Главное при записи в UCSRC использовать бит URSEL=1 (как в примере DI). А в симуляторе студии — это баг, так как в новых МК эти регистры имеют разные адреса(а в старых МК регистра UBRRH вообще не было) то разработчики не парились по поводу обработки одного бита, тем более названия регистров берутся из инклюдника.
      «Я бы всё простил, но при загрузке байта в UDR — флаг UDRE не падает.» у меня он тоже не сразу падает, а только после прохода следуующей команды.

  73. Всем привет!

    У меня ATMega8, фьюзы все заводские (т.е. работает она на частоте 1МГц).
    К USART соединился кабелем на pl2303 (от какого-то сотового).

    Результат пока: светодиод мигает 10 раз (показывает старт), потом на терминал (использую putty) валится мусор какой-то, т.е. как будто бы скорости не совпадают…

    Код вот такой (вставил как есть):

    /*
    USART Test
    */
    #include
    #include
    #include

    // Define baud rate
    #define XTAL 1000000UL
    #define BAUDRATE 9600UL
    #define BAUDDIVIDER (XTAL / (BAUDRATE * 16) — 1)

    void USART_Init(void)
    {
    // Set baud rate
    UBRRH = (uint8_t)(BAUDDIVIDER>>8);
    UBRRL = (uint8_t)BAUDDIVIDER;
    // ?
    UCSRA = 0;
    // Enable receiver and transmitter and disable interrupts
    UCSRB = (1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<TXCIE);
    // Set frame format to 8 data bits, no parity, 1 stop bit
    UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
    }

    void USART_Send(uint8_t u8Data)
    {
    while((UCSRA & (1<<UDRE)) == 0);
    UDR = u8Data;
    }

    uint8_t USART_Recv(void)
    {
    while((UCSRA & (1<<RXC)) == 0);
    return UDR;
    }

    void main(void)
    {
    DDRB=0x01;

    // Show that we’re started (blink 10 times)
    for(uint8_t i=0; i<10; i++)
    {
    PORTB=(0<<PB0);
    _delay_ms(300);
    PORTB=(1<<PB0);
    _delay_ms(300);
    }

    // Initialise USART
    USART_Init();

    // Send forever
    for(;;)
    {
    USART_Send(‘T’);
    USART_Send(‘E’);
    USART_Send(‘S’);
    USART_Send(‘T’);
    USART_Send(0x0D);
    USART_Send(0x0A);
    }
    /*
    // Repeat indefinitely
    for(;;)
    {
    // Echo received characters
    u8Data = USART_vReceiveByte();
    USART_vSendByte(u8Data);
    }
    */
    }

    Где может быть ошибка?

    P.S. И главное что странно — этот код _почти_ работает, т.е. «затык» где-то совсем в малом… :(

      1. Ура! Всё заработало!
        Кабель был просто глючный.
        Вот видео: http://www.youtube.com/watch?v=yqvrzfnHuWI

        Заработал кабель от Pantech на чипе OTI (там прям на платке написаны не номерки, а названия: GND, Tx, Rx и т.д.)

        Кабель который НЕ заработал называется GT2303 (сделан на Prolific PL2303), к нему припаяно 4 проводка (которые к телефону). На плате GT2303 так (номера прям на плате проставлены — на «телефонной» стороне): 6-белый (похоже на VCC, во всяком случае МК от него и зелёного запитывается), 3-синий, 4-красный, 11-зелёный(похоже на GND). Может кто знает что это за зверь? Работает 50/50, хотя винда определяет его всегда.

        DI HALT, а про использование ATMega8 напрямую с USB статья будет? ;)

        И ещё интересно потянет ли ATMega8 ethernet?

  74. ВНИМАНИЕ!

    Чтоб на ATMega8-16PU на внутреннем осцилляторе USART работал _СТАБИЛЬНО_ — надо поставить 8МГц!!!
    Проверил опытным путём — все глюки полностью исчезли!

    фьюзы: 0xD9 и 0xE4(для 8МГц, по умолчанию для 1МГц — 0xE1)

  75. Перечитываю первые посты после статьи и не очень-то доходит. :)
    «Вешаем прерывание по UDRЕ которое пропихивает все данные из буфера в UDR, а когда буфер опустошится само себя отключает»
    Момент «…само себя отключает» это как — это само собой происходит, или в обработчике проверка на опустошение вышеуказанного буфера?
    И что подразумевает под собой прерывание по завершению передачи, включаемое битом TXCIE? Т.е. когда у нас в сдвиговом регистре есть байт, который в данный момент отправляется и в UDR уже помещен следующий, последний из буфера, на отправку (который по хорошему должен быть помещен по перыванию UDRE) и после того как этот последний байт из UDR перейдет в сдвиговый регистр и полностью отправится, а в UDR уже ничего не будет, вот тогда и возникнет прерывание по завершению передачи? Правильно я понимаю?
    Просто никак не могу придумать, как сделать так что-бы не возникало никаких прерываний после отправки последнего байта и не делать запрет прерываний по опустошению UDR или окончания передачи (если такое возможно). Приходит в голову толко проверка последний ли символ или нет и запрещать перывания, чтобы не попадать в обработчик лишний раз

    1. Именно в обработчике мы проверяем по быстрому буффер и если он пуст то запрещаем прерывание UDRE и выходим. Все, на этом передача со стороны софта оканчиается (но продолжается на уровне железа — усарт все еще шлет байт из сдвигового регистра).

      Дальше у нас буффер пуст, сдвиговый регистр тоже опустел и вылетает TXC — полное окончание всей передачи.

    1. «Самый прикол тут это бит селектора URSEL дело в том, что по неизвестной причине создаетели решили сэкономить байт адреса и разместили регистры UCSRC и UBRRH в одной адресном пространстве. А как же определять куда записать? А по старшему биту! Поясню на примере. Если мы записываем число у которого седьмой бит равен 1, то оно попадет в UCSRC, а если 0 то UBRRH.»

      Вроде там просто выбор синхронный-асинхронный. А выбор куда записывать может типа раньше было , ну типа в более ранних моделях? Воще-то в мануале на например 2313 UMSEL просто селектор синхронный-асинхронный, а в UCSRC 7 бит типа просто зарезервирован, ну вроде типа так. Э?

      1. Не путай с UMSEL (Uart Mode Selectror). URSEL (Uart Register Selecter) это бит селектора в какой регистр засылать. Причем этот фокус далеко не на всех моделях AVR а чеерз раз (обычно в старых аврках вроде Меги8 он есть) :)

    1. по уарту это будет сложновато. Т.к. в уарте нет никакой поддержки адресов и коллизий или прочих механизмов разделения источников. Разве что Token Ring организовать с жестким разделением. Тебе лучше подумать в сторону iic

      1. Спасибо за помощь! Но возник вопрос!: Мне надо реализовать модуль сопряжения CPU (CompactPCI) с 16 независимыми станциями (RS422). Я думаю ставить по контроллеру с УАРТ на прием/передачу в сторону станций, они (контроллеры) должны быть связаны с главн.контроллером, который принимает информацию от CPU, вопрос — подойдет ли для связи контроллеров IIC (я прочитал, что у нее низкая пропускная способность и практически не используется в ПК)? Что Вы скажите про SPI? и вообще в том ли направлении двигаюсь?

        1. У IIC скорость 100кбит секунду. Зато есть адресация, разгребание коллизий и много плюшек. Она заточена на работу с множеством приемников. Но на компе ты ее не найдешь, придется делать адаптер.

          SPI работает очень быстро, но у ней три линии + по линии разрешения на КАЖДЫЙ приемник. Т.е. у тебя будет 19 проводов тянутся :)

                  1. DI HALT, возможно ли по SPI соединить 4 равноправных микроконтроллера с одним общим (топология «звезда») каждый может передавать и принимать информацию. (каждый может быть и ведущим и ведомым)?

                    1. Только если от каждого к каждому будет идти провод разрешения работы. Но это проводов убиться. У SPI же нет никакой адресации и системы разрешения коллизий. Тут коллизия вызывает сгорание кого либо :)

                    2. Спасибо за помощь! Поздравляю с переездом! DI HALT, помоги разобраться, предположим МК имеет 8 УСАРТ интерфейсов (Хмега), они подключены к 8 независимым передатчикам, у меня возник вопрос — предположим УСАРТ1 принял инф-ю, сработало прерывание и программа ушла на его обработку, а если в это время остальные УСАРТ принимают инф-ю (много байт), буфер всего 2 байта, не потеряются ли первые принятые байты пока обрабатывается прерывание УСАРТ1. Как управлять всеми 8 УСАРТ, чтобы данные не потерялись и не было застоя?

                    3. Прерывание должно быть очень быстрым. Т.е. хапнул байт из аппаратного буффера и перекинул его в программный буффер. Вышел. На все про все около 10тактов, если на ассемблере писать. Тогда ничо не потеряешь.

  76. Ай-ай-яй… Что действительно все так плохо?
    А если ОЧЕНЬ нужно. Ну например 16 устройств на расстоянии 15-30 метров между ними (общая длинна линии 370 метров).
    А что такое «Token Ring»?

  77. «А что такое “Token Ring”?»
    ——————————
    Рекомендую книгу Л.Райс Эксперименты с локальными сетями микро-ЭВМ. Издательство Мир 1990. 268стр.
    Там большая часть книги как раз про “Token Ring”, а в начале — коротко про другие методы последовательной передачи данных (Манчестер, NRZ, RS232). Написано очень хорошо.

  78. Здравствуйте, подскажите пожалуйсто!
    Надо прицепить мою мегу16 к компу через USART. Для начала решил разобраться с ним в AVR Studio. Вот мой код:
    USART_Init:
    ldi temp, 0x00 ; Set baud rate 9600
    out UBRRH, temp
    ldi temp, 0x1A
    out UBRRL, temp

    ldi temp, (1<<RXEN) | (1<<TXEN)
    out UCSRB,temp ; прием и передача разрешены

    ldi temp, (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0) ; длина — 8 бит
    out UCSRC,temp
    ret
    USART_Receive:
    sbis UCSRA, RXC
    rjmp USART_Receive
    in temp, UDR
    ret
    Значит, как только курсор остнавливается на метке USART_Receive: загоняю я в регистр UDR байт, предпологая, что он там останется, пока не поставлю флаг RXC. А он(UDR) на следующей команде просто сбрасывается в 0 и этот ноль идет в temp.

      1. Да, проверил в Proteus 7.4 вроде работает. А на самом нужно сделать так, чтобы контроллер из всех полученных байтов фильтровал только нужный ему ( у меня это 1), как только его словит на PC5 загорается светодиод. А остальные байты пусть пока засовывает в какой-нибудь рон не изменяя их. В Протеусе на виртуальном терминале от генератора байтов и в самом деле передаются единицы, а вот на RXD приходит какая-то муть, все что угодно, кроме 1. А еще он ругается что у меня ошибка «Error Frame». Не могу понять почему: кадр 8 бит 1 стоп, без четности, от внутреннего генератора, в протеусе принимает биты от генератора HDL через MAX232. Вот мой код, вам на расскритиковку. Подскажите pls, заранее благодарен))

        .include «C:\Program Files\Atmel\AVR Tools\AvrAssembler\Appnotes\m16def.inc»
        .equ baudrate = 9600
        .list
        .def temp = R16
        .def byte_res = R17
        .def byte_count = R18
        .def t0 = R19
        .cseg
        .org 0x0000
        jmp main
        .org $002 ;External Interrupt0 Vector Address
        reti
        .org $004 ;External Interrupt1 Vector Address
        reti
        .org $006 ;Output Compare2 Interrupt Vector Address
        reti
        .org $008 ;Overflow2 Interrupt Vector Address
        reti
        .org $00A ;Input Capture1 Interrupt Vector Address
        reti
        .org $00C ;Output Compare1A Interrupt Vector Address
        reti
        .org $00E ;Output Compare1B Interrupt Vector Address
        reti
        .org $010 ;Overflow1 Interrupt Vector Address
        reti
        .org $012 ;Overflow0 Interrupt Vector Address
        reti
        .org $014 ;SPI Interrupt Vector Address
        reti
        .org $016 ;UART Receive Complete Interrupt Vector Address
        rjmp USART_RXC
        .org $018 ;UART Data Register Empty Interrupt Vector Address
        reti
        .org $01A ;UART Transmit Complete Interrupt Vector Address
        reti
        .org $01C ;ADC Interrupt Vector Address
        reti
        .org $01E ;EEPROM Interrupt Vector Address
        reti
        .org $020 ;Analog Comparator Interrupt Vector Address
        reti
        .org $022 ;Irq. vector address for Two-Wire Interface
        reti
        .org $024 ;External Interrupt2 Vector Address
        reti
        .org $026 ;Output Compare0 Interrupt Vector Address
        reti
        .org $028 ;Store Program Memory Ready Interrupt Vector Address
        reti
        .org $02A
        main:
        ldi temp, 0xff
        out DDRC, temp
        ldi temp, 0x00
        out PORTC, temp
        ldi temp,high(RAMEND) ; инициализация стека
        out SPH,temp
        ldi temp,low(RAMEND)
        out SPL,temp
        uart_init:
        LDI temp, 0x00
        OUT UBRRL,temp
        LDI temp, 0x0C
        OUT UBRRH,temp
        LDI temp, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(1<<UDRIE)
        ; Прерывания запрещены, прием-передача разрешен.

        OUT UCSRB, temp ; ; Формат кадра — 8 бит, пишем в регистр UCSRC, за это отвечает бит селектор
        ldi temp,(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ0)
        out UCSRC,temp
        sei

        T: ; замкнутый цикл
        rjmp T

        ;Обработка прерывания по событию «ПРИЕМ БАЙТА» через USART
        USART_RXC:
        in temp,UDR ;Сохраняем полученный символ (Чтение порта)
        cbi UCSRB, RXCIE ;Запрещаем прерывание
        sbis UCSRA,FE ;Проверяем на наличие ошибки кадра
        rjmp T
        cpi temp, 1
        breq LED

        rcall SendByte ;Отладка
        SBI UCSRB, RXCIE
        SBI UCSRB, UDRIE ; Разрешаем прерывание
        reti

        SendByte: ;Передача символа на ПК
        sbis UCSRA,UDRE ;Пропустить следующую команду если бит в порту сброшен (регистр не пуст)
        rjmp SendByte ;Ожидаем освобождения буфера передачи
        out UDR,temp ;Передаем символ
        ret

        LED:
        sbi PORTC, 0x05
        ret

        1. Зачем ты разрешаешь прерывания по передаче если не используешь его?

          1
          
          LDI temp, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(1<<UDRIE)

          Это не обработчик прерывания, это какой то пиздец:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          
          USART_RXC:
          in temp,UDR ;Сохраняем полученный символ (Чтение порта)
          cbi UCSRB, RXCIE ;Запрещаем прерывание
          sbis UCSRA,FE ;Проверяем на наличие ошибки кадра
          rjmp T
          cpi temp, 1
          breq LED
           
          rcall SendByte ;Отладка
          SBI UCSRB, RXCIE
          SBI UCSRB, UDRIE ; Разрешаем прерывание
          reti

          Нарушены все правила какие только можно нарушить. Регистры не сохраняются в стеке, циклы ожидания в обработчиках. Ну кто так делает? У тебя программу срывает.

          Плюс какого хрена вот это:
          breq LED
          ушел через JMP

          LED:
          sbi PORTC, 0×05
          ret
          вернулся через RET и куда вернулся? Вывалился из обработчика прерываний, но с запрещенными глобально прерываниями.

          Программу в AVR Studio трассировал? Сразу скажу, что нет. Т.к. такие ошибки вылавливаются при ПЕРВОМ же прогоне трассировщика. И смысл было без трассировки дальше голову ломать?

          1. М-да. Согласен, Di HALT, затупил я))))). Взял куски кодов, сдесь имеющиеся, слепил все тупо.
            Код я переделал, работает по-ходу, но не совсем правильно. Дело в том, что когда у меня байт (причем любой) приходит с UDR в TEMP, в TEMPе, 5 и 6 разряды устанавливаются в 1. Поэтому, когда сравниваю с константой, приходится писать не 3, а 51. Что это, глюк протеуса или я опять чего-то напортачил??

             main:
            	ldi temp, 0xff
            	out DDRB, temp
            	ldi temp, 0x00
            	out PORTB, temp
            	ldi temp,high(RAMEND) ; инициализация стека
            	out SPH,temp
            	ldi temp,low(RAMEND)
            	out SPL,temp
            uart_init:	
            	LDI 	temp, 0x0C
            	OUT 	UBRRL,temp
            	LDI 	temp, 0x00
            	OUT 	UBRRH,temp
            	LDI 	temp, (1<<RXEN)|(1<<RXCIE)	;Разрешаем прием и прерывание по приему.
            	OUT 	UCSRB, temp	
            	ldi temp,(1<<URSEL) | (1<<UCSZ0) | (1<<UCSZ1) ; Формат кадра - 8 бит, 1 стоп
            	out UCSRC, temp
            	sei
            Y:	rjmp Y			; ждем, пока на UDR не придет байт
            UART_Receive:
            	in temp, UDR		; записываем в temp байт из UDR  
            	cpi temp, 51		; сравниваю с 51, хотя хотелось бы с 3-кой
            	breq LED
            	out PORTB, temp
            	reti			; если нет ждем дальше
            LED:
            	sbi PORTB, PB5		; если значение в temp совпало зажигаем ногу
            	reti			 

            А еще, чуть не забыл, у меня вопрос, по поводу Протеуса. Собрал я в нем схемку для передачи данных: RS-232 — MAX232 — ATmega16. Когда начал передовать байты нога MAXа засветилась желтым цветом, мол ошибка. Залез на один форум, там сказали, что надо еще один MAX последовательно прицепить. Попробовал — заработало! А как в реале, на железе, нужна ли вторая MAX?

            1. А вот тут должно работать четко. Т.е. что пришло в UDR то и должно попасть в темп.

              Причины могут быть следующие:
              Ты выставил неправильно скорость/частоту/параметры работы уарта и у тебя в UDR приходит что то что уарт принимает за 51. Мог затупить с тактовой частотой виртуального МК в протеусе.

              2. Где то у тебя темп портится в прерывании, а ты это недофиксил. Опять же, в прерывании у тебя грубейшая ошибка — ты не сохраняешь регистры в стек. В этом примере прокатит, т.к. у тебя нет никакого действия фоном

              Y: rjmp Y

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

              В железе нужен 1мах232 , а в протеусе для связи с терминалом он не нужен вообще.

              1. DI HALT, по пункту 2. Я так понимаю, речь идет о сохранении в стеке регистра SREG? Я не совсем понимаю для чего его там сохранять, всегда думал, что стек служит только для сохранения текущего адреса процессора, чтобы тот вернулся туда же, после выполнения прерывания, откуда его вызвали. А как примерно должен выглядеть мой кусок обработчика прерываний после сохранения в стеке регистра?

                1. Не только срег, но и все используемые регистры.

                  Перечитай АВР. Учебный курс. С самого начала (там переписаны заново почти все статьи) и поймешь как работает стек, зачем сохранять регистры и так далее.

  79. Помогите понять проблему пожалуйста.

    Использую обмен данными по USART с COM портом ПК. На компьюторе пробовал через прграммы Terminl by bray, Hypper Terminal и даже написал на C# программу работы с КОМ портом.. Результат тот — же :(

    Вот код:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    
    #define F_CPU 16000000UL
     
     
    #include "avr/io.h"
    #include "avr/iom162.h"
    #include "avr/common.h"
    #include 
    #include "MILib.h"
     
     
     
     
    void ledOn(uint8_t pin);
    void ledOff(uint8_t pin);
     
     
    void ledOn(uint8_t pin)
    {
    	sbi(PORTA, pin);
    }
    void ledOff(uint8_t pin)
    {
    	cbi(PORTA, pin);
    }
    static uint8_t inv;
     
    // Define baud rate
    #define USART_BAUD 115200UL  //115200UL 
    #define USART_UBBR_VALUE ((F_CPU/16/USART_BAUD)) 
     
     
    void USART_vInit(void)
    {
    	// Set baud rate
    	UBRR0H = (uint8_t)(USART_UBBR_VALUE&gt;&gt;8);
    	UBRR0L = (uint8_t)USART_UBBR_VALUE;
    	UCSR0A = (0 << FE0)|(0 << DOR0)|(0 << UPE0)|(0 << U2X0)| BV(UCSZ01);
    	// Set frame format to 8 data bits, no parity, 1 stop bit
    	UCSR0C = BV(URSEL0)|BV(UCSZ00)|BV(UCSZ01)|(0 << UMSEL0);
     
    	// Enable receiver and transmitter. Enable enteroups
    	UCSR0B = BV(RXEN0)|BV(TXEN0)|BV(RXCIE0)|BV(TXCIE0);
    }
     
     
    SIGNAL(SIG_USART0_DATA) 
    {
    	if (IsReadEnabled()) 
    	{
    	 	UDR0 = GetData();
    		ledOn(PA2);
    	}else {
    		cbi(UCSR0B, UDRIE0);
    		ledOff(PA2);
    	}
    }
    SIGNAL(SIG_USART0_RECV) 
    {
    	uint8_t rec = UDR0;
    	if ((UCSR0A & ((1 << FE0) | (1 << DOR0) | (1 <<UPE0)))) 
    	{
    		return;
    	} 
    	if (IsWriteEnabled()) AddData(rec);
    	if (rec == 'm'){
    		ledOn(PA0);
    	} else if(rec == 'a'){
    		ledOn(PA1);
    	}else if(rec == 'x'){
    		ledOn(PA2);
    	}
     
    }
    SIGNAL(SIG_USART0_TRANS) 
    {
     
    }
     
    int main()
    {
    	inv = 0;
    	BtnSwitchInit();
     
     
    	USART_vInit();
    	sei();
    	sbi(TIMSK, TOIE1);
    	//sbi(TIMSK, OCIE1A);
    	//sbi(TIMSK, OCIE1B);
    	sbi(TCCR1A, WGM10);
    	sbi(TCCR1B, WGM12);
     
     
    	TCNT1 = 1;
    	//OCR1A = 178;
    	//OCR1B = 150;
     
    	sbi(TCCR1B, CS10);
    	//sbi(TCCR1B, CS10);
    	sbi(DDRA, PA0);
    	sbi(DDRA, PA1);
    	sbi(DDRA, PA2);
    	cbi(DDRC, PC0);
    	sbi(PORTC, PC0);
     
     
    	if (IsWriteEnabled()) AddData('h');
    	if (IsWriteEnabled()) AddData('e');
    	if (IsWriteEnabled()) AddData('l');
    	if (IsWriteEnabled()) AddData('l');
    	if (IsWriteEnabled()) AddData('o');
    	if (IsWriteEnabled()) AddData(' ');
    	if (IsWriteEnabled()) AddData('1');
    	if (IsWriteEnabled()) AddData('2');
    	if (IsWriteEnabled()) AddData('3');
    	if (IsWriteEnabled()) AddData('4');
    	if (IsWriteEnabled()) AddData('5');
     
    	sbi(UCSR0B, UDRIE0);
     
    	while(1);
     
     
     	return 0;
    }
     
     
    SIGNAL(SIG_OVERFLOW1)
    {
    	if (IsBtnPressed(!isbi(PINC, PC0))){
    		if (inv  3) {
    			inv = 0;
    			ledOff(PA0);
    			ledOff(PA1);
    			ledOff(PA2);
    		}
    	} 
    }
     
    реализация буфера:
    #define BUF_SIZE    32  
    #define BUF_MASK    (BUF_SIZE-1)
     
    uint8_t idxIN, idxOUT;
    char buffer [BUF_SIZE]; 
     
    void AddData(uint8_t value)
    {
    	//ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    	{
    	buffer[idxIN++] = value;
    	idxIN &amp;= BUF_MASK;
    	sbi(UCSR0B, UDRIE0);
    	}
    }
    uint8_t GetData()
    {
    	uint8_t value;
    	//ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    	{
    	value = buffer[idxOUT++];
    	idxOUT &amp;= BUF_MASK;
    	}
    	return value;
    }
     
    bool IsReadEnabled()
    {
    	//ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    	{
    	if (idxIN != idxOUT)
    		return true;
    	}	
    	return false;
    }
    bool IsWriteEnabled()
    {
    	//ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    	{
        if (idxIN &gt;= idxOUT)
    	{
            if ((idxIN - idxOUT) &lt; BUF_MASK)
    			return true;
    	}
        else
    	{
            if (((BUF_SIZE - idxOUT) + idxIN) &lt; BUF_MASK)
    			return true;
    	}
    	}
    	return false;
    }

    В итоге: При запуске устройства — исправно приходят в терминал надпись Hello 12345
    если я через терминал отправлю ОДИН! символ.. он исправно придет.
    Если отправлю несколько — приходит белиберда. Если внутри AddData поставю таймаут на 20-30 мс, то и несколько символов приходят отлично. В чем может быть загвоздка? почему если налету получать символ и пересылать эхом его назад — УАРТ не успевает? :(

    Спасибо. 3-й день бьюсь (

    1. У тебя, как понял, буффер который выгребается и заполняется по прерываниям.

      У тебя не может буффер схлопываться? Т.е. следующий байт пытается в него записаться когда он еще не отправлен?

      Проблема тут не аппаратная. Если сделать на ассемблере то код вида

      1
      2
      
      IN R16,UDR
      OUT UDR,R16

      в обработчике по приему дает отличное эхо и ничего не цепляется, несмотря на то что тут всего две команды и выполняется очень быстро.

      1. А ты про какой буфер? УАртовский или мой (в коде buffer)?
        Он циклический.. в данный момент 64 байта размер. на Recive я записываю в массив(буффер) байт из UDR. и включаю выключенное прерывание UDRIE. Соотв на прерывание пустого буфера байт может без проблем достаться.
        В коде видно ATOMIC блоки… я пробовал и с ними.. чтоб небыло конфликтов.. все одно и то же.
        Если я (как видно в коде) при инициализации заполню свой буфер батами, потом запущу прерывания, то данные Hello 12345 уйдут без ошибок..но проблема выходи когда мы читаем байт — пишем в бкфер.. на следущем прерывании пустого буфера данных я считываю из массива свой байт и помещаю в UDR. и какая же тут может быть ошибка? помещение в массив происходит в прерывании приема.. я правильно понимаю что оно не прервется другим прерыванием пока не закончится? SIGNAL вроде бы так работает.. соотв. байт в мой массив запишется 100%.. и точно так же при отправке… в любом случае я пробовал в прерываниях писать cli() , sei()

          1. Блин, только на работу причалил. Проект дома. Писал в AVR studio. Собственно код в первом посте — это все. По инклуднику можно увидеть что это ATMega162
            проц работает на 16 Мгц(проверено , скорость такая).
            Ед. что можно тут удалить это обработчик прерывания таймера и BtnSwitchInit(); Эта часть отдельная… она ловит нажатие кнопки с обработкой пржинистости :) механического контакта..
            а дефаны эти юзал
            #define BV(x) (1<<x)
            #define sbi(port, bit) (port) |= (1 << (bit))
            #define cbi(port, bit) (port) &= ~(1 << (bit))

  80. Люди помогите.
    Вот код, где ошибка??? Чт оне так???
    Весь вечер убил :(

    .include = «m8515def.inc»
    ; ATmega8535
    ; 8MHz

    .DSEG

    .equ XTAL = 8000000
    .equ baudrate = 9600
    .equ bauddivider = XTAL/(16*baudrate)-1
    .equ CS = PB4
    .equ MOSI = PB5
    .equ MISO = PB6
    .equ SCK = PB7
    .equ LabelSTART = 0x60
    .equ LabelEND = 0x73

    .CSEG

    ;Èíèöèàëèçàöèÿ ÌÊ

    .ORG 0x000
    RJMP Reset ;Ïðîïóñêàåì âåêòîðà è ïåðåõîäèì íà èíèöèàëèçàöèþ ñòåêà
    .ORG $001
    RETI ; (INT0) External Interrupt Request 0
    .ORG $002
    RETI ; (INT1) External Interrupt Request 1
    .ORG $003
    RETI ; (TIMER2 COMP) Timer/Counter2 Compare Match
    .ORG $004
    RETI ; (TIMER2 OVF) Timer/Counter2 Overflow
    .ORG $005
    RETI ; (TIMER1 CAPT) Timer/Counter1 Capture Event
    .ORG $006
    RETI ; (TIMER1 COMPA) Timer/Counter1 Compare Match A
    .ORG $007
    RETI ; (TIMER1 COMPB) Timer/Counter1 Compare Match B
    .ORG $008
    RETI ; (TIMER1 OVF) Timer/Counter1 Overflow
    .ORG $009
    RETI ; (TIMER0 OVF) Timer/Counter0 Overflow
    .ORG $00A
    RETI ; (SPI,STC) Serial Transfer Complete
    .ORG $00B
    RETI ; (USART,RXC) USART, Rx Complete
    .ORG $00C
    RETI ; (USART,UDRE) USART Data Register Empty
    .ORG $00D
    RETI ; (USART,TXC) USART, Tx Complete
    .ORG $00E
    RETI ; (ADC) ADC Conversion Complete
    .ORG $00F
    RETI ; (EE_RDY) EEPROM Ready
    .ORG $010
    RETI ; (ANA_COMP) Analog Comparator
    .ORG $012
    RETI ; (SPM_RDY) Store Program Memory Ready
    .ORG INT_VECTORS_SIZE

    ; Êîíåö òàáëèöû ïðåðûâàíèé

    Reset:

    ;Î÷èñòêà ÎÇÓ

    RAM_Flush: LDI ZL,Low(SRAM_START)
    LDI ZH,High(SRAM_START)
    CLR R16
    Flush: ST Z+,R16
    CPI ZH,High(RAMEND)
    BRNE Flush
    CPI ZL,Low(RAMEND)
    BRNE Flush

    ;Î÷èñòêà âñåõ ðåãèñòðîâ

    CLR R0
    CLR R1
    CLR R2
    CLR R3
    CLR R4
    CLR R5
    CLR R6
    CLR R7
    CLR R8
    CLR R9
    CLR R10
    CLR R11
    CLR R12
    CLR R13
    CLR R14
    CLR R15
    CLR R16
    CLR R17
    CLR R18
    CLR R19
    CLR R20
    CLR R21
    CLR R22
    CLR R23
    CLR R24
    CLR R25
    CLR R26
    CLR R27
    CLR R28
    CLR R29
    CLR R30
    CLR R31

    ;Èíèöèàëèçàöèÿ ñòåêà

    STACK: LDI R16,Low(RAMEND) ; Çàïèñûâàåì äíî ÎÇÓ
    OUT SPL,R16 ; â ìëàäøèé ðåãèñòð
    LDI R16,High(RAMEND) ; Çàïèñûâàåì äíî â ÎÇÓ
    OUT SPH,R16 ; â ñòàðøèé ðåãèñòð
    CLR R16

    ;Çàïèñü ïðèâåòñòâèÿ â SRAM

    Data:

    LDI R16,’C’
    STS 0x60,R16
    LDI R16,’O’
    STS 0x61,R16
    LDI R16,’N’
    STS 0x62,R16
    LDI R16,’T’
    STS 0x63,R16
    LDI R16,’R’
    STS 0x64,R16
    LDI R16,’O’
    STS 0x65,R16
    LDI R16,’L’
    STS 0x66,R16
    LDI R16,’L’
    STS 0x67,R16
    LDI R16,’E’
    STS 0x68,R16
    LDI R16,’R’
    STS 0x69,R16
    LDI R16,’ ‘
    STS 0x6A,R16
    LDI R16,’H’
    STS 0x6B,R16
    LDI R16,’O’
    STS 0x6C,R16
    LDI R16,’N’
    STS 0x6D,R16
    LDI R16,’D’
    STS 0x6E,R16
    LDI R16,’A’
    STS 0x6F,R16
    LDI R16,’-‘
    STS 0x70,R16
    LDI R16,’B’
    STS 0x71,R16
    LDI R16,’U’
    STS 0x72,R16
    LDI R16,’S’
    STS 0x73,R16
    RJMP SENT

    ;Èíèöèàëèçàöèÿ RS-232
    UART_Init:
    ; Set baud rate
    LDI R16, low(bauddivider)
    OUT UBRRL, r16
    LDI R16, high(bauddivider)
    OUT UBRRH, r16
    ;Ïðåðûâàíèÿ çàïðåùåíû, ïðèåì-ïåðåäà÷à ðàçðåøåí.
    LDI r16, (1<<RXEN)|(1<<TXEN)
    OUT UCSRB,r16
    ; Set frame format: 8data, 2stop bit
    LDI r16, (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
    OUT UCSRC,r16
    RET

    ;Ïðîöåäóðà îòïðàâêè áàéòà
    uart_snt: SBIS UCSRA,UDRE ; Ïðîïóñê åñëè íåò ôëàãà ãîòîâíîñòè
    RJMP uart_snt ; æäåì ãîòîâíîñòè — ôëàãà UDRE
    OUT UDR, R17 ; øëåì áàéò!
    RET

    ;Èíèöèàëèçàöèÿ SPI

    spi_init:
    LDI R16, (1<<SPE)|(1<<MSTR)|(1<<CPOL)|(1<<DORD)
    OUT SPCR, R16
    LDI R16, (1<<SCK)|(1<<MOSI)|(1<<CS)|(1<<PB4)
    OUT DDRB, R16;
    SBI PORTB, CS; Óñòàíàâëèâàåì â 1

    RET

    ;Òåïåðü ïîñûëêà ïðèâåòñòâèÿ

    SENT:
    LDI ZL,Low(LabelSTART)
    LDI ZH,High(LabelSTART)

    M1: RCALL uart_init
    LD R17,Z+
    RCALL uart_snt
    CPI ZL,Low(LabelEND)
    BRNE M1
    M2: CPI ZH,High(LabelEND)
    BRNE M2
    RJMP STOP
    STOP: NOP
    RJMP STOP

      1. В том то и дело, что не работает!

        Не посылается байт в UDR.

        Смысл, я так понимаю понятен. Берём байт из оперативки и посылаем в ком-порт.

        В реале не работает. По ощущениям не вызывается подпрограмма uart_snt

        Писал в авр-студии.

  81. Вопрос о USART

    на меге8 пытаюсь принять данные с датчика
    9600Baud, 81N
    9600, 8 bits, no parity, with one stop bit.
    буржуйским языком написано, 9600бод, 8бит, без четности

    кварц 3686400Гц
    вот так конфигурирую USART,
    ldi temp,0;
    out UCSRA,temp;
    ldi temp,0b10010000;
    out UCSRB,temp;
    ldi temp,0b10000000;
    out UCSRC,temp;
    ldi temp,0b10000110;
    out UCSRC,temp;
    ldi temp,23;//23
    out UBRRL,temp;

    принимаю по прерыванию
    в инструкции к датчику сказано что должны идти символы АСКИ. «R»это 0х52 и CR это 0х0D
    вместо них похоже принимается 0х06 и 0x2B
    06 67 64 79 2B блоки такой структуры

    считал ошибки по флагу FE, насчитывает почти при каждом прерывании

    почему это может быть?

      1. подключил другой контроллер, на передачу, с такой же конфигурацией, принимает без проблем

        скорость 9600бод, точно, контроллеры были с разной тактовой, правильно выбрал UBRRL

        я правильно сконфигурировал USART под требования датчика, которые я указал?
        если да, то что то не то с датчиком

        на нем стоит контролер PIC16f676, он может конфликтовать с мегой8?

        буду очень рад дельному совету, ато 2дня бьюсь

              1. С датчика 9600бод точно сигнал

                на контроллере 3 686 400, точно, потому что я соединял его по USART с контролером который работал от внутреннего 1МГц.
                UBRRL=23 для 3 686 400
                UBRRL=6 для 1МГц и прием работал

                1. Если один МК верно передает, а второй верно принимает.

                  А когде у нас датчик то прием не верный.

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

                    1. Датчик, случайно не УЗ дальномер? ;)

                      Кстати, когда ты сказал что там PIC была мысль такая (они так могут), но что то подумал что такой изврат не встречается никогда.

                    2. Ага. Именно с ним. Тоже долго мозг ебал на тему чего же он мне не то шлет. Правда я в терминале смотрел.

                      Написал даже разработчикам. Очень оперативно ответили. Двух часов не прошло :)

                      Извращенцы.

                    3. офтоп конечно, но

                      успешное устройство с этим датчиком получилось?

                    4. Устройство нет, пока экспериментирую. Я для робота его брал, но показания с него адски скачут и чет не особо меня это радует. Думаю как бы их фильтровать.

  82. Добрый день. Di, а нет у тебя проверенного кода для работы с UART? На рабочем примере гораздо проще написать свое, попутно и разобраться, что там к чему. Только на ассемблере нужен, желательно для меги8.
    Еще вопрос, получится у меня связать по ЮАРТ два проца через 2 max232, если один кварцованый (16 мгц), а второй от внутреннего генератора работает? А годится вообще RS232 для применения в заводских условиях?

  83. Мне лучше под Mega 8, но в принципе все-равно. Мне для примера, чтобы свою написать. Единственное, если под другой МК, скажи на что обратить внимание, чем могут отличаться.
    И еще. Казалось бы простой вопрос, но не найду ответа. Везде описывается связь МК с компом по RS232. Там понятно — преобразует компортовские 12в до уровня 5в. Если же два МК соединять через MAX232, то питать их тоже от 5в надо? А в линию 12в она выдаст от своего внутреннего преобразователя? Или лучше взять 2 RS485 ?

    1. Для мега8

      1
      2
      3
      4
      5
      6
      7
      8
      9
      
               .equ XTAL          = MainClock
               .equ baudrate       = 9600
               .equ bauddivider    = XTAL/(16*baudrate)-1
       
               OUTI    UBRRL,low(bauddivider)
               OUTI    UBRRH,high(bauddivider)
               OUTI    UCSRA, 0
               OUTI    UCSRB,(1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)
               OUTI    UCSRC,(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)

      Обычно для всех мег одинаково. Разница может быть в именах регистров (если у меги два уарта, то у них будут номера 0 или 1) и в бите выбора целевого регистра URSEL он может быть, а может и не быть.

    2. Если два контнроллера соединяются УАРТ’оми, то ставить MAX232 не нужно, если они конечно не очень далеко друг от друга расположены. Ели далековато, то тогда для защиты от помех думаю стоит. И то, для бОльших расстояних надо бы подумать про другие сети, типа RS485

  84. Di, спасибо. Я имел ввиду не только это, а кусок кода, где можно разобраться, как данные вносятся в UDR, из него извлекаются. Хочу написать макрос, в который надо было бы передать несколько параметров и не думать потом больше, как там оно все происходит, т.к. далеко не у всех такая светлая голова, как у тебя — забывается. А с макросом все ясно — вот макрос, вот данные. Отправил и забыл, как там оно работает. Так же и с приемом.

    1. А там все просто. Закинуть данные в UDR

      OUTI UDR,0xFF

      например. А забирать по прерываниям
      Т.е. выставляешь обработчик прерывания и там делаешь

      RX_OK:
      cохранение регистров
      IN R17,UDR
      восстановление регистров
      RETI

  85. Прошу пощения, что не по теме — навеяло. Жаль, что на этой странице никаких меток для себя нельзя поставить. Найдешь нужное место, потом туда-сюда — заново искать надо. А страница большая — не так и просто. Да и в АВР Студии добавили бы еще один «верхний прозрачный слой», что бы можно было над текстом прорисовать стрелками переходы. Было бы намного удобнее. Что там эти буржуи до таких простых вещей не додумаются, что ли?

  86. rjmp USART_Receive_Complete ;URXC ;addr=$00b
    rjmp USART_Data_Register_Empty ;UDRE ;addr=$00c
    rjmp USART_Transmit_Complete ;UTXC ;addr=$00d
    ; Инициализация UART
    .dseg
    .equ XTAL = MainClock
    .equ baudrate = 9600
    .equ bauddivider = XTAL/(16*baudrate)-1
    .cseg
    OUTI UBRRL,low(bauddivider)
    OUTI UBRRH,high(bauddivider)
    OUTI UCSRA, 0
    OUTI UCSRB,(1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)
    OUTI UCSRC,(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)
    ;_________________________________________________________
    sei
    ;==========================================
    Start:
    LDI R16,’E’ ; загоняем в регистр код буквы «E»
    RCALL uart_snt
    rjmp Start

    ; бла бла бла куча какого нибудь другого кода.

    ; Процедура отправки байта
    uart_snt: sbis UCSRA,UDRE ; Пропуск если нет флага готовности
    rjmp uart_snt ; ждем готовности — флага UDRE
    out UDR, R16 ; шлем байт!
    ret

    Хе-хе.) Всю ночь разбирался, так и не понял, почему при готовности UDRE не происходит
    перехода на USART_Data_Register_Empty (прерывание) ? Да и отправка байта при частоте 8Мгц занимает 1 млсек в А-студии. Это нормально для 9600 бод? Пытался RXD сбросить в ноль (симитировать приход байта) — тоже никакого прерывания не произошло. Что за фигня такая?
    Если я правильно понял, то механизм работы ЮАРта примерно такой — в основной программе постоянно проверяем готовность UDRE, как только он готов — загружаем в него байт и продолжаем в основной программе крутиться, ждать готовности UDRE?
    А если отправлять нечего, ( готовый UDRE вызывает прерывание), то что же, он постоянно будет прыгать на обработчик?

    1. Ну так ты бы еще разрешил прерывание от UDRE то :) тогда бы и заработало.

      9600 бод это 9600 битов в секунду. Один байт — 10бит Так что как раз 1мс и получается примерно.

      http://easyelectronics.ru/avr-uchebnyj-kurs-rabota-na-preryvaniyax.html

      вот как надо работть с USART на прерываниях.

  87. А будет ли мешать прием и передача данных по UART, генерированию двух ШИМ-ов? Планировалось завести мышь подключенную к АВР и еще принимать сигнал по Инфракрасному порту. Все больше и больше кажеться, что я буду терять кучу данных. И по большей части из за шим, хоть он и занимает очень мало времени…

      1. Я имею ввиду тот эффект который получился в конце статьи про шим. =-) Хотелось уяснить, есть ли вероятность того, что шим не повлияет на целостность получаемых данных

        1. Уарт принимает данные аппаратно, по прерыванию они только забираются из буффера. Т.е. у тебя прерывание должно быть доступно как минимум раз в байт, на текущей скорости уарта.

  88. Помогите пожалуйста! немогу понять почему не работает программа для Atmega16. Суть программы-скорость 4800 бит/с (для кварца 14,318 МГц ),при получении последовательности символов «start» начать непрерывные посылки в порт символа «9».
    вот текст моей программы:
    .include «m16def.inc»

    main:

    ldi r16, 116 ;при 1 в U2X 14318000/(8*(UBRR + 1))=4798
    out UBRRL, r16 ;при UBRR = 372
    ldi r16, 1 ;372 = 256 + 116 = 100000000 + 01110100
    out UBRRH, r16 ;116= 64+32+16+4
    ldi r20, (1<<RXEN)|(1<<TXEN)
    out UCSRB, r20
    ldi r20, (1<<U2X)
    out UCSRA, r20

    priem:

    sbis UCSRA, RXC
    rjmp priem
    in r16, UDR
    cpi r16, 115
    brne priem

    sbis UCSRA, RXC
    rjmp priem
    in r16, UDR
    cpi r16, 116
    brne priem

    sbis UCSRA, RXC
    rjmp priem
    in r16, UDR
    cpi r16, 97
    brne priem

    sbis UCSRA, RXC
    rjmp priem
    in r16, UDR
    cpi r16, 114
    brne priem

    sbis UCSRA, RXC
    rjmp priem
    in r16, UDR
    cpi r16, 116
    brne priem

    SendByte:
    sbis UCSRA, UDRE
    rjmp SendByte
    ldi r16, 9
    out UDR, r16
    rjmp SendByte

    Если слово «start» заменить на букву «s» и переписать с учетом этого программу ,то все работает. Программа писалась в AVR studio, прием и передача осуцествлялись с помощь. программы Hyper Terminal

    1. Ну а сам то не догадываешься? Прогони в отладке авр студии и поподставляй на проверках в R16 твое значение. Сам поймешь что дальше первого сравнения у тебя программа не полезет никогда.

      1. Кажется понял,у меня в программе ,не дожидаясь посылки следующего за первым слова, программа переходила на метку priem. я это исправил,сделав у каждой буквы свою метку:

        priem:
        sbis UCSRA, RXC
        rjmp priem
        in r16, UDR
        cpi r16, 115
        brne priem

        priem2:
        sbis UCSRA, RXC
        rjmp priem2
        in r16, UDR
        cpi r16, 116
        brne priem2

        и так далее. спасибо за помощь! проведу испытания на железе

  89. >>Самый прикол тут — это бит селектора URSEL дело в том, что по неизвестной причине
    >>создаетели решили сэкономить байт адреса и разместили регистры UCSRC и UBRRH в
    >>одной адресном пространстве.

    Я думаю причина понятна. Заняты все 0x3F регистров и чтобы не вылезать за область работы out, они решили объединить самые ненужные, по их мнению, регистры :)

  90. Добавлю свои 2 копейки.
    мега8, 4 мгц внутренний генератор. инициализирую как тут в примере, 9600,8-нет-1.
    передаю … в терминале — мусор. экспериментирую, ставлю 7бит — О! работает!
    Во, думаю, обманули, 7 бит а не 8 получается. Смотрю в даташит, в студии на битики — да нет, все как надо. Экспериментирую со скоростью (в терминале) — соответственно посылка уменьшается или увеличивается, но мусор. С передачей примерно та же фигня, но иногда срабатывает на 8 бит, иногда на 7 иногда вообще никак. Думаю, точно со скоростью косяк — пересчитал формулу — не обманывает Ди, фузы-точно на 4 мгц, благо когда шьешь через дракона и студию, ошибиться сложно. И тут я припоминаю что генератор-то калиброваный, и но вроде как вручную. Читаю даташит — точно, автоматом загружается значение калибрации для 1 Мгц (Вот уроды, не могли сразу нужное значение загружать?) Считал значение для 4 Мгц, воткнул в начало проги
    ldi r16, 0xa7 ;Это значение считано из МК
    out osccal,r16
    И все! глюки исчзли! 8-нет-1! и прием, и передача.
    Так что, уважаемый читатель, если последняя надежда настроить УАРТ умерла, все настройки перепробованы, уже 3 часа ночи, и т.д. — попробуй откалибровать кристалл!

  91. Здраствуйте. Есть у меня одна проблемка: ратотаю с 64й мегой, суть программы в том что она после инициализации ждет приема символоа с компа, отправляет ему ответ, молв «все ОК» и переходит к дальнейшему выполнению своей работы. Проблема в том что после каждого приема прога уходит на прерывание, записывает в буфер и выходит из него в … RESET! Притом косяк этот только на 64й появился, рядом валяется 16я мега на которой этот код вдоль и поперек обкатан, а тут на 64ю перевел и пошло-поехало. Что это может быть?? Может ли это быть косяк платы или всеже прога?
    (Если надо выложу код)

    1. Возможные косяки:

      1) криво инициалзировал стек.
      2) Кривая таблица векторов прерываний.

      Код на асме? Если на асме, то на мегу 64 его так просто копипастой не перебросишь, надо ряд адресов подправить. В частности таблицу векторов.

        1. И инициализация стека вот:

          ; PROGRAMM INIT ===========================================

          Reset: OUTI UDR0,’0′

          OUTI SPL,low(RAMEND)
          OUTI SPH,High(RAMEND)

          .include «init_uart.asm» ; Инициализируем UART
          SEI ; Разрешаем прерывания

          ; MAIN PROGRAMM +++++++++++++++++++++++++++++++++++++++++++

  92. Кстати, вот только сейчас аццкий глюк нарисовался %) У меня после запуска и инициализации всех портов, уартов и прочего загорается контрольный светодиод, так вот он теперь вообще то сразу загорается то через секунд 10, то вообще не загорается. Это что еще может быть?

    1. Да что угодно, вплоть до срыва стека из-за косяка в программе.

      Надо понять где глюк в железе или в софте. Софт исключить проще всего. Пишешь программу в которой точно понятно, что контроллер ребутнулся, а потом делаешь экшены которые гарантированно работают и как бы орудуют периферией. Например щелкают релюшками просто так.

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

      1. Я уже так сделал, понавесил светодиодов на все порты и написал тупую щелкалку. Нашел косяк с пином PD0 при инициализации которого контроллеру взрывало мозг, и точно установил что срыв на Reset идет после выхода их обработчика прерывания по приему. Тока непойму никак что может провацировать срыв на выходе из обработчика?

        Кстати не в нем ли проблема :

        RX_OK:
        PUSH R16
        IN R16,SREG
        PUSH R16

        IN uBUF,UDR0

        POP R16
        OUT SREG,R16
        POP R16
        RETI

        1. uBUF это R16? Если нет, то его тоже надо в стек прятать. Или он нигде не используется кроме как буфер для данных из уарта?

          А нет ли у тебя замыкания между PD0 и RESET? А то может инитишь PD0 на что нибудь, а у тебя ресет происходит. Собака включена?

      1. Собаку не включал.

        uBUF это регистр чисто для вытаскивания из уарта, большой буфер просто не нужен.

        PD0 идет рядом с XTAL1. Но я уже забил его инитить а глюк всеравно остался.

        Вот код для теста накидал (с «мигалкой») на которой у меня этот глюк очень четко проявляется.

        В процессе работы он присылает ы терминал «+» после инициализации и потом ы цикле шлет вот такую ерунду «0о0о0о0о». Как- только отсылаю какой нибуть символ снова вылазит «+».

        Вот
        http://easyelectronics.ru/repository.php?act=view&id=15

        1. Вот лажовый кусок:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          
          RX_OK: 
          	PUSH	R16		
          	IN	R16,SREG	
          	PUSH	R16		
           
          	IN	uBUF,UDR0
           
          	POP	R16
          	OUT	SREG,R16
          	POP	R16
           
          	CLRB	UCSR0A,7,R16
           
          	RETI

          Ты сохраняешь срег, а потом восстанавливаешь его. И зачем? Для того, чтобы макросом
          CLRB UCSR0A,7,R16 зафачить регистр R16 и еще флаги (а в макросе этом, емнип, они не могут не юзаться, т.к. сьем бита идет через логические операции.

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

          Но одно непонятно — какого хера оно в ресет уходит. Ну зафачит цикл, но не ресет же. Где то мы прозевали какую то хуйню. Дай ка мне на мыло проект целиком. dihalt@dihalt.ru интересно где же лажа :)

        2. Погонял я тут вот такой код обработчика:

          RX_OK:
          OUTUR R16

          PUSH R16
          IN R16,SREG
          PUSH R16

          IN uBUF,UDR0

          POP R16
          OUT SREG,R16
          POP R16

          OUTUR R16
          RETI

          Так вот на входе, перед сохранением в стек, R16 какоето число а на выходе всегда 255!!! и после этого прога уходит в RESET. Я понимаю что это срыв стека, но не понимаю изз-а чего ему быть в этом месте, именно в обработчике.

          *Макросом OUTUR R16 я отправлял в терминалку, глюк есть что с ним что без него.

            1. Отправил проект на мыло. Но вот копчиком чувствую что лажа гдето в стеке закралась, разве что-то еще может такие глюки плодить. Что интересно симулятор прогу прокручивает и никакой лажи и в ппомине не видно. Скоро в стену бится начну%)

              1. Знаешь в чем еще может быть лажа — совместимость с ATmega103. Там есть fuse бит и изначально он стоит в режиме 0, т.е. включен режим совместимости с ATmega103 а там другая адресация, нет мемори мапед регистров. В результате получается странная херня — в симуляторе работает, т.к. студия думает, что мы сидим на меге64, а в железе глючит, т.к. фактически мы работаем на 103 меге :)

                1. А это идея, надо поковырять. Я тут еще нарыл инфу о том что RamEnd для ATmega64 = 0x10FF, а это явно больше чем 0xFF.
                  Стек я инициализирую как в 16й,

                  OUTI SPL,low(RAMEND)
                  OUTI SPH,High(RAMEND)

                  может тут по другому надо, блин, смущает меня меня это 0x10FF…

                  1. Да ерунда это. Какой бы ты рамэнд не взял, оно нормально войдет. т.к. по любому меньше чем FFFF и все ок. Т.е. двубайтное. Не могу понять почему у тебя стек срывает и вообще от чего ребут идет. Даже если и работает на меге103 (щас в симуляторе запустил на 103), то от этого может периферия отпасть, память меньше чем в 64, но на возвраты из стека никакой разницы!!!

                    1. Я те в мыло тоже кой чего скинул. Позырь, там ты POP забыл в обработчике.

                      И еще, у тебя срывает только в железе? А в симуляции все ок? Т.к. у меня в симе все шикарно.

                      ВЫкини нахер все макросы. Оставь нативный ручной вывод, А то там у тебя столько прогрузов стека ,может забыл чего. И выброси все кроме закидывания оО в уарт (причем вручную, а не макросом). Задержку можешь оставить. Ну и обработчик прерывания естественно. Если и ЭТО будет глючить, то ищи утечку по плате.

                    2. Бля, говорят же работает — не трогай. Не открыл бы дизассемблер не понял бы.

                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      
                      ---- MEGA64_DCSU-1.asm ----------------------------------------------------------------------------
                      14:       		OUTI 	SPL,low(RAMEND) 		; Первым делом инициализируем стек
                      +00000046:   930F        PUSH      R16            Push register on stack
                      +00000047:   EF0F        SER       R16            Set Register
                      +00000048:   BF0D        OUT       0x3D,R16       Out to I/O location
                      +00000049:   910F        POP       R16            Pop register from stack
                      15:       		OUTI	SPH,High(RAMEND)
                      +0000004A:   930F        PUSH      R16            Push register on stack
                      +0000004B:   E100        LDI       R16,0x10       Load immediate
                      +0000004C:   BF0E        OUT       0x3E,R16       Out to I/O location
                      +0000004D:   910F        POP       R16            Pop register from stack

                      Понял в чем жопа? ;)

                    3. На макрос посмотри и на то ЧТО он делает!

                      Ты грузишь данные через него в Stack Pointer. Но как? ПРедварительно сохранив R16 в стек! Но стек то еще не инициализирован. В результате со указателем стека получается интересная метамарфоза :) Ты как бы его грузишь и одновременно двигаешь туда сюда командами PUSH/POP причем побайтно. В результатет чудом получился интересный замут — значенение не 10FF, а 1100 :) Разница всего в один байт!

                      Вроде бы в конце памяти, но на байт дальше. Итог — данные проваливаются в никуда. Но ты этого пока не замечаешь, т.к. ничего критичного там нет.

                      Но почему все работает в студии? А потому, что у тебя там до жопы макросов и во всех из них юзается дофига PUSH и POP и когда ты вручную вызывал прерывание, то в всегда это было тогда, когда выполнился хотя бы один PUSH и указатель стека вылезал в адекватную область. Но стоит выполниться прерыванию в том месте, где PUSH не прошел — как все рушится, т.к. адрес сохраняется в несуществующую область памяти. Возникает вероятностный глюк.

                      Но это теория и второй скрытый баг, у тебя же все вылетало нахер изза другого.
                      Бит совместимости с ATmega103. Он по дефолту включен и ты работаешь не с мегой64, а с мегой 103, а у ней оперативка короче на 96 байт. Таким образом, указав рамэнд от меги64 ты указал не просто на байт дальше, а на целых 96 байт дальше. В результате у тебя стек не просто плавает на границе адекватных адресов, а оказалс вообще за границей памяти. В макросах ты этого бы и не заметил — там пофигу. Но вот на первом же адресе возврата у тебя наступает армагеддон :)

                    4. Блин, заработало-же))))

                      Убрал я эту соместимость нахер и инициализировал стек вручную через LDI n OUT, и все встало на свои места, никаких долбаных ребутов.

                      СПАСИБО ОГРОМНОЕ!!! А то я б тут всю ночь сидел, а в понедельник железяку уже отдавать нада)

                1. Не, не помагло((
                  Я вот такую байду в код загнал тупа посреди цикла:

                  LDI R16,’E’
                  OUTUR R16 ;отправка
                  PUSH R16
                  POP R16
                  OUTUR R16 ;отправка

                  И получаю на терминал что сначало по терминалке приходит буква «Е», потом она лезит в стек, потом вылазит оттуда и приходит уже «!» Я фигею…

  93. ; забираю данные с АЦП
    IN R16,ADCL
    IN R17,ADCH
    ; пишу в УАРТ младший байт (в R16)
    RCALL UART_SNT
    MOV R16,R17
    ; пишу в УАРТ старший байт (был в R17)
    RCALL UART_SNT

    если в старшем байте после преобразования оказывается 0, то терминал на другом конце линии УАРТ принимает только 1 число, если же не 0, то отображаются 2 числа.

  94. Я тут новенький, поэтому, во-первых хочу сказать огромное спасибо за этот сайт, действительно, здесь можно научиться программировать МК с нуля (как это происходит у меня:), а во вторых — такой вопрос к уважаемому DI HALT:
    учусь работать с UART на основе этой статьи, застрял на работе с ним через буфер
    http://easyelectronics.ru/repository.php?act=view&id=17
    соответственно при запуске в терминал сыпятся «Hello Interrupt Request!!! «, если при этом ввести что-нибудь в терминале и приостановить выполнение (Ctrl+F5) — в RAM будут введённые данные, как им и положено, но если вознобновить выполнение (F5) и снова приостановить — в RAM на месте прежде введённых байтов — нули, непонятно, почему перезаписывается нулями буфер ввода???

    1. P.S. кстати, стирается не только то что было помещено в буфер, если просто забить в произвольно месте байты в RAM а потом запустить и приостановить программу — на месте данных — нули, сохраняется только SP и первые 2 байта указателя на строку o_O сломал себе мозг….

      1. Пишу подробнее:)
        терминал юзаю wTerm, железо реальное;) отладка через jtag (программатор какой-то olimex)….
        пока сидел разбирался, начало кое-что проясняться:) если посмотришь код, есть у меня обработчик прерывания, почти полностью скопированный с тебя:) ставлю я брякпойнт на RETI из этого прерывания и смотрю на SP, почти всегда у него значение на выходе 10FD, но иногда по непонятным мне пока причинам там оказывается 1100, ну и соответственно у меня прога начинается с org 0000:) всё остальное — следствие — т.е. чистится по-новой память, инициализируется уарт и т.д. почему срывается стек понять не могу, ведь выполняется одна и та же процедура обработки o_O
        Прошу прощения за сумбурное изложение, просто совсем уже убился я об этот стек….

        1. Чето сайт атмела лежит. Не могу шит на 90кан128 качнуть. Позырь фузы, там нет никаких гадских режимов совместимости с чем нибудь. А то у меги128 есть, например, бит совместимости с мегой103 (то же самое почти, но памяти меньше) и по дефолту, что самая жопа, этот фуз включен. И получается что стек пробивает, т.к. в реале он адресуется не туда.

  95. Всем привет!

    Пробую работать с UART на ATtiny2313. Простой пример — вывод строки.
    Выдает белиберду — в некоторых битах неточности.
    Попробовал поменять делитель bauddivider — вместо стандартного 51 для 8Мгц и 9600 — пробовал немного сместить. На числе 55 — стало выдавать нормально.
    Схема БЕЗ кварца, работает на внутреннем генераторе 8МГц.

    Работало пару дней, теперь перестало. Опять белиберда. Подбирать коэффициенты уже устал.
    Это так действительно должно быть, что без кварца не получится?
    Или можно как-то оптимизировать — снизить частоту процессора, снизить скорость передачи?
    Кварц некуда впихивать, все ноги заняты.

            1. Косяк был в «равенстве» регистров UBRRH и UCSRC. Хотя в даташите НИЧЕГО по этому поводу не написано, и бит UCSEL в даташите на ATtiny2313 отсутствует.
              При инициализации порта и настройки 8N1 в регистре UCSRC одновременно записывался и UBRRH, и настройка скорости сбивалась.

                1. Адреса у них разные, да. Но реально, даже при симуляции в AVR Studio при записи в UCSRS любого бита, либо целиком — все так же копируется в UBRRH. Был бы чип «левый» — еще можно понять, но ведь и Студио ТАК показывает!

                    1. действительно в AVR Studio при записи в UCSRS изменяется UBRRH,
                      если порядок поменять выглядит вроде нормально. Это ошибка AVR Studio или в чипе тоже?

                    2. действительно в AVR Studio при записи в UCSRS изменяется UBRRH,
                      если порядок поменять выглядит вроде нормально. Это ошибка AVR Studio или в чипе тоже? Чип ставил mega48.

                    3. Надо проверять. ВОзможно гон студии. ТАм было что то подобное. В мега48 точно нет бита выбора регистра?

  96. я сделал первый способ на атмеге32
    Main: RCALL uart_rcv ; Ждем байта

    INC R16 ; Делаем с ним что-то

    RCALL uart_snt ; Отправляем обратно.

    JMP Main

    но обратно в терминал возвращается значение большее не на 1 а на 129 (0х81)

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

    1. Возможно скорости просто не совпадают. Попробуй закоментировать INC и должно возвращать то же самое, но этого не будет :) Разберись с тем на какой частоте работает МК, на какую частоту настроен UART и терминал.

  97. Причем с самим протоколом можно не заморачиваться — все реализовано аппаратно. Разве что захочется завести второй UART, тогда придется делать его программно.
    А что будет, если на одной стороне выставить одну скорость, а на другой — другую. Эти устройства смогут договориться?

      1. Тогда еще один глупый вопрос. Помню давно когда пользовался модемом, в настройках COM порта менял baudrate и это ни на что не влияло. Как модем подстроился под изменившийся baudrate? Меня этот вопрос давно мучал =)

        1. Ну есть алгоритмы высчитывания бодрейта. Особенно если знаешь что тебе пришлют. Те же ARM контроллеры имеют уарт с определением бодрейта. ТАк что алгоритмы то есть. С другой стороны зачем грузить зря процессор ненужными вычислениями если можно сразу выставить правильно.

            1. Наверное. Главное поймать несколько байт и их проанализировать. Модему проще, т.к. ему в основном идет первая команада вида АТ… и уже зная, что первый байт «А» можно вычислить,что у нас за частота, сколько стартов, стопов, какая четность и тыды.

  98. подскажите пожалуйста, целый день убил сегодня.
    код ниже, по идее при оправке по USARTу через терминал(терминал v1.9b) должен возвращаться символ обратно а что бы не оправлял возвращается вот такая ботва

    гыде туплю))), Спасибо

    .include «tn2313def.inc»

    .MACRO outi
    LDI R20,@1
    OUT @0,R20
    .ENDMACRO

    .org 0
    start: rjmp reset
    .org 7
    rjmp prin

    reset:
    sbi ddrd, 5 ;мигает
    outi tccr0a, 1<<com0b0 ;
    outi tccr0b, 1<<cs00|1<<cs02 ; диод

    outi ubrrl, 207 ;2400
    outi ucsrb, 1<<rxen|1<<txen|1<<rxcie ; разр. прием передачу, прерывание-прием завершен
    sei

    cicle:
    rjmp cicle

    prin:
    outi tccr0b, 1<<foc0b ; вкл. выкл. диод
    in r16, udr
    inc r16
    nop
    out udr, r16
    reti

      1. добавил ,картина не изменилась, схема собрана на макетке мож наводки там или еще что, потому что прога работает в принципе как положено при каждой отправке символа диод меняет свое состояние, а вот символ отправленный не возвращается.
        А кодировку в терминалке ни как менять не надо? я отсылаю просто еденичку «1»
        она вроде как и должна вернуться???

  99. Вопрос 1.
    В многопроцессорном режиме USART (один мастер и несколько подчиненных),
    при передаче от подчиненного к мастеру, должны ли быть отключены передатчики остальных ведомых? Т.е. не нарушится ли корректность режима шины?
    Вопрос 2.
    В том же режиме. Насколько велика нагрузочная способность передатчика (мастера), если у нас 20-30 ведомых? Может какой-то буфер надо городить?

  100. Дойдя до ручки, и устав биться головой об стену я решил все-таки написать. Особенно после того как прочитал главу учебного курса « Передача данных через UART».
    Я пытаюсь реализовать девайс на 2313 подключенный к ПК и выполняющие «простые движения». Таймер 0, каждую секунду пихает в порт запрос АА. На компьютере работает сервис который приняв АА понимает что устройство подключено отправляет А1, на что девайс должен вернуть состояние пина 2 порта D. Если 1 то отправляем АВ если 0 то АС. После этого компьютер посылает А2 и девайс должен вернуть 4 байта температуры. Все это время таймер отключен. После того как все выплюнуто в порт, таймер включается и через секунду снова посылает байт присутствия АА. Все просто и понятно когда проходит один прогон. А вот на втором заходе хрень получается. Я залогировал данные в файле log.txt.
    Одним словом после первого измерения идет 3 запроса 3 ответа на измерение пина и 3 запроса на температуру. Причем пачкой. Как будто где-то что-то стояло в очереди и вышло наружу. И тут же в порт пошло 2 температуры сразу. Дальше больше.
    Прочитал в ДШ что нужно с осторожностью использовать команды sbic sbis. Вроде они влияют на буфер FIFO. А где этот буфер я так и не понял, тем более как они на него влияют. Может быть здесь собака порылась? А как мне без этих команд считать состояние нужной ноги… нужны они мне.
    Хотя, ДАЖЕ, или скорее всего я напортачил с УАРТ. Я использую только одно прерывание USART_RXC. Получаю А1 или А2 проверяю и выпадаю из него. Таймер тоже на прерывании. Считает секунду – останавливается, посылает АА, измеряет температуру и снова запускается.
    Что можете посоветовать в моем случае? Использовать прерывание на передачу? Задать буфер? Я уже совсем заплутал.

    1. При чем тут UART вообще? У тебя один байт уходит нормально? Если да, то косяк в кривом алгоритме. Буфера у уарта нет, точнее есть, но он всего на один байт так что за раз можно отправить максимум два байта. Если делать это быстрей чем уарт может прожевать, то будет лажа.

      sbic/sbis это команды ветвления. Ни на что кроме порядка выполнения кода они не влияют. И уж тем более не влияют на периферию.

      Скорей всего у тебя криво организована программа и где то либо срыв стека, либо просто логика идет не так как ты задумал.

    1. Сразу вижу два глобальных косяка — у тебя в прерываниях регистры не сохраняются в стеке и не достаются оттуда при выходе. Причем поганятся не только флаги, но и temp через которое у тебя все сделано. Естественно логика рушится мгновенно.

  101. здравствуйте уважаемые пользователи сайта, прошу помощи…написал программу в CVAVR, контроллер атмега 8535,частота 8Мгц не получается с UARTом. По прерыванию от UART, в зависимости от посланного символа, загорается светодиод…все хорошо работает, но когда через UART обратно посылает какое либо сообщение светодиоды гаснут((настройки все верны и перепроверенны раз на 50…в чем может быть причина??проверяю работоспособность в Proteuse сигналы нормально принимаются.

    interrupt [USART_RXC] void usart_rx_isr(void)
    {
    u=UDR;
    if(u==’1′)PORTD.2=1;
    if(u==’2′)PORTD.3=1;
    if(u==’3′) putchar(‘y’);
    }
    обработчик прерывания..пробывал через switch, тоже самое…отправлял через printf тот же результат…надеюсь кто нибуть поможет. Заранее спасибо.

        1. попробывал этуже программу в AVR Studio тажа ситуация…грешил на регистры и стек..почитал в описании программы CVAVR что при прерывании все регистры и стек сохраняются автоматически и потом обратно загружаются…может проблема в том что в прерывании по приему происходит выдача символа…пробывал разные функциии и способы выдачи сообщения ничего…есть ли книга по AVR Studio? если есть такая скажите ссылку)

      1. нашел вроде бы причину в моей проблеме….у меня почему по выдаче сообщения обратно по UART стек обнуляется и снова происходит инициализация МК, заново происходит настройка портов и т.д,..думаю причина в этом…не знаешь причина сброса стека???

        1. У тебя просто сброс контроллера. Причин может быть много. Например вачдог таймер. Если ты его включил. Или кривой вектор в прерывании (если не подключил библиотеку прерываний, но это в winavr не знаю как насчет свавр) еще может быть аппаратная проблема. Например кз или утечка между ТХ и ресетом (у меня один раз так было). Байт выскочил и сам себя сбросил.

          1. тебе лучше программу в каком виде прислать в си или асемблере??и еще у меня почему регистр SREG и GIFR с ума сходят….GIFR устанавливает в 1 все биты а в регистре SREG все биты меняют свое значение..с вектором прерываний вроде бы нормально…заранее спасибо.

  102. Столкнулся с такой проблемой. Беру данные с АЦП и засылаю в UDR, при этом фотодиодами контролирую бит от АЦП. В терминальной программе смотрю на график, а график как энцефалограмма при приступе эпилепсии. Видно что график реагирует на вращение резистором, но вот как четкости добиться не могу вкурить. Код до безобразия простой:
    wait_ADC:

    sbis ADCSRA, ADIF ; Пропустить если флаг конца преобразования АЦП выставлен
    rjmp wait_ADC

    in data, ADCH ; читаем регистр от АЦП
    out PORTB, data ; закидываем в порт значение из data для подачи на светодиоды
    rcall UART_sent

    sbi ADCSRA, ADSC ;запускаем опять АЦП
    rjmp wait_ADC

    UART_sent: ;процедура отправки UARTа
    sbis UCSRA, UDRE ;если не занят то послать data
    rjmp UART_sent
    out UDR, data
    ret

      1. Ага, разобрался — поставил скорость в два разабольше в терминальной проге…и я понял что кварц 4Мгц, который я воткнул с умным видом — не работает, так как по умолчанию на PinBoard МК работает от внутреннего генератора, походу 8Мгц, где то в доке я встречал… А вообще что лучше — внешний или внутренний кварц, при условии что задача простая и особой точности не надо? И есть ли разница по потреблению или ей можно пренебреч?
        Спасибо!

        1. Кварц ставят ради точности или скорости. Без него работает замечательно, а еще меньше боится влажности всякой. Но внутренний RC хорошо так плавает от температуры.

  103. DI HALT такой вопрос, в описании работы UART приведен такой код

    «Например, надо нам отрыгнуть текстовую строку из флеша:»

    Main: NOP ; Любой произвольный код главной программы
    NOP
    NOP

    LDI R17,low(2*String) ; Берем младший байт
    LDI R18,High(2*String) ; Берем старший байт

    STS StrPtr,R17 ; Сохраняем Младший байт
    STS StrPtr+1,R18 ; Сохраняем Сташрий байт

    UD_OK: PUSHF ; Макрос, сохраняющий SREG и R16
    PUSH ZL ; Сохраняем в стеке Z
    PUSH ZH

    LDS ZL,StrPtr ; Грузим указатели в индексные регистры
    LDS ZH,StrPtr+1

    LPM R16,Z+ ; Хватаем байт из флеша. Из нашей строки

    CPI R16,0 ; Если он не ноль, значит читаем дальше
    BREQ STOP_RX ; Иначе останавливаем передачу

    OUT UDR,R16 ; Выдача данных в усарт.

    STS StrPtr,ZL ; Сохраняем указатель
    STS StrPtr+1,ZH ; обратно, в память

    Exit_RX: POP ZH ; Все достаем из стека, выходим.
    POP ZL
    POPF
    RETI

    STOP_RX: LDI R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(0<<UDRIE)
    OUT UCSRB, R16
    RJMP Exit_RX

    Вопрос: если сама строка находиться во флеше то зачем обращаться к стеку и заводить дополнительно указатель который сидит в стеке если можно отправить строку и ниже приведенным кодом?

    Main:
    .
    .
    .
    .
    LDI ZL,LOW(2*String)
    LDI ZH,HIGH(2*String)
    CLR R1

    UD_OK:
    LPM R0,Z+
    CP R0,R1
    BREQ STOP_RX
    OUT UDR,R0
    RETI

    STOP_RX: LDI R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(0<<UDRIE)
    OUT UCSRB, R16
    RJMP Exit_RX

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

      1. Я так понимаю что смысл складирования в стек полученных битов в том, что если обработка полученных данных занимает какоето время, мы не пропустим вновь пришедший байт. Но если скажем нам на УАРТ летят 100 байт информации а у нас в данном случае буффер 10 байт, и мы не знаем что обьем информации которая прилетит сильно превышает обьем буффера, то есть после переполнения буффера нам надо обработать данные, что бы получить новые 10 байт данных, но ведь если этой обработкой этих 10 байт займется какаято функция (B) то функция (А) которая срабатывает на событие (UDR-пуст) не будет паралельно принимать и записывать в стек новые 10 байт ?

        1. Да здесь в примере стек используется для временного хранения содержимого регистровой пары Z на время работы процедуры. В стек приходящие байты лучше вообще не складывать. А выделить изначально в оперативке достаточное место и туда складировать. И при этом заранее знать, какая порция данных будет приходить, чтобы выделить нужный объём. Если будут приходить 100 байт — значит резервировать 100. К тому же в зависимости от скорости UART’а можно успевать обрабатывать данные. Например на скорости 9600 один байт идет за 1 мс где-то.

          1. В данном случае я имел ввиду раздел «Буферизация»
            там где в регистр X записывается адрес 10 битного массива OUT_buff

            LDI XL,low(IN_buff)
            LDI XH,high(IN_buff)

            а потом смещением адреса записываем побитно данные в этот массив. А выше приведенный код я привел для того что бы понять почему нельзя воспользоваться только (LPM R0, Z+) чтобы выстрелить строку.

            1. Ничего не мешает взять и выстрелить строку из флеша — это быстро. Всю обработку делаешь в прерывании. Т.е. прям в обработчике берешь и хаваешь из флеша через указатель. Только указатель на текущий символ надо хранить в памяти. И при входе в прерывание загружать его из памяти в Z.

              Нет, ты можешь его из Z и не доставать. Но при этом у тебя Z становится регистром неприкосновенный (А точнее сразу оба регистра) и попытка что то туда записать черевата тем, что в прерывании твой уарт начнет срыгивать не твою строку, а вообще все что угодно. =)

              1. Это понятно что сам факт использования региста Z, озночает что он неприкосновенный :). Понятно что стек используется для сброса данных. Это и освобождает тотже регистр Z и также полученные байты можно обработать позже. Но все равно получается что перед тем как принимать данные мы должны знать их точное количество (байты). Или же при каждом присланном байте отсылать байт потверждения о получении байта. Тогда можно наверно даже гигами пересылать без опаски потери какогото байта.

              2. Это понятно что сам факт использования региста Z, озночает что он неприкосновенный :). Понятно что стек используется для сброса данных. Это и освобождает тотже регистр Z и также полученные байты можно обработать позже. Но все равно получается что перед тем как принимать данные мы должны знать их точное количество (байты). Или же при каждом присланном байте отсылать байт потверждения что байт обработан и сохранен. Тогда можно наверно даже гигами пересылать без опаски потери какогото байта.

                1. Я тебе про передачу, а ты мне про прием.

                  При приеме мы пишем входящие данные в кольцевой буфер. И нам главное вовремя его обрабатывать. А знать сколько мы получим данных мы должны полюбому. По крайней мере чтобы понимать вообще что это нам идет. Впрочем, данные могут быть потоковыми и тогда главное вовремя их вынимать из кольцевого буфера и обрабатывать. Можно даже не складировать. Буфер позволит на время долгих операций подстраховаться, а уж выгрузить его дело недолгое.

                  1. Кстати функция вызванная прерыванием и функция вызваная программно могут работать параллельно(одновременно)? потому как если нет то обработка буфера займет может и небольшое но какоето время, пока летит 11-ый байт

                    1. Нет конечно. Проц то у нас один. Когда прерывание выполняется все остальное стоит. Поэтому то прерывания делают максимально короткими. Пришел байт? Заскочил, вытащил его из УДР регистра, кинул в буфер в память и выскочил обратно в фоновую программу. Пришел следующий байт? Вытащил, кинул в буфер и так далее.

                      Задача буфера продержать прием без потерь пока проц выполняет что то тяжелое (но редкое иначе у проца ваще времени не будет, а это уже косяк) и не может сразу обработать данные (например вычисляет чего или экран обновляет) зато потом у него будет время прожевать буфер ни на что другое не отвлекаясь.

  104. Возник следующий вопрос. Допустим отправлена от МК к МК строка , являющаяся некоей командой. Но дошла с искажением. Как представляется, слейв должен отправить мастеру об этом сообщение, а тот в ответ должен повторить команду. Вот и вопрос — как это правильно организуется? На приемной стороне понятно, а вот на отправляющей ведь нужно какое-то ожидание подтверждения правильности принятия посылки от слейва..
    И еще. Сколько (минимально) байт должна содержать посылка, чтобы считать ее достаточно надежной. Имеется ввиду искажение в процессе передачи. Например, есть две команды » _A» и » _B». Если в процессе передачи вместо ‘А’ , случайно выскочит ‘B’, то получится нехорошо..

  105. Не нужно ли запрещать прерывания, когда читаем данные из буфера приема? А то вдруг в момент чтения из буфера придет байт и не дочитав из буфера нужно будет ускакать на обработчик прерывания и записать в тот же буфер байт. Указатели записей начала и конца и флаги переполнения не крякнуться?

  106. Почитал статью и решил написать простенький класс уарта для плюсов, по мне, так как если есть стек, не люблю возиться с ОЗУ, а плюсы мне кажется gcc оптимизирует лучше, чем Си (размер всегда меньше у прошивок, лично у меня) Да и компактность, понятность кода лучше. Вот проект для AVR STUDIO 5.0 для attiny2313 http://file.qip.ru/file/wCDDOgNN/USART_simple.html (в 4 студии у меня что-то дебагер gcc не работает, извините), программа по запуску передаёт по уарту всю память флеш (заданы граничные адреса передачи), а потом пересылает принятые данные. В протеусе прошивка работает, в реальности ещё не пробывал. В протеусе класс обеспечивает полный дуплекс 38кбит на частоте камня 8МГц (при этом передачи работают в фоне и можно выполнять основную программу при этом), размер программы получился 600 байт, не знаю насколько это много. Вопрос вот в чём, есть ли умельцы кодинга, которые умеют оптимизировать код С++ ассемблерными вставками, думаю там можно сократить размер вдвое, если переписать прерывания (очень много идёт сохранений/восстановлений регистров). И если получится, отправьте на почту плиз результат, очень интересно узнать :) kefcresta(at)yandex.ru

  107. Доброго времени суток .Частично «накопипастил» ,частично написал такую программу —
    проблема в том что ..2 раза прерывание срабатывает по пустоте UDR.а потом прога тупо зацикливается и невходит в прерывание

    .include «m32def.inc»
    .list
    .include «macro.inc»

    .equ INPUT =0
    .equ XTAL = 8000000
    .equ baudrate = 9600
    .equ bauddivider = XTAL/(8*baudrate)-1
    .def Devais =R26

    .def S =R0
    .def inttemp =R1
    .def ref1 =R2
    .def ref2 =R3

    .def temp =R16

    .def date =R17

    .def system =R19
    .def command =R20

    .def bitcnt =R21

    .def signal =R18
    .def tmp =R22
    .def flag =R23

    .def FCODE =R24 ;

    ;###################################################

    .DSEG ;SRAM memory segment

    ;######################################################################

    .CSEG ;begin of program memory
    .org 0

    ;——————Прерывания ———————————
    .ORG $000 ; (RESET)
    RJMP start
    .org $01C
    RJMP uart_snt
    .org $01A
    RJMP uart_snt
    .org $01E
    RJMP uart_snt
    ;——————— Messages & Constants ———————————

    DATA: .db 0xAA,0xAA,0xAA,0xAA

    ;———————— BEGIN OF PROGRAMM ———————————

    . ; (INT0) External Interrupt Request 0
    ;———————- *** setup part *** ———————————

    Start:
    ;—————————————————

    ;———————————————————
    LDI R16,High(RAMEND)
    OUT SPH,R16

    LDI R16,Low(RAMEND) ; Инициализация стека
    OUT SPL,R16 ; Обязательно!!!

    .equ Byte = 50
    .equ Delay = 20
    ;ldi r16,0xdf ;
    ;out SPL,r16 ;указатель стека

    sbi ACME,ACD ;отключени компаратора О_о (разобраться с ASD)

    ldi r16,0x00 ;
    out DDRB,r16 ;DDRB «DDRB = 0xff»

    ldi r16,0x00 ; «0»
    out PORTB,r16 ;

    ldi r16,0x00 ;
    out DDRC,r16 ;

    ldi r16,0x00 ; «0»
    out PORTC,r16 ;

    ldi r16,0x3 ; 11000000
    out DDRD,r16 ;

    ldi r16,0x00 ;
    out PORTD,r16 ;

    ;————SPI инициализация ——————————————

    ldi r16,0xD0 ;SPCR=0xD0; (0xC0 – ??? slave)
    out SPCR,r16 ;

    ldi r16,0x0 ;SPSR=0?0
    out SPSR,r16 ;

    ;——————— USART ———————————-

    LDI R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(0<<UDRIE)
    OUT UCSRB, R16

    LDI R16, low(bauddivider)
    OUT UBRRL,R16
    LDI R16, high(bauddivider)
    OUT UBRRH,R16

    LDI R16,0
    OUT UCSRA, R16

    LDI R16, (1<<URSEL)|(1<<UCSZ0)|(1<MSB (Z)
    ldi r30,0x1d ;Load 29=>LSB (Z)
    RgClr: st Z,R31 ;0=>R(Z)
    dec R30 ;Counter=counter-1 dec-??????????
    brne RgClr ;Do until counter=0 Brne -??????? ???? ?? ?????

    ;———————- ????????? —————————————

    sei ;разрешить прерывания глобально
    loop :
    ldi r16,’R’

    mov date,r16

    cpi date,1

    BRGE m1 ;перейти если больше

    Rcall loop

    m1:
    LDI R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(1<<UDRIE)
    OUT UCSRB, R16
    nop
    nop
    nop
    nop

    rjmp loop
    ;—————Процедура отправки байта——————-

    uart_snt:

    PUSHF
    SBIS UCSRA,UDRE ; Пропуск если нет флага готовности
    RJMP uart_snt ; ждем готовности — флага UDRE

    OUT UDR, date ; шлем байт
    LDI R16, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE)|(0<<UDRIE)
    OUT UCSRB, R16
    ldi r16,0
    ; Возврат
    POPF
    RETi

    не могу понять в чем проблема в Протеусе в терминал шлет какуюто ахинею
    помогите плз=)

    1. посмотри по таблице ASCII что означают твои данные….настройки терминала и самого контроллера. и регистры перепроверь….тоже проблема была не работал уарт, всю прогу перепроверил не работает…потом просто ещё раз регистры настройки уарта посмотрел и нашел ошибку)))

  108. DI привет, совет твой нужен как спеца.

    Вопрос_1:
    Допустим в avr сыплются команды всего 8 команд. Каждая команда имеет пусть 10 символов (все условно). Но только на одну команду мне нужно ответить. Как лучше всего организовать отбор нашей нужной команды от остальных ненужных. Мне не нужен код, нужна просто идея, мысль как грамотно это сделать. Код я сам напишу. Может быть так — записать во флеш ту команду на которую надо ответить. Принимаемые же команды просто пишем в буфер, а когда считываем принятую команду из буфера сравниваем ее по CPI с командой из флеша, если какойто байт не совпал значит в ответ тишина. А если все байты совпали тогда принятая команда наша, тогда запускаем прерывание по UDR и выплевываем наш ответ?

    Вопрос 2:
    Нужно отвечать уже на все 8 команд. У каждой команды свои какие нибудь символы. Как команды отличать друг от друга? Каждую принятую команду сравнивать побайтно по CPI с восемью эталонами забитыми во флеш?

    1. Проще так:
      Нужное слово лежит в памяти или уже в регистрах, не важно.

      Команду мы побайтно сверяем по XOR (можно и по CP) если не совпал хоть один из ожидаемого — пропускаем и ждем следующую пачку.

      Тебе в любом случае придется побайтно сравнивать. Причем сразу же выбирать пути вариантов. Т.е. по мере ввода байтов у тебя сужается поле вариантов, на последнем байте остается только один. Проще всего это сделать пропуская через серию if

  109. Всем Привет!
    Возник следующий вопрос. Помогите пожалуйста кто может. Пытаюсь связать атмегу16 с телефоном Motorola E1000 через УАРТ. Проблема в том что телефон исполняет команды, но отклика не дает. Подключал по следующей схеме: http://img17.imageshack.us/i/indexpp.gif/
    Распиновка разъема: http://img193.imageshack.us/i/indexln.gif/
    Тактирую от внешнего кварца 11.0592 МГц, скорость передачи 9600.

    PS: С меня пиво!

  110. привет всем. Подскажите как лучше сделать…есть программа, которая принимает и отсылает сообщения по уарту, у меня на макетке пока сделано просто: в обработчике прерывания на прием идет его обработка, читаю регистр UDR, сохраняю его значение в переменную и погнали дальше))дак вот суть вопроса есть много флагов типа прием завершен, регистр пуст, ошибки по передаче нет…как это всё можно по простому реализовать, т.е как написать в пару строк проверку этих флагов??желательно на Си. Заранее спасибо

  111. Добрый день. Среди комментариев не нашел аналогичной проблемы.
    Программа, написанна на С в AVRStudio 4. Читаю данные и сразу отправляю обратно:

    ISR(SIG_UART_RECV) {
    U_Transmit(UDR);
    }

    В железе не работает :(
    При отладке (использую simulation) устанавливаю бит RXC в UCSRA и значение UDR (в том числе внутри прерывания). При выполнении любой команды UDR обнуляется и, естественно, в функцию U_Transmit передается 0x00.

    Подскажите в чем затык? Почему может UDR обнуляться.
    Вот весь код:

    #define F_CPU 8000000L
    #include
    #include

    #define BDRATE 9600L
    #define BDDIVIDER (F_CPU/(16*BDRATE)-1)
    #define HI(x) ((x)>>8)
    #define LO(x) ((x)&0xFF)

    ISR(SIG_UART_RECV);
    void initUART(void);
    void U_Transmit(unsigned char);

    int main(void)
    {
    volatile unsigned char i;
    initUART();

    sei(); // accept interrupt
    while(1) {
    i++;
    }
    return 0;
    }

    /*
    * Interrupts
    */

    /*
    * USART RX
    */
    ISR(SIG_UART_RECV) {
    U_Transmit(UDR);
    }

    /*
    * USART init
    */
    inline void initUART() {
    UBRRL = LO(BDDIVIDER);
    UBRRH = HI(BDDIVIDER);
    UCSRA = 0;
    UCSRB = 1<<RXEN | 1<<TXEN | 1<<RXCIE | 0<<TXCIE; // accept rx & tx by USART, accept rx interrupt
    UCSRC = 1<<URSEL | 1<<UCSZ0 | 1<<UCSZ1; //8bit + 1 stop + even parity
    return;
    }

    // transmite byte by USART
    void U_Transmit(unsigned char data) {
    while(!(UCSRA & (1<<UDRE))) {
    ;
    }
    UCSRB &= ~(1<<TXB8);
    if (data &0x100)
    UCSRB |= (1<<TXB8);
    UDR=data;
    }

    1. А попробуй отправлять обратно не в функции (она кстати лажовая, ибо использует цикл ожидания, а такие функции в прерывании юзать вообще жопа)

      У тебя же по факту получается UDR = UDR.

      Сделай в ISR просто:

      i=UDR ;
      UDR = i;

      1. Разобрался:
        1. Отладчик выдавал такой косяк из-за того что что проект хранился на сетевом диске. Положил проект в корень диска C: и сразу заработало. (походу, критичен не только путь установки компилятора, но и расположение самого проекта)
        2. В железе не работало потому, что были установлены fuse биты, повлиявшие на частоту (изменил CKOPT и CKSELx). Не могу понять зачем они их на заводе выставляют.
        PS
        Может внесете изменения в статью «Программирование на Си» и укажете там что нужно еще fuse биты проверить?

        1. 2. Ну дык надо понимать на какой частоте работает контроллер. Изначально многие работают на низкой частоте. А FUSE к программированию мало относятся. Это скорей аппаратное обеспечение.

  112. прошил мегу16, подключил… отрпавляю вайт возвращяеться не то что надо.. первые 6 бит правельны а последние 3 всегда то 101 то 111 чета не то.. настроил правельно пользуюсь терминалом v1.9b. до этого подлуючал GSM модуль всё замечательно работало.. может быть в монтаже??

  113. В конце статьи было написано, что циклическая инициализация приводит к потери данных.
    Это касается любых изменений в регистрах настройки UART или нет? Если например в прерывании по флагу RXC я разрешаю прерывание по флагу UDRE. Ну или вообще разрешаю или запрещаю прерывания по приему или передаче. Может ли это чисто теоретически привести к потерям?

  114. у протеуса кстати UART симулирован неправильно — поначалу в нем хотел отладить -а он мне говорит- дескать неправильный опкод по такому то адресу, выходящему, вообще говоря, не то что за границы программы, но и вообще памяти МК — залил в реальный МК — все заработало как часы

  115. Привет DI
    бьюсь уже второй день над такой проблемой, задача очень простая, слать каждые 250мс в уарт один байт(0x48)
    atmega16A-16 подключена к RT232RL (по схеме с этого сайта), дальше к компу через усб
    на компе слушается виртуальный ком-порт
    подключаю питание, землю, уарт на вход и выход, больше к контролеру ничего не подключено
    слушаю порт, а там вместо 0x48 идет 0x88
    причем если скажем послать c мк 0x7f приходит 0xbf
    такое чувство что идет сдвиг вправо, только какой то странный
    и тут самая магия, если отключить питание (+5v), то всё работает нормально(!)
    непонятно откуда идёт питание, однако экспериментами удалось установить, что если отключить ножку уарта на приём, то контролер выключается, получается он как то берёт с неё питание, хотя в коде она вообще не используется
    получается такой расклад
    +5 подключено и ножка на приём подключена = неправильные данные
    +5 подключено и ножка на приём не подключена = неправильные данные
    +5 не подключено и ножка на приём подключена = всё работает корректно
    +5 не подключено и ножка на приём не подключена = контролер не работает
    это просто какая то жесть, как такое вообще возможно
    из этого что я пытался сделать и не помогло
    1) ресет подтягивал по питанию через 10ком резистор
    2) менял скорости уарта
    3) проверял переходник FT232RL путём замыкание приёмника и передатчика, работает отлично, данные возвращаются корректно
    4) думал на глюки драйвера под линуксом, однако под вендой всё тоже самое
    5) ставил фильтрующие конденсаторы на питание мк
    6) добавлял в код перед инициалиазацией очистку памяти и регистров
    7) ну и уже 10 раз всё перепроверил
    еще интересный момент, когда дотрагиваешься пальцем до земли (при этом питание вообще не подключено, только уарт) то контролер стартует и передает вообще кашу в уарт, но это вообщем то логично
    вот привожу код, простой как 3 копейки
    http://easyelectronics.ru/repository.php?act=view&id=76

    что делать? в чем причина такого поведения? может брак ft232 или контролера?

    1. Проверь вначале FTDI и весь интерфейс. Коротни на ноге контроллера RX на TX и пошли что нибудь с компа, должно вернуться.

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

      1. да, FTDI проверял, замыкаю перемычкой TX и RX, пишу в порт байт, тут же возвращается, всё работает отлично

        а питание берется с системного блока компьютера, 5 вольт, стоят конденсаторы в качестве фильтров, вроде как хорошее питание, компьютер то без проблем работает

        есть еще какие то причины, что такая фигня вылазиет?

      2. о, еще обнаружил такую фишку
        с самого начала изучения МК я сделал себе платку с разьемом для атмеги и выводом всех его контактов на штыри, чтоб удобно было эксперементировать
        так вот, сейчас отключил линию RX совсем от мк, так как данные идут только от мк
        беру питание (+5), включаю терминал, мониторю интерфейс
        втыкаю на Vcc, идут битые данные как и было
        но стоит посадить питание (+5) на любую другую ножку, кроме GND и Vcc, то всё работает отлично, получается проблема не в плохом питании?

        FTDI тоже норм работает, замыкаю rx и tx, отдаёт эхом, как и должно быть
        сам переходник USB-UART делал по схеме с этого сайта
        единственное сделал разводку на выводных элементов сам
        вот, на всякий случай файл схемы ftp://luckystriker.net/usb_uart.lay
        сам FT232 нормально припаялся, вроде нигде косяков нет

        дальше тупо соединяется RX переходника и TX мк, ну и питание к мк, ресет подтягивается на 10кОм к +5, всё, больше ничего нет, питание к мк беру с переходника
        впрочем, если брать питание с БП компа та же самая ситуация

        что же делать? помоги пожалуйста

          1. 1 MHz, настройки заводские, фуз биты не трогал
            вот на всякие случай настройки фуз битов

            fatalist@fatalist-laptop:~$ avrdude -c usbasp -p m16 -U hfuse:r:-:h -U lfuse:r:-:h

            avrdude: AVR device initialized and ready to accept instructions

            Reading | ################################################## | 100% 0.02s

            avrdude: Device signature = 0x1e9403
            avrdude: reading hfuse memory:

            Reading | ################################################## | 100% 0.01s

            avrdude: writing output file «»
            0x99
            avrdude: reading lfuse memory:

            Reading | ################################################## | 100% 0.01s

            avrdude: writing output file «»
            0xe1

            avrdude: safemode: Fuses OK

            avrdude done. Thank you.

          2. блин, щя покрутил частоту
            8MHz — о чудо, всё работает нормально
            4MHz — тоже всё работает
            2MHz — работает
            вернул на 1MHz — не работает блин
            прочитал всю главу про USART в даташите, ничего про неработающий UART на 1 Мгц не сказано
            в конце даже табличка для расчета UBBR на 1 мгц (с неё тоже пробовал напрямую забивать число)
            блин, как так? на 1 мгц не работает уарт?

          3. продолжаю мучить уарт, получил интересную зависимость
            все результаты получены для частоты 1 мгц
            снизив скорость уарта внезапно стало работать нормально
            результаты:
            2400 — работает
            4800 — работает
            9600 — не работает
            ну и всё что выше не работает
            в даташите сказано

            For standard crystal and resonator frequencies, the most commonly used baud rates for
            asynchronous operation can be generated by using the UBRR settings in Table 68.
            UBRR values which yield an actual baud rate differing less than 0.5% from the target
            baud rate, are bold in the table. Higher error ratings are acceptable, but the receiver will
            have less noise resistance when the error ratings are high, especially for large serial
            frames

            это таблица из даташита, скорость и процент ошибок, частота мк 1 мгц

            2400 25 0.2%
            4800 12 0.2%
            9600 6 -7.0%
            14.4k 3 8.5%
            19.2k 2 8.5%
            28.8k 1 8.5%
            38.4k 1 -18.6%
            57.6k 0 8.5%

            Вот где собака зарыта!

            сигнал сдвинут и стробы стробирующего приёмника не попадают на импульс, поэтому получается такой странный «сдвиг» о котором я говорил в