USI as SPI
USI as SPI
Для работы с трансивером нам необходим интерфейс SPI. Извлекать его будем всё из того же модуля USI. Здесь кратенько. Линии данных у SPI однонаправленные, а это значит, что пин DO всегда подключен к концу сдвигового регистра, а DI - к началу. Таким образом организован режим передачи full duplex. Здесь нам не нужны никакие подтягивающие резисторы, т.к. линия ВСЕГДА либо в нуле, либо в единице. А когда мы не работаем по интерфейсу SPI, то нам и пофигу, что творится на линиях. Здесь только про режим master. Здесь нет пина SS - соответственно нет и проблем, которые он создает в аппаратном SPI (пин SS всегда должен быть сконфигурирован как выход, если мы работаем ТОЛЬКО как мастер. Как только на SS приходит низкий уровень, аппаратный SPI бросает всё и переключает себя в режим SLAVE). Трансивер по интерфейсу SPI способен работать со скоростями вплоть до 10 МГц. Поэтому наша задача организовать тактирование максимально энергоэффективно ни теряя понапрасну ни единого такта.
Начнем с функции инициализации.
//... Инициализация SPI void SPI_Init(void) { SPI_DDR |= (1<<MOSI) | (1<<SCK) | (0<<MISO); // Все выводы, кроме MISO, выходы // SPI_PORT |= (0<<MOSI) | (0<<SCK) | (0<<MISO); USICR = (1<<USIWM0) | (1<<USICLK); }
Здесь не нужны ни счетчик, ни его убогие флаги, ни флаги старта, стопа - всё это идет лесом.
Далее идет функция отправки элементарного байта.
//... Передать байт данных void SPI_WriteByte(uint8_t data) { uint8_t clk0 = (1<<USIWM0) | (1<<USITC); uint8_t clk1 = (1<<USIWM0) | (1<<USITC) | (1<<USICLK); //... Копируем байт в регистр USIDR USIDR = data; USICR = clk0; // Режим SPI, тактируем шину сами USICR = clk1; USICR = clk0; USICR = clk1; USICR = clk0; USICR = clk1; USICR = clk0; USICR = clk1; USICR = clk0; USICR = clk1; USICR = clk0; USICR = clk1; USICR = clk0; USICR = clk1; USICR = clk0; USICR = clk1; }
Здесь в регистр USIDR пишем наш байт и начинаем дергать за веревочку. Как писал выше, при тактировании от USICLK нам необходимо самим писать в этот бит то "0", то "1". Нам надо 8 тактов, поэтому за веревочку дергаем аж 16 раз!
Так сложилось исторически, но функция отправки и получения байта отдельно
//... Передать и получить байт данных uint8_t SPI_ReadByte(uint8_t data) { //... Отправляем байт SPI_WriteByte(data); //... Принятый байт возвращаем return USIDR; }
Функция отправки массива байт
//... Отправить несколько байт по SPI. cmd - команда, data - данные для отправки void SPI_WriteArray(uint8_t cmd, uint8_t num, uint8_t *data) { nRF_SELECT(); //... Отправим команду SPI_WriteByte(cmd); //... Затем данные while(num--) { SPI_WriteByte(*data++); } nRF_DESELECT(); }
И напоследок макросы
#define nRF_SELECT() ClearBit(nRF_CSN_PORT, nRF_CSN) #define nRF_DESELECT() SetBit(nRF_CSN_PORT, nRF_CSN)
#define nRF_CE_PORT PORTB // #define nRF_CE_DDR DDRB // #define nRF_CSN_PORT PORTB #define nRF_CSN_DDR DDRB
#define Bit(bit) (1<<(bit)) #define ClearBit(reg, bit) reg &= (~(1<<(bit))) //пример: ClearBit(PORTB, 1); //сбросить 1-й бит PORTB #define SetBit(reg, bit) reg |= (1<<(bit)) //пример: SetBit(PORTB, 3); //установить 3-й бит PORTB
7 Комментариев
Рекомендуемые комментарии
Присоединяйтесь к обсуждению
Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.