Некоторые особенности среднего семейства PIC


Разрабатывая устройства на основе микроконтроллеров PIC я иногда сталкивался с каким-то, порой, совершенно невероятным поведением моих устройств, причём, на первый взгляд всё выглядело так, будто ошибок в схеме и программе никаких нет.

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

Нижеописанное актуально для основной части МК из семейства PIC16 и PIC18, однако также могут быть и некоторые различия - если у вас не получается решить свою проблему, на всякий случай прочтите ещё раз даташит на свою модель и проверьте лишний раз код.

Беспричинные перезагрузки

При разработке плат для моделей, имеющих вывод PGM следует подтягивать этот вывод к питанию через внешний резистор 15-30 кОм. В противном случае могут наблюдаться рандомные беспричинные перезагрузки контроллера. Я не видел упоминания данного факта в каком-либо даташите, возможно потому что смотрел не внимательно, а возможно потому что этот факт описан в каком-нибудь отдельном документе, скажем на ICSP.
Так или иначе, когда я не знал этой особенности, я потратил около месяца на выяснение причины перезагрузки одного из моих первых устройств на PIC.

-------

Зависания и проблемы с UART

При инициализации последовательного порта следует дёрнуть бит CREN:

CREN = 0;
CREN = 1;


Иначе возможны ошибки в работе порта.



Также полезно добавить куда-нибудь в цикл проверку ошибки переполнения:

if (OERR) { CREN = 0; CREN = 1; } // Сброс ошибки переполнения

-------

Задание скорости работы UART

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

Предпочитаю делать так:

// Глобальные константы
const unsigned char BRGH_STATE = 1;
const unsigned char BRG16_STATE = 1;

// В данном примере использовано значение SPBRG из даташита, равное 4165 (в десятичной системе)
const unsigned char SPBRG_H = 0x10;// Расчётное значение старшего байта SPBRG
const unsigned int SPBRG_L = 0x45;// Расчётное значение младшего байта SPBRG

// При инициализации установить требуемую скорость передачи с помощью регистра SPBRG и бита BRGH
TX9 = 0;
SYNC = 0; // Выбрать асинхронный режим сбросом бита SYNC в ‘0’
BRGH = BRGH_STATE;
BRG16 = BRG16_STATE;
SPBRGH = SPBRG_H;
SPBRG = SPBRG_L;


---------

Косяки Timer1

В Errata к PIC18F2580 написано что таймер в каких-то случаях может глючить, поэтому писать всегда следует в TRM1L сразу после TMR1H:

TMR1H = TIMER1_H;
TMR1L = TIMER1_L;
TMR1IF = 0; // Сброс флага прерывания


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

На всякий случай я так делаю для любого 16-бит таймера.