Перейти к содержанию

Как подружить STM32F103 и MAX7219?


Eddy_Em

Рекомендуемые сообщения

Вожусь со светодиодной матрицей (поочередно соединены четыре матрицы 8х га MAX7219. И в мануале на STM32F103 вообще не вижу прерывания SPI по окончанию передачи данных! Есть по TXE, но в это время передача еще идет. Данные передаю при помощи DMA, а MAX7219 требует сразу после последнего тактового импульса поднять ногу CS, чтобы захватить данные из буфера.
А еще у меня почему-то все данные, приходящие на вход, идут на выход, хотя последние 16 бит должны захватываться (в даташите на MAX7219 черным по-белому сказано, что при опущенном CS данные на выход идут лишь после 16.5 тактов)!

Как с этим бороться нормальным способом? Чтобы не по-абдурински, используя блокирующие вызовы, а с помощью DMA и конечных автоматов?

Вот, что получается на выходе первого блока при посылке сообщений на 2 (должно быть только последние 16 бит), на шум в канале CS не обращайте внимания (я забыл взять на работе второй щуп, поэтому вместо щупа просто провод засунул):

 

IMG_20210313_131107.jpg

Изменено пользователем Eddy_Em
Ссылка на комментарий
Поделиться на другие сайты

31 минуту назад, Eddy_Em сказал:

 Есть по TXE, но в это время передача еще идет.

TXE - Это флаг показывающий опустошение буфера передачи, поэтому и передача еще идет, когда он взводится. (При передачи данных из буфера в сдвиговый регистр этот флаг взводится, но передача из сдвигового регистра продолжается)

34 минуты назад, Eddy_Em сказал:

Данные передаю при помощи DMA, а MAX7219 требует сразу после последнего тактового импульса поднять ногу CS, чтобы захватить данные из буфера.

Для отслеживания окончания передачи используй флаг BSY, он взводится с последним передаваемым битом. И тут нет костылей

Ссылка на комментарий
Поделиться на другие сайты

Так я и использую:

void MAX7219_process(){
    static int linenum = 0; // current processing line number
    static uint16_t sendreg[NSCREENS];
    if(s == S_RELAX) return;
    if(rdy2sendNXT){ // send next line
        if(linenum == 8){
            linenum = 0;
            s = S_RELAX;
            return;
        }
        rdy2sendNXT = 0;
        for(int i = 0; i < NSCREENS; ++i){
            sendreg[i] = ((linenum + 1) << 8) | screenbuf[linenum][i];
        }
        ++linenum;
        CLEAR(CS);
        if(SPI_transmit(sendreg, NSCREENS)){ // error, start from 0
            rdy2sendNXT = 1;
            SET(CS);
            linenum = 0;
            SPI_setup();
            s = S_RELAX;
            return;
        }
    }else{
        if(!(SPI1->SR & SPI_SR_BSY)){ // last data transmitted
            SET(CS);
            rdy2sendNXT = 1;
            USB_send("Nxt\n");
        }
    }
}

Однако, вот это SET(CS), появляющееся после обнаружения отсутствия флага BSY, выполняется через произвольный момент времени (т.к. я вызываю MAX7219_process из вечного цикла в main). А по даташиту нужно вздернуть CS в единицу сразу же.

Судя по тому, что при включении светодиодная матрица иногда рандомно загорается, она таки рабочая. Но вот данные принимать отказывается. Кроме того, судя по даташиту, как я уже говорил, при подаче на первую MAX 32 бит данных, на выходе мы должны получить лишь 16 бит. А у меня выход полностью идентичен входу.

Ссылка на комментарий
Поделиться на другие сайты

Сравнительное тестирование аккумуляторов EVE Energy и Samsung типоразмера 18650

Инженеры КОМПЭЛ провели сравнительное тестирование аккумуляторов EVE и Samsung популярного для бытовых и индустриальных применений типоразмера 18650. 

Для теста были выбраны аккумуляторы литий-никельмарганцевой системы: по два образца одного наименования каждого производителя – и протестированы на двух значениях тока разряда: 0,5 А и 2,5 А. Испытания проводились в нормальных условиях на электронной нагрузке EBD-USB от ZKEtech, а зарядка осуществлялась от лабораторного источника питания в режиме CC+CV в соответствии с рекомендациями в даташите на определенную модель. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Новый аккумулятор EVE серии PLM для GSM-трекеров, работающих в жёстких условиях (до -40°С)

Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре. 

Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств. Подробнее параметры и результаты тестов новой серии PLM по ссылке.

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

26 минут назад, Eddy_Em сказал:

if(!(SPI1->SR & SPI_SR_BSY)){ // last data transmitted

 

А почему вы проверяете через if, а не в while ?

Вы получается не ждете установку флага, а просто проверяете ?

Изменено пользователем john2103
Ссылка на комментарий
Поделиться на другие сайты

Литиевые батарейки и аккумуляторы от мирового лидера  EVE в Компэл

Компания Компэл, официальный дистрибьютор EVE Energy, бренда №1 по производству химических источников тока (ХИТ) в мире, предлагает продукцию EVE как со склада, так и под заказ. Компания EVE широко известна в странах Европы, Америки и Юго-Восточной Азии уже более 20 лет. Недавно EVE была объявлена поставщиком новых аккумуляторных элементов круглого формата для электрических моделей «нового класса» компании BMW.

Продукция EVE предназначена для самого широкого спектра применений – от бытового до промышленного. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

55 минут назад, Eddy_Em сказал:

 


        if(SPI_transmit(sendreg, NSCREENS))	{ // error, start from 0
            rdy2sendNXT = 1;
->            SET(CS);
            linenum = 0;
            SPI_setup();
            s = S_RELAX;
            return;
        }
    }else{
        if(!(SPI1->SR & SPI_SR_BSY)){ // last data transmitted
 ->           SET(CS);
            rdy2sendNXT = 1;
            USB_send("Nxt\n");
        }

Однако, вот это SET(CS), появляющееся после обнаружения отсутствия флага BSY, выполняется через произвольный момент времени (т.к. я вызываю MAX7219_process из вечного цикла в main). А по даташиту нужно вздернуть CS в единицу сразу же.

Извините, у вас в коде два раза встречается SET(CS). Вы уверены, что это происходит именно в тот момент, как вы ожидаете?
Вы дебаггером смотрели, это именно в этом месте происходит?

В референса в главе 25.3.9 SPI communication using DMA написано следующее:
"In transmission mode, when the DMA has written all the data to be transmitted (flag TCIF is
set in the DMA_ISR register), the BSY flag can be monitored to ensure that the SPI
communication is complete. This is required to avoid corrupting the last transmission before
disabling the SPI or entering the Stop mode. The software must first wait until TXE=1 and
then until BSY=0."

Т.е., они предлагают сначала дождаться появления флага TXE, а потом - сброса флага BSY.
 

Ссылка на комментарий
Поделиться на другие сайты

Точно! Но вот так

if(SPI1->SR & SPI_SR_TXE && !(SPI1->SR & SPI_SR_BSY))

все равно не помогло. Попробовал в блокирующем режиме - данные передаются (и, что интересно, после того, как микросхема получит новые данные, старые она тоже выплевывает дальше, т.е. можно таким образом грубую бегущую строку строить).

Получилось передавать регистры "тест". И данные пошли, но почему-то  только нулевая строчка. Разбираюсь дальше.

Ссылка на комментарий
Поделиться на другие сайты

5 минут назад, LiVit сказал:

Т.е., они предлагают сначала дождаться появления флага TXE, а потом - сброса флага BSY.

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

Если конечно там правда требования прям сразу.

Ссылка на комментарий
Поделиться на другие сайты

1 час назад, Eddy_Em сказал:

Однако, вот это SET(CS), появляющееся после обнаружения отсутствия флага BSY, выполняется через произвольный момент времени (т.к. я вызываю MAX7219_process из вечного цикла в main).

Слушайте, а каковы промежутки времени между вызовами этого MAX7219_process? Не может быть такого, что вы вызываете эту процедуру с произвольными промежутками, поэтому и происходит непонятное?

Как вариант могу предложить ужас ужасный - в прерывании от DMA по окончанию передачи сидите и ждете пока флаг BSY не будет сброшен.

Это, конечно, лютая дичь.... но как вариант?
Другой вариант - в том же прерывании от DMA взводится некий флаг, а ваш конечный автомат увидев этот флаг, бросает все дела и вызывает процедуру MAX7219_process вне очереди.

Ссылка на комментарий
Поделиться на другие сайты

Ах вот оно что! Оказывается, по умолчанию у MAX7219 в регистре SCAN_LIMIT стоит 0, т.е. отображается только первая строчка на экране. Победил!

Ссылка на комментарий
Поделиться на другие сайты

1 час назад, Eddy_Em сказал:

Так я и использую:


void MAX7219_process(){
    static int linenum = 0; // current processing line number
    static uint16_t sendreg[NSCREENS];
    if(s == S_RELAX) return;
    if(rdy2sendNXT){ // send next line
        if(linenum == 8){
            linenum = 0;
            s = S_RELAX;
            return;
        }
        rdy2sendNXT = 0;
        for(int i = 0; i < NSCREENS; ++i){
            sendreg[i] = ((linenum + 1) << 8) | screenbuf[linenum][i];
        }
        ++linenum;
        CLEAR(CS);
        if(SPI_transmit(sendreg, NSCREENS)){ // error, start from 0
            rdy2sendNXT = 1;
            SET(CS);
            linenum = 0;
            SPI_setup();
            s = S_RELAX;
            return;
        }
    }else{
        if(!(SPI1->SR & SPI_SR_BSY)){ // last data transmitted
            SET(CS);
            rdy2sendNXT = 1;
            USB_send("Nxt\n");
        }
    }
}

 

Если я понял правильно принцип Вашей программы, то у Вас в main идет вызов функции "MAX7219_process()" ,а в этой функции идет просто проверка

если нет строки то выход;

если есть строка то формируем ее и передаем как я понимаю иначе

проверяем установку занятости и выход

 

А если в момент когда вы проверяете занятость, еще идет передача???, то он просто возвращается в main и все, SET_CS не формируется.

А сформируется он только при следующем заходе в эту функцию.

Ну это если я правильно понял принцип.

 

2 минуты назад, Eddy_Em сказал:

Ах вот оно что! Оказывается, по умолчанию у MAX7219 в регистре SCAN_LIMIT стоит 0, т.е. отображается только первая строчка на экране. Победил!

А сейчас у Вас запустилось с While или без этого работает ?

Ссылка на комментарий
Поделиться на другие сайты

Сейчас все работает в таком виде:

void MAX7219_process(){
    static int linenum = 0; // current processing line number
    static uint16_t sendreg[NSCREENS];
    if(s == S_RELAX) return;
    if(rdy2sendNXT){ // send next line
        if(linenum == 8){
            linenum = 0;
            s = S_RELAX;
            return;
        }
        rdy2sendNXT = 0;
        for(int i = 0; i < NSCREENS; ++i){
            sendreg[i] = ((linenum + 1) << 8) | screenbuf[linenum][i];
        }
        ++linenum;
        CLEAR(CS);
        if(SPI_transmit(sendreg, NSCREENS)){ // error, start from 0
            linenum = 0;
            MAX7219_setup();
            return;
        }
    }else{
        if(SPI1->SR & SPI_SR_TXE && !(SPI1->SR & SPI_SR_BSY)){ // last data transmitted
            SET(CS);
            rdy2sendNXT = 1;
#ifdef EBUG
            USB_send("Nxt\n");
#endif
        }
    }
}

Вот так отправляет данные по SPI:

uint8_t SPI_transmit(const uint16_t *buf, uint8_t len){
    if(!buf || !len) return 1; // bad data format
    if(SPI_status != SPI_READY) return 2; // spi not ready to transmit data
    DMA_SPI_Channel->CCR &=~ DMA_CCR_EN;
    DMA_SPI_Channel->CMAR = (uint32_t)buf;
    DMA_SPI_Channel->CNDTR = len;
    SPI_status = SPI_BUSY;
    DMA_SPI_Channel->CCR |= DMA_CCR_EN;
    return 0;
}

И вот так выставляет флаг по готовности DMA:

void dma1_channel3_isr(){
  if(DMA1->ISR & DMA_ISR_TCIF3){
    DMA1->IFCR |= DMA_IFCR_CTCIF3; // Clear TC flag
    SPI_status = SPI_READY;
  }
}

Сейчас еще какой-нибудь "спецэффект" добавлю и выложу ссылку на гитхаб.

Изменено пользователем Eddy_Em
Ссылка на комментарий
Поделиться на другие сайты

Я может не прав, но при понижении скорости spi, по идее пойдут опять танцы с ,cs-set, что бы уйти от этой привязки к скорости надо или while использовать и ждать чётко или прерывания, в остальных случаях скорость МК и скорость spi , и содержимое Майн могут сильно влиять на cs set

Ссылка на комментарий
Поделиться на другие сайты

В данном случае все нормально: установка CS приводит к переписыванию данных из сдвигового регистра во внутренний. Ее можно делать в любой момент после заполнения сдвигового регистра.

Причем, судя по моим опытам, игры с CS никак не влияют на содержимое сдвигового регистра. А я думал вначале, что он обнуляется после копирования во внутренний.

Выкладываю материал: в ЖЖ.

Изменено пользователем Eddy_Em
Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...