Продолжаем работу с передачей данных по шине SPI между двумя контроллерами STM32F1. И на данном уроке мы объединим наши знания по шине SPI и периферии DMA в контроллере STM32 и попробуем применить технологию DMA при передаче данных по интерфейсу SPI. Хотя мы тем же самым занимались в уроке 159 и уроке 160, но только применяли мы при этом библиотеки HAL и LL, а сегодня уже будем применять библиотеку CMSIS, которая, как известно, простотой программирования также, как и LL, не блещет, к тому же инициализацию периферии нам тоже придётся писать самим, но зато она позволяет нам ещё более гибко настроить периферию, а также более гибко пользоваться аппаратной частью в процессе работы написания кода программы.
С DMA мы уже знакомы неплохо, мы с ней работали в любых режимах, причём с применением библиотеки CMSIS тоже работали в уроке 176, только в режиме MEM2MEM, в остальных режимах работали с применением библиотек HAL и LL.
Тем не менее, когда мы работали с SPI и DMA с применением библиотеки LL, инициализацию, автоматически сгенерированную при помощи Cube MX, мы всю просматривали и анализировали, поэтому нам не составит много труда проделать это и с применением библиотеки CMSIS.
Схема урока осталась та же самая, что и в прошлом уроке
Сначала мы займёмся ведущим устройством, проект для которого мы сделаем из проекта урока 175 с именем CMSIS_SPI_MASTER и назовём его CMSIS_SPI_MASTER_DMA.
Подключим к ПК наше ведущее устройство, ведомое пока не подключаем.
Откроем наш проект в Keil и в файле main.c добавим два буфера, один для передачи, другой для приёма, а также парочку глобальных переменных, одну — для счёта и другую — для отслеживания событий
1 2 3 4 5 |
__IO uint32_t SysTick_CNT = 0; uint16_t src_buf[128] = {0}; uint16_t dst_buf[128] = {0}; uint32_t full_cnt=0; uint8_t fl=0; |
В функции SPI1_Init поднимем в самое начало функции включение тактирования порта
1 2 |
//SPI1 GPIO SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN); |
Так же, как и в уроках с использованием библиотек HAL и LL, мы будем работать с DMA1 и её каналами 2 и 3, которые, соответственно, будут отвечать за приём и передачу данных.
Данные мы также будем передавать пакетами по 128 полуслов.
Выше функции main() добавим функцию инициализации DMA1, в которой мы только включим тактирование на данную периферию и разрешим глобальные прерывания от каналов 2 и 3
1 2 3 4 5 6 7 8 9 10 11 |
//---------------------------------------------------------- static void DMA1_Init(void) { //DMA controller clock enable SET_BIT(RCC->AHBENR, RCC_AHBENR_DMA1EN); tmpreg = READ_BIT(RCC->AHBENR, RCC_AHBENR_DMA1EN); (void)tmpreg; NVIC_EnableIRQ(DMA1_Channel2_IRQn); NVIC_EnableIRQ(DMA1_Channel3_IRQn); } //---------------------------------------------------------- |
Вернёмся в функцию SPI1_Init, в которой у нас и будет находиться основная инициализация каналов 2 и 3.
Начнём с канала 2, отвечающего за приём данных, а вернее за передачу данных из периферии SPI в оперативную память с помощью DMA.
Выберем направление передачи данных
1 2 3 4 5 |
tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_SPI2EN); //SPI1 DMA Init //SPI1_RX Init //Set transfer direction (Peripheral to Memory) CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_DIR | DMA_CCR2_MEM2MEM); |
Настроим приоритет
1 2 3 |
CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_DIR | DMA_CCR2_MEM2MEM); //Set priority level CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_PL); |
Выберем обычный режим (не циклический)
1 2 3 |
CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_PL); //Transfer mode NORMAL CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_CIRC); |
Периферию не инкрементируем
1 2 3 |
CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_CIRC); //Set peripheral no increment mode CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_PINC); |
Память инкрементируем
1 2 3 |
CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_PINC); //Set memory increment mode SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_MINC); |
Размер пакета (ширина) — полуслово и для источника и для приёмника
1 2 3 4 5 |
SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_MINC); //Set peripheral data width MODIFY_REG(DMA1_Channel2->CCR, DMA_CCR2_PSIZE_1, DMA_CCR2_PSIZE_0); //Set memory data width MODIFY_REG(DMA1_Channel2->CCR, DMA_CCR2_MSIZE_1, DMA_CCR2_MSIZE_0); |
Аналогично настроим и канал 3, который будет отвечать за передачу данных из оперативной памяти в буфер SPI (за приём данных по шине SPI). Разница будет только в настройке направления передачи
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
MODIFY_REG(DMA1_Channel2->CCR, DMA_CCR2_MSIZE_1, DMA_CCR2_MSIZE_0); //SPI1_TX Init //Set transfer direction (Memory to Peripheral) MODIFY_REG(DMA1_Channel3->CCR, DMA_CCR3_MEM2MEM, DMA_CCR3_DIR); //Set priority level CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_PL); //Transfer mode NORMAL CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_CIRC); //Set peripheral no increment mode CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_PINC); //Set memory increment mode SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_MINC); //Set peripheral data width MODIFY_REG(DMA1_Channel3->CCR, DMA_CCR3_PSIZE_1, DMA_CCR3_PSIZE_0); //Set memory data width MODIFY_REG(DMA1_Channel3->CCR, DMA_CCR3_MSIZE_1, DMA_CCR3_MSIZE_0); |
В функции main() мы изменим имя локальной переменной
uint16_t i, n;
Вызовем функцию инициализации DMA1
1 2 |
GPIO_Init(); DMA1_Init(); |
Удалим вызов макроса старта модуля
SPI1_ENABLE();
Отключим пока оба канала DMA
1 2 3 4 |
delay_ms(2000); //Disable DMA channels CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_EN); CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_EN); |
Очистим флаги всех прерываний каналов
1 2 3 4 5 6 7 8 9 |
CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_EN); //Clear Channel 2 transfer complete flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTCIF2); //Clear Channel 2 transfer error flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTEIF2); //Clear Channel 3 transfer complete flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTCIF3); //Clear Channel 3 transfer error flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTEIF3); |
Разрешим запросы от каналов 2 и 3 DMA1 для SPI1
1 2 3 4 5 |
WRITE_REG(DMA1->IFCR, DMA_IFCR_CTEIF3); //Enable DMA Tx SPI1 SET_BIT(SPI1->CR2, SPI_CR2_TXDMAEN); //Enable DMA Rx SPI1 SET_BIT(SPI1->CR2, SPI_CR2_RXDMAEN); |
Разрешим прерывания по событиям ошибки и окончания передачи для каналов DMA
1 2 3 4 5 6 7 8 9 |
SET_BIT(SPI1->CR2, SPI_CR2_RXDMAEN); //Enable Transfer complete interrupt Channel2 SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_TCIE); //Enable Transfer error interrupt Channel2 SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_TEIE); //Enable Transfer complete interrupt Channel3 SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_TCIE); //Enable Transfer error interrupt Channel3 SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_TEIE); |
Включим SPI1
1 2 |
SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_TEIE); SPI1_ENABLE(); |
Из бесконечного цикла всё удалим и заполним буфер передачи значениями
1 2 3 4 5 6 7 |
while(1) { //fill src buffer for(i=0;i<128;i++) { src_buf[i] = full_cnt + i; } |
На время отключим пока каналы DMA
1 2 3 4 5 |
src_buf[i] = full_cnt + i; } //Disable DMA channels CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_EN); CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_EN); |
Занесём в соответствующие регистры каналов количество полуслов, которое мы хотим передать через данные каналы
1 2 3 4 |
CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_EN); //Set Number of data to transfer MODIFY_REG(DMA1_Channel2->CNDTR, DMA_CNDTR2_NDT, 128); MODIFY_REG(DMA1_Channel3->CNDTR, DMA_CNDTR3_NDT, 128); |
Занесём адреса приёмника и источника в соответствующие регистры каналов
1 2 3 4 5 6 |
MODIFY_REG(DMA1_Channel3->CNDTR, DMA_CNDTR3_NDT, 128); //Configure the Source and Destination addresses WRITE_REG(DMA1_Channel2->CPAR, (uint32_t)&(SPI1->DR)); WRITE_REG(DMA1_Channel2->CMAR, (uint32_t)&dst_buf); WRITE_REG(DMA1_Channel3->CPAR, (uint32_t)&(SPI1->DR)); WRITE_REG(DMA1_Channel3->CMAR, (uint32_t)&src_buf); |
Опустим ножку выбора шины SPI
1 2 |
WRITE_REG(DMA1_Channel3->CMAR, (uint32_t)&src_buf); CS1_SET(); |
Включим каналы DMA
1 2 3 4 |
CS1_SET(); //Enable DMA channels SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_EN); SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_EN); |
Добавим в самом низу файла обработчик прерываний от канала 2, отвечающего за приём данных, нашего DMA, в котором мы в случае установленного флага окончания передачи данных сбросим данный флаг и поднимем ножку выбора шины SPI, а в случае установленного флага ошибки вызовем инструкцию nop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//---------------------------------------------------------- void DMA1_Channel2_IRQHandler(void) { if(READ_BIT(DMA1->ISR, DMA_ISR_TCIF2) == (DMA_ISR_TCIF2)) { //Clear Channel 2 global interrupt flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CGIF2); CS1_RESET(); } else if(READ_BIT(DMA1->ISR, DMA_ISR_TEIF2) == (DMA_ISR_TEIF2)) { __NOP(); } } //---------------------------------------------------------- |
Добавим аналогичный обработчик прерываний от канала 3, в котором, в отличии от предыдущего обработчика, мы уже не будем поднимать ножку выбора, а установим наш пользовательский флаг
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//---------------------------------------------------------- void DMA1_Channel3_IRQHandler(void) { if(READ_BIT(DMA1->ISR, DMA_ISR_TCIF3) == (DMA_ISR_TCIF3)) { //Clear Channel 3 global interrupt flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CGIF3); fl = 1; } else if(READ_BIT(DMA1->ISR, DMA_ISR_TEIF3) == (DMA_ISR_TEIF3)) { __NOP(); } } //---------------------------------------------------------- |
Вернёмся в бесконечный цикл функции main() и дождёмся там установки нашего флага и заново его обнулим
1 2 3 |
SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_EN); while(!fl) {} fl=0; |
Затем отобразим на индикаторе принятые и переданные данные
1 2 3 4 5 6 7 |
fl=0; for(n=0;n<128;n++) { NumberL_7219(dst_buf[n]); NumberR_7219(src_buf[n]); delay_ms(50); } |
Подождём 1 секунду
1 2 3 |
delay_ms(50); } delay_ms(1000); |
Добавим 128 к нашему счётчику переданных полуслов
1 2 |
delay_ms(1000); full_cnt += 128; |
Обнулим его при достижении им порога значений
1 2 |
full_cnt += 128; if(full_cnt>=8191) full_cnt = 0; |
Вот и весь код для устройства MASTER. Соберём его, прошьём контроллер, отключим от ПК наше ведущее устройство, подключим его к независимому источнику энергии, а к ПК подключим ведомое устройство.
Проект для ведомого устройства был сделан из из проекта того же урока 175 с именем CMSIS_SPI_SLAVE и назовём его, соответственно, CMSIS_SPI_SLAVE_DMA.
Откроем данный проект в Keil и также добавим 2 буфера и переменную для пользовательского флага
1 2 3 4 |
__IO uint32_t SysTick_CNT = 0; uint16_t src_buf[128] = {0}; uint16_t dst_buf[128] = {0}; uint8_t fl=0; |
Также добавим выше функции main() функцию инициализации DMA1, точь в точь такую же как и в проекте ведущего устройства
1 2 3 4 5 6 7 8 9 10 11 |
//---------------------------------------------------------- static void DMA1_Init(void) { //DMA controller clock enable SET_BIT(RCC->AHBENR, RCC_AHBENR_DMA1EN); tmpreg = READ_BIT(RCC->AHBENR, RCC_AHBENR_DMA1EN); (void)tmpreg; NVIC_EnableIRQ(DMA1_Channel2_IRQn); NVIC_EnableIRQ(DMA1_Channel3_IRQn); } //---------------------------------------------------------- |
В функции SPI1_Init проинициализируем каналы 2 и 3 периферии DMA1, код инициализации такой же как и в проекте для устройства MASTER
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 |
tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SPI1EN); //SPI1 DMA Init //SPI1_RX Init //Set transfer direction (Peripheral to Memory) CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_DIR | DMA_CCR2_MEM2MEM); //Set priority level CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_PL); //Transfer mode NORMAL CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_CIRC); //Set peripheral no increment mode CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_PINC); //Set memory increment mode SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_MINC); //Set peripheral data width MODIFY_REG(DMA1_Channel2->CCR, DMA_CCR2_PSIZE_1, DMA_CCR2_PSIZE_0); //Set memory data width MODIFY_REG(DMA1_Channel2->CCR, DMA_CCR2_MSIZE_1, DMA_CCR2_MSIZE_0); //SPI1_TX Init //Set transfer direction (Memory to Peripheral) MODIFY_REG(DMA1_Channel3->CCR, DMA_CCR3_MEM2MEM, DMA_CCR3_DIR); //Set priority level CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_PL); //Transfer mode NORMAL CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_CIRC); //Set peripheral no increment mode CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_PINC); //Set memory increment mode SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_MINC); //Set peripheral data width MODIFY_REG(DMA1_Channel3->CCR, DMA_CCR3_PSIZE_1, DMA_CCR3_PSIZE_0); //Set memory data width MODIFY_REG(DMA1_Channel3->CCR, DMA_CCR3_MSIZE_1, DMA_CCR3_MSIZE_0); |
В функции main() изменим имя локальной переменной
uint16_t i, n;
Вызовем функцию инициализации DMA1
1 2 |
GPIO_Init(); DMA1_Init(); |
Удалим вот этот код
i=0; r=0;
Также удалим вызов макроса старта модуля
SPI1_ENABLE();
Удалим задержку
delay_ms(2000);
Заполним пока буфер передачи какими-нибудь значениями, например числами от 128 до 1 в обратном порядке
1 2 3 4 5 6 |
Init_7219(); //fill src buffer for(i=0;i<128;i++) { src_buf[i] = 128 - i; } |
Настроим каналы DMA точно так же, как и в проекте ведущего устройства, и включим периферию SPI1
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 |
Number_7219(87654321); //Disable DMA channels CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_EN); CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_EN); //Clear Channel 2 transfer complete flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTCIF2); //Clear Channel 2 transfer error flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTEIF2); //Clear Channel 3 transfer complete flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTCIF3); //Clear Channel 3 transfer error flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CTEIF3); //Enable DMA Tx SPI1 SET_BIT(SPI1->CR2, SPI_CR2_TXDMAEN); //Enable DMA Rx SPI1 SET_BIT(SPI1->CR2, SPI_CR2_RXDMAEN); //Enable Transfer complete interrupt Channel2 SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_TCIE); //Enable Transfer error interrupt Channel2 SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_TEIE); //Enable Transfer complete interrupt Channel3 SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_TCIE); //Enable Transfer error interrupt Channel3 SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_TEIE); SPI1_ENABLE(); |
Из бесконечного цикла пока удалим весь код и произведём там настройку каналов таким же образом как и в проекте устройства MASTER, только здесь мы не опускаем ножку выбора, она у нас управляется другим устройством
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
while(1) { //Disable DMA channels CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR2_EN); CLEAR_BIT(DMA1_Channel3->CCR, DMA_CCR3_EN); //Set Number of data to transfer MODIFY_REG(DMA1_Channel2->CNDTR, DMA_CNDTR2_NDT, 128); MODIFY_REG(DMA1_Channel3->CNDTR, DMA_CNDTR3_NDT, 128); //Configure the Source and Destination addresses WRITE_REG(DMA1_Channel2->CPAR, (uint32_t)&(SPI1->DR)); WRITE_REG(DMA1_Channel2->CMAR, (uint32_t)&dst_buf); WRITE_REG(DMA1_Channel3->CPAR, (uint32_t)&(SPI1->DR)); WRITE_REG(DMA1_Channel3->CMAR, (uint32_t)&src_buf); //Enable DMA channels SET_BIT(DMA1_Channel3->CCR, DMA_CCR3_EN); SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_EN); |
В самом низу файла добавим обработчики прерываний от каналов DMA
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 |
//---------------------------------------------------------- void DMA1_Channel2_IRQHandler(void) { if(READ_BIT(DMA1->ISR, DMA_ISR_TCIF2) == (DMA_ISR_TCIF2)) { //Clear Channel 2 global interrupt flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CGIF2); } else if(READ_BIT(DMA1->ISR, DMA_ISR_TEIF2) == (DMA_ISR_TEIF2)) { __NOP(); } } //---------------------------------------------------------- void DMA1_Channel3_IRQHandler(void) { if(READ_BIT(DMA1->ISR, DMA_ISR_TCIF3) == (DMA_ISR_TCIF3)) { //Clear Channel 3 global interrupt flag WRITE_REG(DMA1->IFCR, DMA_IFCR_CGIF3); fl = 1; } else if(READ_BIT(DMA1->ISR, DMA_ISR_TEIF3) == (DMA_ISR_TEIF3)) { __NOP(); } } //---------------------------------------------------------- |
Код обработчиков практически такой же как и для устройства MASTER, только здесь мы не поднимаем ножку выбора в обработчике канала 2.
В бесконечном цикле функции main() дождёмся установки пользовательского флага и снова его сбросим
1 2 3 |
SET_BIT(DMA1_Channel2->CCR, DMA_CCR2_EN); while(!fl) {} fl=0; |
Заполним буфер для передачи ведущему устройствами значениями приёмного буфера, вычтенными из 8192
1 2 3 4 5 |
fl=0; for(i=0;i<128;i++) { src_buf[i] = 8192 - dst_buf[i]; } |
Отобразим на индикаторе принятые и переданные числа
1 2 3 4 5 6 7 8 |
src_buf[i] = 8192 - dst_buf[i]; } for(n=0;n<128;n++) { NumberR_7219(dst_buf[n]); NumberL_7219(src_buf[n]); delay_ms(50); } |
Соберём код, прошьём контроллер и посмотрим результат обмена данными между устройствами
Показания приёмного индикатора на ведущем будут всегда отставать на 128 от ведомого, так как мы там отображаем предыдущий пакет.
Посмотрим теперь обмен в программе логического анализа
Мы видим, что передаваемые числа следуют в пакетах друг за другом непрерывно.
А здесь мы видим, что у нас ничего не пропускается и не выпадает
А вот здесь мы видим, что ножке по окончанию пакета теперь поднимается вовремя
Итак, на данном уроке мы отработали механизм передачи данных по шине SPI с применением технологии DMA. Зная уже всю аппаратную часть, нам гораздо легче было написать код, чем если бы начинали с нуля. Также на данном уроке мы заканчиваем цикл уроков по шине SPI контроллера STM32F1 с применением библиотеки CMSIS. Конечно, это вовсе не означает, что мы в дальнейшем перестанем пользоваться в уроках данной шиной и технологией DMA. Просто нам теперь будет гораздо легче ориентироваться в написании кода для данных немаловажных видов периферии контроллера. Думаю, что нам очень скоро это пригодится, так как нам предстоит вскоре поработать с дисплеем LCD TFT под управлением контроллера ILI9341, подключенного именно по шине SPI. Такой дисплей у меня наконец-то появился. Подключим мы его к плате STM32F4-Discovery, с которой мы когда-то очень долго работали, причём работали с таким же дисплеем, но подключенным по 8-битной шине. Не каждому удалось заиметь такой дисплей, многие по ошибке, а многие и не по ошибке приобрели дисплей, именно подключенный по шине SPI и просили меня сделать такой урок. Конечно же на такие массовые просьбы я просто не мог не откликнуться, мне прислали именно такой дисплей и мы теперь с ним поработаем, начнём с библиотеки HAL, причём сначала без DMA, потом применим DMA для того, чтобы сравнить, что нам даст эта периферия. А потом, возможно применим библиотеки LL и CMSIS, с которыми мы пока ещё с контроллером F4 ни разу не работали. Вот заодно и сравним разницу применения данных библиотек по отношению к разным контроллерам.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Исходный код для ведущего устройства (MASTER)
Исходный код для ведомого устройства (MASTER)
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Индикатор светодиодный семиразрядный с драйвером MAX7219
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
Здравствуйте!
Спасибо за уроки!
У меня вот какой вопрос. Я настроил размер передаваемых данных в SPI как SPI_DataSize_16b. Когда (в ручном режиме) записываю двухбайтное число в SPI1->DR, данные благополучно уходят как нужно (режим SPI_FirstBit_MSB). Как пример, отправляю 0x4020, получаю 0x4020 (смотрю по логическому анализатору). А вот когда настраиваю SPI с DMA где DMA_PeripheralDataSize_HalfWord и DMA_PeripheralDataSize_HalfWord и указываю 1 посылку (т.е те же 2 байта) происходят чудеса (по таблице выравниваний которая в Reference Manual, п 13.3.4, все должно быть ок), если отправляю 0x4020 то получаю 0x0020. Перепробовал все что знаю. Не подскажите, пожалуйста, в чем причина?
Забавно, сейчас прочитал и понял в чем ошибка. Я указал размер периферии и памяти как — DMA_PeripheralDataSize_HalfWord.
Будет ли урок по работе в CMSIS c I2S через DMA?