Страница 1 из 4
Программный I2C
Добавлено: Пт апр 25, 2014 11:28:05
Demetros
Доброго времени суток!
Столкнулся с проблемой при реализации программного I2C. Суть такова: микроконтроллер PIC16F876A работает в паре с DS1307. Это нечто вроде будильника. Я задаю время(в программе это 00:00:00), часы тикают и при достижении заданного времени (5 секунд) зажигается, например, светодиод (на самом деле проект куда более серьезный, данный пример лишь упрощение). I2C повесил на RC6 и RC7. Все работает стабильно, прием и передача идет, но проблема в том, что на порт С больше ничего повесить нельзя. После того как зажигается светодиод, сбивается прием/передача. Перепроверял много раз, нигде в коде весь порт не участвует, оперирую только отдельными пинами. Помогите, пожалуйста, найти ошибку и освободить оставшиеся пины порта С. Программу на си и проект в Proteus 7.10 SP0 прилагаю.
Заранее благодарен.
Re: Программный I2C
Добавлено: Пт апр 25, 2014 13:46:08
cahek80
можно вопрос? а зачем скобки в (RC2=1) в строке if (sec==5) (RC2=1);
Re: Программный I2C
Добавлено: Пт апр 25, 2014 15:17:09
Demetros
В принципе они там действительно не нужны, это наверно моя привычка с QBasica и Pascal'а брать все в операторные скобки.
Re: Программный I2C
Добавлено: Пт апр 25, 2014 15:19:02
ARV
не может это быть связано с тем, что для вывода сигналов и2ц вы пишите в регистр TRISC, а светодиод зажигаете совсем не так?
P.S. как все эти нестандартные возможности "якобы Си" затрудняют понимание программы!
Re: Программный I2C
Добавлено: Пт апр 25, 2014 15:24:45
cahek80
Demetros писал(а):В принципе они там действительно не нужны, это наверно моя привычка с QBasica и Pascal'а брать все в операторные скобки.
немного оффтоп, но как-то впервые встречаю подобные скобки еще с кубасика и паскалей... ))
и еще немного неясно, пока не на чем прошагать - SDA_IN Вы нигде в 0 не выставляете?
Re: Программный I2C
Добавлено: Пт апр 25, 2014 16:51:17
Demetros
ARV: По всем симптомам так и есть, будто в какой-то момент программы происходит управление всем портом, но в программе я оперирую TRISC6 и TRISC7 (направлением 6 и 7-й ножки), по идее остальные ноги остаются работать "на выход".
cahek80: В паскале вместо "()" используется "Begin End". Прошу прощения за кривой код, на скорую руку урезал ненужные части программы. На самом деле там стояла не одна функция "RC2=1", а несколько других, они и были объединены скобками.
По поводу SDA_IN, я вроде единожды выставляю в 0 при инициализации и больше не трогаю SDA_IN. Дальнейшее "общение" идет за счет изменения направления работы ножки и подтягивающих резисторов (если я конечно не ошибаюсь).
Re: Программный I2C
Добавлено: Пт апр 25, 2014 17:08:23
cahek80
begin end, хм тогда уж {}, а не ()

про TRIS* и SDA_IN да, я понял, сам когда-то так рулил тоже. Но работало без сбоев. хз.
Re: Программный I2C
Добавлено: Пт апр 25, 2014 19:15:51
Аlex
но в программе я оперирую TRISC6 и TRISC7 (направлением 6 и 7-й ножки)
А как Вы думаете, после перенаправления бита порта, что будет находится в защёлке ? Откуда такая уверенность, что там будет то, что у Вас на уме ?
ЗЫ: Курим -
http://www.microchip.ru/files/d-sheets-rus/pic16_9.pdf
http://www.microchip.ru/lit/?mid=1x0
Re: Программный I2C
Добавлено: Пт апр 25, 2014 19:25:06
HHIMERA
Demetros писал(а):
I2C повесил на RC6 и RC7. Все работает стабильно, прием и передача идет, но проблема в том, что на порт С больше ничего повесить нельзя. После того как зажигается светодиод, сбивается прием/передача. Перепроверял много раз, нигде в коде весь порт не участвует, оперирую только отдельными пинами.
Смотрите в сторону RMW... "оперирую только отдельными пинами" в этих ПИКах на самом деле является операцией с портом...
В последующих ПИках поэтому и ввели регистры LATx... У Микрочипа есть апнота на эту тему...
Re: Программный I2C
Добавлено: Пт апр 25, 2014 19:58:05
urry
ну да, согласен с Химерой - RMW.
Я уже всегда в мк, в которых нет LAT, выделяю регистр, обзываю его LATX и меняю битики в нем, а его уже - целиком выдаю в порт.
Да, дольше, зато спишь спокойно.
Re: Программный I2C
Добавлено: Пт апр 25, 2014 20:02:06
ARV
urry писал(а):Я уже всегда в мк, в которых нет LAT, выделяю регистр, обзываю его LATX и меняю битики в нем, а его уже - целиком выдаю в порт.
я так понимаю, если бы было написано традиционно
PORTC |= 1<<PC7; то никаких проблем не возникло бы? я с PIC-ами никогда в жизни дел не имел
Re: Программный I2C
Добавлено: Пт апр 25, 2014 20:12:43
HHIMERA
ARV писал(а):если бы было написано традиционно PORTC |= 1<<PC7; то никаких проблем не возникло бы?
Это и есть Read-Modify-Write...
Как сказал уже Юрий... лучше юзать "фантомный регистр" и через XOR...
Re: Программный I2C
Добавлено: Пт апр 25, 2014 21:04:59
ARV
Юрий сказал, что правильно - это когда считываем в промежуточную переменную, модифицируем и выводим обратно - так?
что написал я, так это то же самое! это сокращенная запись PORTC = PORTC | (1<<PB7); то есть в промежуточную переменную (ее компилятор должен создать сам - автоматические переменные обычно бывают как раз регистровыми) считывается порт, над ним выполняется операция модификации, а затем результат записывается обратно в порт.
что не так?
Re: Программный I2C
Добавлено: Пт апр 25, 2014 21:30:52
Аlex
ARV писал(а):PORTC = PORTC | (1<<PB7); то есть в промежуточную переменную (ее компилятор должен создать сам - автоматические переменные обычно бывают как раз регистровыми) считывается порт, над ним выполняется операция модификации, а затем результат записывается обратно в порт.
что не так?
Оптимизатор убьёт эти все предположения и вставит единственную команду bsf (установка бита в регистре).
У ТС'а проблема в другом. Я уже пытался ему намекнуть, ссылочку кинул, чтобы он почитал хоть немного и покумекал. Но тут некоторые не выдержали и открыли тайну напрямую

Re: Программный I2C
Добавлено: Пт апр 25, 2014 21:40:05
ARV
Аlex писал(а):Оптимизатор убьёт эти все предположения и вставит единственную команду bsf (установка бита в регистре).
если
PORTC определен, как
volatile-переменная, оптимизатор
не имеет на это права. но если это так - я пас, к PIC-ам ни рукой ни ногой

Re: Программный I2C
Добавлено: Пт апр 25, 2014 22:02:30
HHIMERA
Угу... "Гложут меня смутные сомнения!"(С)
AVR Studio 4.18... Mega16A...
Код: Выделить всё
23: asm("nop");
+000000CE: 0000 NOP No operation
25: PORTC = PORTC | (1<<PB7);
+000000CF: 9AAF SBI 0x15,7 Set bit in I/O register
27: asm("nop");
+000000D0: 0000 NOP No operation
29: PORTC |= (1<<PB7);
+000000D1: 9AAF SBI 0x15,7 Set bit in I/O register
Re: Программный I2C
Добавлено: Пт апр 25, 2014 22:03:21
Аlex
ARV писал(а):не имеет на это права
Мне кажется, Вы заблуждаетесь.
volatile тут совсем не причём. Ну не убьёт компилятор совсем эту строку, если посчитает нужным, но никак не будет создавать ещё переменную. С какой радости ?
ЗЫ: Не путайте с языком
java. Там строки, типа
a=a+1; и
a+=1; не эквивалентны. Для Си - это одно и тоже...
Re: Программный I2C
Добавлено: Пт апр 25, 2014 22:17:27
ARV
Аlex писал(а):Мне кажется, Вы заблуждаетесь.
volatile тут совсем не причём. Ну не убьёт компилятор совсем эту строку, если посчитает нужным, но никак не будет создавать ещё переменную. С какой радости ?
ЗЫ: Не путайте с языком java. Там строки, типа a=a+1; и a++; не эквивалентны. Для Си - это одно и тоже...
а мне кажется - вы заблуждаетесь

что касается java - то я знаю ее (или его?) на уровне "смотрю в книгу - вижу фигу".
если переменная объявлена как volatile, то компилятор не имеет права выкинуть любое обращение к ней.
здесь обращение к
var компилятор имеет право выкинуть, а обращение на чтение к
vol - не имеет. соответственно в операторе
vol |= 1; произойдет обязательное считывание содержимого переменной в промежуточную переменну, а затем модифицированный результат промежуточной переменной запишется обратно. более того, если написать
var = vol + vol + vol; то компилятор не будет иметь права заменить это на
var = vol * 3; - он так и будет
трижды считывать vol!!!
но это в нормальных компиляторах Си так, как там в PIC-антном компиляторе - я не в курсе... может, он и не адекватный восе...
Re: Программный I2C
Добавлено: Пт апр 25, 2014 22:27:32
Аlex
Ну я даже и не знаю что сказать...
Код: Выделить всё
int main(int argc, char **argv) {
int a;
volatile int b;
while(1){
a = a | (1<<7);
a |= (1<<7);
b = b | (1<<7);
b |= (1<<7);
}
}
Код: Выделить всё
29 a = a | (1<<7);
080002f6: ldr r3, [r7, #12]
080002f8: orr.w r3, r3, #128 ; 0x80
080002fc: str r3, [r7, #12]
30 a |= (1<<7);
080002fe: ldr r3, [r7, #12]
08000300: orr.w r3, r3, #128 ; 0x80
08000304: str r3, [r7, #12]
32 b = b | (1<<7);
08000306: ldr r3, [r7, #8]
08000308: orr.w r3, r3, #128 ; 0x80
0800030c: str r3, [r7, #8]
33 b |= (1<<7);
0800030e: ldr r3, [r7, #8]
08000310: orr.w r3, r3, #128 ; 0x80
08000314: str r3, [r7, #8]
IDE -
CooCox. компилятор, как я понял, -
arm-none-eabi.

Re: Программный I2C
Добавлено: Пт апр 25, 2014 22:41:10
ARV
пока и я не вижу, что вы хотели сказать
трудно ориентироваться в незнакомом ассемблере, но по-моему, везде использованы промежуточные регистры, с которыми производятся операции

нигде нет замены на прямую модификацию битов и т.п.
а вот вам примерчик для AVR:
Код: Выделить всё
PORTB = var + var + var;
7c: 90 91 61 00 lds r25, 0x0061
80: 89 2f mov r24, r25
82: 88 0f add r24, r24
84: 89 0f add r24, r25
86: 88 bb out 0x18, r24 ; 24
PORTB = vol + vol + vol;
88: 20 91 60 00 lds r18, 0x0060
8c: 80 91 60 00 lds r24, 0x0060
90: 90 91 60 00 lds r25, 0x0060
94: 82 0f add r24, r18
96: 89 0f add r24, r25
98: 88 bb out 0x18, r24 ; 24
как видите, в первом случае считывание в регистр было 1 раз, а потом этот регистр плюсовался к исходному, а во втором случае считывание переменной сделано трижды
