22.05.2018, 12:10
|
|
Прописка
Регистрация: 27.01.2015
Сообщений: 265
Сказал спасибо: 51
Сказали Спасибо 28 раз(а) в 27 сообщении(ях)
|
PIC16F1829, I2C, ACKSTAT
Какое-то время назад сообразил, типа, функцию для аппаратной передачи данных по I2C в режиме мастера. Получилось такое вот:
Код:
|
void itwoc(){
SSP1BUF=data;
while(SSP1IF==0){
}
SSP1IF=0;
while(SSP1CON2bits.ACKSTAT==1){
}
SSP1CON2bits.ACKSTAT=1;
} |
Условия START и STOP формировались в функции main, а между ними в функцию I2C передается значение переменной data, в которую последовательно записывались адрес чипа, адрес регистра, старший и младший байты для передачи в 16-битные регистры ведомого устройства. Работало вроде нормально, не жаловался. А недавно решил подрихтовать это дело, избавившись от лишних движений в main(). Натворил следующее:
Код:
|
void itwoc(){
//---Старт передачи-----------------------------------------------------
SSP1CON2bits.SEN=1; // Формирование условия START на линиях SCL, SDA
while(SSP1IF==0){ // Ожидание формирования условия START. Если флаг SSP1IF равен нулю, тогда продолжать ждать
}
SSP1IF=0; // Сброс флага SSP1IF после поднятия его по выполнению условия START
//---Передача адреса чипа и направления передачи
SSP1BUF=ADCHIPTX; // Загрузка в буфер адреса чипа и направления передачи
while(SSP1IF==0){ // Проверка отправки данных - опустошения буфера...
} //...цикл ожидания повторяется, пока данные не будут отправлены и не поднимется флаг SSP1IF
SSP1IF=0; // Сброс флага SSP1IF
while(SSP1CON2bits.ACKSTAT==1){ // Ожидание ACK - сигнала подтверждения приема данных ведомым...
} //...цикл ожидания повторяется пока не будет получен сигнал подтверждения приема - сброс в ноль бита ACKSTAT
SSP1CON2bits.ACKSTAT=1; //Установить бит ACKSTAT в 1 для следующей передачи
//---Передача адреса 16-разрядного регистра-----------------------------
SSP1BUF=ADREGTX; // Загрузка в буфер адреса регистра
while(SSP1IF==0){ // Проверка отправки данных - опустошения буфера...
} //...цикл ожидания повторяется, пока данные не будут отправлены и не поднимется флаг SSP1IF
SSP1IF=0; // Сброс флага SSP1IF
while(SSP1CON2bits.ACKSTAT==1){ // Ожидание ACK - сигнала подтверждения приема данных ведомым...
} //...цикл ожидания повторяется пока не будет получен сигнал подтверждения приема - сброс в ноль бита ACKSTAT
SSP1CON2bits.ACKSTAT=1; //Установить бит ACKSTAT в 1 для следующей передачи
//---Передача первого (старшего) байта----------------------------------
SSP1BUF=REGTXH; // Загрузка в буфер первого байта
while(SSP1IF==0){ // Проверка отправки данных - опустошения буфера...
} //...цикл ожидания повторяется, пока данные не будут отправлены и не поднимется флаг SSP1IF
SSP1IF=0; // Сброс флага SSP1IF
while(SSP1CON2bits.ACKSTAT==1){ // Ожидание ACK - сигнала подтверждения приема данных ведомым...
} //...цикл ожидания повторяется пока не будет получен сигнал подтверждения приема - сброс в ноль бита ACKSTAT
SSP1CON2bits.ACKSTAT=1; //Установить бит ACKSTAT в 1 для следующей передачи
//---Передача второго (младшего) байта----------------------------------
SSP1BUF=REGTXL; //загрузка в буфер второго байта
while(SSP1IF==0){ // Проверка отправки данных - опустошения буфера...
} //...цикл ожидания повторяется, пока данные не будут отправлены и не поднимется флаг SSP1IF
SSP1IF=0; // Сброс флага SSP1IF
while(SSP1CON2bits.ACKSTAT==1){ // Ожидание ACK - сигнала подтверждения приема данных ведомым...
} //...цикл ожидания повторяется пока не будет получен сигнал подтверждения приема - сброс в ноль бита ACKSTAT
SSP1CON2bits.ACKSTAT=1; //Установить бит ACKSTAT в 1 для следующей передачи
//---Стоп передачи------------------------------------------------------
SSP1CON2bits.PEN=1; // Формирование условия STOP на линиях SCL, SDA
while(SSP1IF==0){ // Ожидание формирования условия STOP. Если флаг SSP1IF не равен нулю, тогда продолжать ждать
}
SSP1IF=0; // Обнуление флага SSP1IF после поднятия его по выполнению условия STOP
} |
ADCHIPTX, ADREGTX, REGTXH, REGTXL, это адрес чипа, адрес регистра, старший и младший байты данных соответственно.
Работать это отказалось. Полез разбираться. Оказалось, зависает на проверке состояния ACKSTAT в ходе передачи адреса регистра. Полез глубже, и узнал, что ACKSTAT у меня вообще не шевелится и всегда находится в состоянии 1. И совсем ничего не шевелится в регистре SSP1CON2 при наблюдении в окне Variables, ни один бит...
Причем, как оказалось, он и в предыдущем коде точно так же себя вел, не сбрасываясь в 0. Но, при этом все работало Рабочая точка программы просто проскакивала мимо while(SSP1CON2bits.ACKSTAT==1){} как будто его там и не было. ACKSTAT так же всегда в состоянии 1, как в регистре SSP1CON2, так и в переменной SSP1CON2bits.
Как думаете, что за чертовщина? Чего мне делать-то со всем этим? MPLAB X, компилятор XC8, PIC16F1829 и PICkit3.
P.S. Если все проверки ACKSTAT во втором коде закомментировать ваще, тогда передача по I2C работает. Но, такое положение дел определенно не может быть нормальным.
P.P.S. Со вторым кодом еще странность была. Удалил ненужные комменты, и запустил на отладку. И заработало даже с проверками ACKSTAT, но не надолго, до следующего запуска только. Больше не работало.
|
|
|
|
22.05.2018, 20:27
|
|
Прописка
Регистрация: 29.10.2008
Сообщений: 272
Сказал спасибо: 0
Сказали Спасибо 102 раз(а) в 95 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
Напиши следующее... под свой синтаксис
char ACK;
char DATA;
//Старт
pir1.SSP1IF=0;
ssp1con2.SEN=1;
while(ssp1con2.SEN);
//Передача байта
pir1.SSP1IF=0;
ssp1buf=Data;
while(pir1.SSPIF == 0);
ACK=ssp1con2.ACKSTAT; // читаем ответ
.....
//Стоп
pir1.SSP1IF=0;
ssp1con2.PEN=1;
while(ssp1con2.PEN);
|
|
|
Сказали "Спасибо" mimuh64
|
|
|
22.05.2018, 22:41
|
|
Прописка
Регистрация: 27.01.2015
Сообщений: 265
Сказал спасибо: 51
Сказали Спасибо 28 раз(а) в 27 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
У меня практически то же самое сейчас. Только SSPIF обнуляю сразу после его установки в 1. Попробую сбрасывать его в ноль перед загрузкой данных в SSPBUF.
Тут еще вопросы назрели. После проверки подтверждения ACKSTAT нужно что-то делать с битом ACKSTAT? Устанавливать в 1 или сбрасывать в 0? В примерах из интернета увидел, как люди пытались установить ACKSTAT в 1 после аппаратного сброса его в 0 при подтверждении. И сам сделал так же. А сейчас посмотрел даташит подробно, там для ACKSTAT четко указано - R. Выходит, что не должен бит писаться?
P.S. Попробовал переставить сбросы SSPIF перед загрузками в буфер. Стало еще хуже, стало виснуть уже на отправке адреса чипа в ведомый. Вернул назад - поставил сбросы сразу после проверки SSPIF на установку в 1. Теперь виснет на проверке ACKSTAT после отправки адреса регистра в ведомый.
Если убрать проверки ACKSTAT (кроме первой), тогда вроде нормально работает, пишет что нужно куда нужно. Начинаю подозревать, что ведомый чип вообще не передает подтверждение приема кроме как после отправки адреса в него, хотя на диаграмме указано подтверждение ACK после отправки адреса чипа, адреса регистра и обоих байтов.
Или я чего не учел? Кстати, если убрать проверки SSPIF сразу после записи данных в буфер, тогда проверка ACKSTAT проходит на всех этапах передачи данных в ведомого. То ли ACK не успевает в 1 встать, то ли наоборот, за время проверки SSPIF что-то зависает в ведомом и он не выдает 0 в бит ACKSTAT контроллера.
Последний раз редактировалось Alcest; 22.05.2018 в 23:33.
Причина: добавление
|
|
|
|
23.05.2018, 00:05
|
|
Прописка
Регистрация: 27.01.2015
Сообщений: 265
Сказал спасибо: 51
Сказали Спасибо 28 раз(а) в 27 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
Вот что я подумал. А оно нужно, проверять флаг прерывания SSPIF после загрузки данных в буфер SSPBUF? Разве подтверждения приема от ведомого путем сброса в 0 бита ACKSTAT недостаточно для того, чтобы стало ясно, что данные отправлены, а значит буфер пуст?
В даташите на PIC16F87x нашел описание процедуры передачи данных по I2C в режиме ведущего.
В пунктах 2 и 12 (подчеркнуто красным) четко указано "ждать прерывания или установки флага SSPIF". В пунктах 6 и 10 (подчеркнуто синим) пишется только, что поднимается флаг SSPIF после отправки данных и подтверждения ведомым приема сигналом ACK. То есть, ничто не указывает на необходимость проверки SSPIF после загрузки данных в буфер, тем более до проверки бита ACKSTAT. Отмечается только, что флаг поднимается. Мало ли где еще может пригодится состояние этого флага.
|
|
|
|
23.05.2018, 01:19
|
|
Прописка
Регистрация: 27.01.2015
Сообщений: 265
Сказал спасибо: 51
Сказали Спасибо 28 раз(а) в 27 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
Попробовал убрать задержки на проверку флага SSPIF кроме как в формировании условий START и STOP. Зависания прекратились, но данные в ведомый чип не передаются, он молчит как рыба об лед. Походу, сигналы ACK игнорируются MSSP модулем микроконтроллера, и он гонит данные в слейв напропалую, не интересуясь, слушает он его или нет. А может я чего не учел. Но что именно?
Попытался поставить проверки на поднятие флага SSPIF после проверок ACK. Снова начались зависания. Теперь на передаче первого байта данных зависает прожка...
Вот рекомендованная последовательность действий из даташита на PIC16F1829. Вроде все сделал как там написано, а в результате нихрена...
1. Пользователь генерирует условие запуска, установив бит SEN регистра SSPxCON2.
2. SSPxIF устанавливается аппаратным обеспечением после завершения запуска.
3. SSPxIF очищается программным обеспечением.
4. Модуль MSSPx будет ожидать требуемое время начала до начала любой другой операции.
5. Пользователь загружает SSPxBUF с подчиненным адресом для передачи.
6. Адрес сдвигается на вывод SDAx до тех пор, пока все восемь бит не будут переданы. Передача начинается, как только SSPxBUF записывается.
7. Модуль MSSPx сдвигается в бит ACK от подчиненного устройства и записывает его значение в бит ACKSTAT регистра SSPxCON2.
8. Модуль MSSPx генерирует прерывание в конце девятого тактового цикла, устанавливая бит SSPxIF.
9. Пользователь загружает SSPxBUF с восемью битами данных.
10. Данные сдвигаются с вывода SDAx до тех пор, пока не будут переданы все восемь бит.
11. Модуль MSSPx сдвигается в бит ACK от подчиненного устройства и записывает его значение в бит ACKSTAT регистра SSPxCON2.
12. Шаги 8-11 повторяются для всех переданных байтов данных.
13. Пользователь генерирует условие Stop или Restart, устанавливая биты PEN или RSEN регистра SSPxCON2. Прерывание генерируется после завершения состояния Stop / Restart.
Последний раз редактировалось Alcest; 23.05.2018 в 01:23.
|
|
|
|
23.05.2018, 02:04
|
|
Прописка
Регистрация: 27.01.2015
Сообщений: 265
Сказал спасибо: 51
Сказали Спасибо 28 раз(а) в 27 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
Дошло до смешного, и одновременно грустного. Вспомнив, что раньше передача данных работала, когда данные в буфер передавались через переменную из функции main(), и записывались в буфер в функции передачи по I2C, решил, что корявая в принципе прожка работала из-за временных задержек между загрузкой данных в буфер.
Закомментировал в проге все проверки приема данных ACK ведомым чипом (проверку флага SSPIF убрал еще раньше) и понатыкал задержек по 100 микросекунд между загрузками данных в буфер SSPBUF. И что думаете? Заработало...
Теперь мне очень хочется громко ругаться нецензурными выражениями. Где искать косяк? В ведомом устройстве? В программе? В даташите на микроконтроллер??? Ведь не оставлю же я эту байду с временными задержками, потому как несерьезно.
Чего теперь делать? Ведь то что ниже, это же позор... не знаю уж чей: мой, Микрочипа или компании KT Micro, чью микросхему тюнера я юзаю в качестве ведомого устройства.
Код:
|
void itwoc(){
//---Старт передачи-----------------------------------------------------
SSP1CON2bits.SEN=1; // Формирование условия START на линиях SCL, SDA
while(SSP1IF==0){ // Ожидание формирования условия START. Если флаг SSP1IF равен нулю, тогда продолжать ждать
}
SSP1IF=0; // Сброс флага SSP1IF после поднятия его по выполнению условия START
__delay_us(100);
//---Передача адреса чипа и направления передачи
SSP1BUF=ADCHIPTX;
// while(SSP1CON2bits.ACKSTAT==1){
// }
__delay_us(100);
//---Передача адреса 16-разрядного регистра-----------------------------
SSP1BUF=ADREGTX;
// while(SSP1CON2bits.ACKSTAT==1){
// }
__delay_us(100);
//---Передача первого (старшего) байта----------------------------------
SSP1BUF=REGTXH;
// while(SSP1CON2bits.ACKSTAT==1){
// }
__delay_us(100);
//---Передача второго (младшего) байта----------------------------------
SSP1BUF=REGTXL;
// while(SSP1CON2bits.ACKSTAT==1){
// }
__delay_us(100);
//---Стоп передачи------------------------------------------------------
SSP1CON2bits.PEN=1; // Формирование условия STOP на линиях SCL, SDA
while(SSP1IF==0){ // Ожидание формирования условия STOP. Если флаг SSP1IF не равен нулю, тогда продолжать ждать
}
SSP1IF=0; // Обнуление флага SSP1IF после поднятия его по выполнению условия STOP
} |
|
|
|
|
23.05.2018, 08:57
|
|
Прописка
Регистрация: 29.10.2008
Сообщений: 272
Сказал спасибо: 0
Сказали Спасибо 102 раз(а) в 95 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
Зачем так много писать, если достаточно было сделать как я написал...
Контроль освобождения буфера SSP1BUF через while(SSP1CON2bits.ACKSTAT==1) и тем более так контролировать его наличие - это бред, в ACKSTAT как правило (при корректной работе) всегда 0, поэтому тебе и понадобилось __delay_us(100); иначе переполнение буфера и зависание модуля... Контроль отправки - через while(pir1.SSP1IF == 0); Таким образом контролируем освобождение SSP1BUF и формирование девятого импульса на SCL для чтения ACK.
И это - ACK=ssp1con2.ACKSTAT; // читаем ответ - не совсем то что ты пишешь "как у тебя"...
При малейшем сбое появление 1 в ACKSTAT - зависание и все... ACKSTAT - просто читают, и делают с ним что хотят, хотят контролируют, хотят нет...
Если не хочешь делать как пишут.... продолжай выдумывать велосипед....
Последний раз редактировалось mimuh64; 23.05.2018 в 09:37.
|
|
|
Сказали "Спасибо" mimuh64
|
|
|
23.05.2018, 10:14
|
|
Прописка
Регистрация: 27.01.2015
Сообщений: 265
Сказал спасибо: 51
Сказали Спасибо 28 раз(а) в 27 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
Сообщение от mimuh64
|
Если не хочешь делать как пишут.... продолжай выдумывать велосипед....
|
Меня инфа из даташитов и описаний I2C заставила думать, что без проверки ACKSTAT ваще никуда. Оказалось, в ней нет особой необходимости. Спасибо, помогли избавится от заблуждения
Я ведь уже удалял проверки ACK из проги, ограничиваясь проверкой SSPIF после каждой загрузки в буфер, и все работало. Но мне казалось, что это неправильно, в даташитах везде долбят про проверку ACKSTAT. Получилось же так, что в общем случае проверка ACK нафик не нужна.
Последний раз редактировалось Alcest; 23.05.2018 в 10:25.
|
|
|
|
23.05.2018, 10:46
|
|
Прописка
Регистрация: 29.10.2008
Сообщений: 272
Сказал спасибо: 0
Сказали Спасибо 102 раз(а) в 95 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
Сообщение от Alcest
|
в даташитах везде долбят про проверку ACKSTAT.
|
Всего лишь рекомендуют... дабы быть уверенным что "Ведомый" прочел байт. Но у тебя и ранее не было условий контроля - что делать если АСК равен 1. Что касаемо инфы, которая, "заставила думать"... Смотрим рисунок FIGURE 25-28 из ДШ (для мастера в режиме "записи") и видим что контроль "Старт" и "Стоп" можно делать через биты SEN или SSP1IF и PEN или SSP1IF, соответственно... Я контролирую их через SEN и PEN. Контроль выдачи из SSP1BUF можно делать через SSP1IF или через R/W, рекомендуют через SSP1IF, это заменит твои __delay_us(100);...
|
|
|
|
23.05.2018, 11:14
|
|
Прописка
Регистрация: 27.01.2015
Сообщений: 265
Сказал спасибо: 51
Сказали Спасибо 28 раз(а) в 27 сообщении(ях)
|
Re: PIC16F1829, I2C, ACKSTAT
Сообщение от mimuh64
|
Всего лишь рекомендуют...
|
Из фрагментов даташитов приведенных мною выше видно, что рекомендуют они проверку SSPIF, а проверку ACK делать чуть ли не приказывают. Не знаю, может это одна из трудностей англо-русского перевода. ХЗ, чего там англоязычные считают советом, а что прямым указанием.
Так или иначе я оставил только проверку SSPIF, как в вашем примере. Чтение ACK делать не стану, за ненадобностью на данное время.
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 04:49.
|
|