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

STM32F103 SPI Slave Проблемы с выходом из прерывания


carlogulliani

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

в main.c есть бесконечный цикл, где постоянно опрашиваются датчики, управляется светодиодами и тд. И вот когда срабатывает прерывание на приемку SPI, после чтения всех байтов - управление не возвращается и бесконечный цикл не возобновляется (светодиоды не мигают а находятся в последнем состоянии перед прерывание, принт не печатает). Пакет данным у меня 32 байта, при этом в самом прерывании все 32 байта считываются.

Если убрать while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); в ф-ции SPI_RW(), то бесконечный цикл работает, но каждый байт считывается по много раз (иногда слишком много) - то есть проблема либо с этим циклом, либо с приоритетом прерывания у SPI, пробовал ему 7 выставлять - то же самое.

Я собрал тестовый стенд с минимум кода, где эта проблема воспроизводится

// SPI.c
#include "SPI.h"
#include "stdio.h"
#include "stm32f10x.h"

void SPI_Init(void) {
    GPIO_InitTypeDef GPIO_InitDef;
	SPI_InitTypeDef SPI_InitDef;

	// initialize init structs
	GPIO_StructInit(&GPIO_InitDef);
	SPI_StructInit(&SPI_InitDef);

	// initialize clocks
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);

	// initialize A4/SS alternate function open-drain (50 MHz)
	GPIO_InitDef.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6;
	GPIO_InitDef.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitDef);

	// initialize A5/SCK alternate function open-drain (50 MHz)
	GPIO_InitDef.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitDef);

	//  initialize SPI slave
	// for slave, no need to define SPI_BaudRatePrescaler
	SPI_InitDef.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitDef.SPI_Mode = SPI_Mode_Slave;
	SPI_InitDef.SPI_DataSize = SPI_DataSize_8b; // 8-bit transactions
	SPI_InitDef.SPI_FirstBit = SPI_FirstBit_MSB; // MSB first
	SPI_InitDef.SPI_CPOL = SPI_CPOL_Low; // CPOL = 0, clock idle low
	SPI_InitDef.SPI_CPHA = SPI_CPHA_1Edge; // CPHA = 1
	SPI_InitDef.SPI_NSS = SPI_NSS_Soft; // use hardware SS
	SPI_InitDef.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; // APB2 72/64 = 1.125 MHz
	SPI_InitDef.SPI_CRCPolynomial = 7;
	SPI_Init(SPI1, &SPI_InitDef);

	SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);

	SPI_Cmd(SPI1, ENABLE);
	NVIC_EnableIRQ(SPI1_IRQn);
	NVIC_SetPriority(SPI1_IRQn, 7);
	printf("SPI bus init success...\r\n");
}

void SPI_RW() {
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_IT_RXNE) == RESET);
    return SPI_I2S_ReceiveData(SPI1);
}
// main.c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_spi.h"
#include "SPI.h"

#ifdef OS_USE_SEMIHOSTING
    extern void initialise_monitor_handles(void);
#endif

static void SysTickConfig(void) {
  /* Setup SysTick Timer for 10ms interrupts  */
  if (SysTick_Config(SystemCoreClock / 1000))
  {
    /* Capture error */
    while (1);
  }
  /* Configure the SysTick handler priority */
  NVIC_SetPriority(SysTick_IRQn, 1); // if I change Priority to 0 then I can't catch SPI' interruption
}

uint8_t RX_LEN = 32;
uint8_t RX_BUF[RX_LEN] = {0};

void print_buf(uint8_t len) {
    for (uint8_t i=0; i<len; i++) {
        printf("0x%02x \r\n", RX_BUF[i]);
    }
    printf("\r\n");
}

void SPI1_IRQn(void) {
    for (uint8_t i=0; i<RX_LEN; i++) {
        RX_BUF[i] = SPI_RW();
    }
    print_buf(RX_LEN);
}

void main() {
    /* Enable semihosting */
    #ifdef OS_USE_SEMIHOSTING
        initialise_monitor_handles();
    #endif
    /* SysTickConfig */
    SysTickConfig();

    SPI_Init();

    while(1) {
        printf("hello\r\n");
    }
}

Помогите разобраться и пофиксить этот баг

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

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

А этот код даже работал? Его компилятор не пропустит.

Для начала убрать явные ошибки вроде:

void SPI_RW() {
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_IT_RXNE) == RESET);
    return SPI_I2S_ReceiveData(SPI1);
}

Или:

uint8_t RX_LEN = 32;
uint8_t RX_BUF[RX_LEN] = {0};

Потом выкинуть StdPeripheralLibrary и переписать код на регистрах, или на HAL, в крайнем случае.
Ну и разобраться как должны быть настроены GPIO для работы SPI.
 

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

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

6 minutes ago, Вуйко said:

А этот код даже работал? Его компилятор не пропустит.

Для начала убрать явные ошибки вроде:


void SPI_RW() {
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_IT_RXNE) == RESET);
    return SPI_I2S_ReceiveData(SPI1);
}

Или:


uint8_t RX_LEN = 32;
uint8_t RX_BUF[RX_LEN] = {0};

Потом выкинуть StdPeripheralLibrary и переписать код на регистрах, или на HAL, в крайнем случае.
Ну и разобраться как должны быть настроены GPIO для работы SPI.
 

Код работает, до момента приемки данных.

А в чем "явные ошибки"???

Что не так с GPIO? На текущий момент есть задача использовать stdperph, причем тут HAL?

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

Особенности хранения литиевых аккумуляторов и батареек

Потеря емкости аккумулятора напрямую зависит от условий хранения и эксплуатации. При неправильном хранении даже самый лучший литиевый источник тока с превосходными характеристиками может не оправдать ожиданий. Технология, основанная на рекомендациях таких известных производителей литиевых источников тока, как компании FANSO и EVE Energy, поможет организовать правильный процесс хранения батареек и аккумуляторов. Подробнее>>

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

13 часов назад, carlogulliani сказал:

while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_IT_RXNE) == RESET);

проблема видимо в этой строчке. Так как RESET у вас константа, то прога просто считывает один раз статус SPI и цикл while замирает в бесконечной истине

нужно как то заставить прогу думать, что все это дело волатильное и меняется железом, а значит должно считываться каждый раз при применении операции к ним

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

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

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

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

void SPI_RW() {
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_IT_RXNE) == RESET);
    return SPI_I2S_ReceiveData(SPI1);
}

Функция возвращает void, но return не пустой. void должен быть заменен на тип идентичный возвращаемому функцией SPI_I2S_ReceiveData(SPI1).

Иначе в буффер данных будет записан мусор.

uint8_t RX_LEN = 32;
uint8_t RX_BUF[RX_LEN] = {0};

Инициализация массивов переменной длинны вообще стандартом языка С запрещена. Добавление const тут не поможет. RX_LEN должен быть задан через #define.

Все эти ошибки компилятор прекрасно отлавливает.

Ну и править код прямо в первом посте непринято. Да еще и снова на неправильный.

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

7 часов назад, mail_robot сказал:

проблема видимо в этой строчке. Так как RESET у вас константа, то прога просто считывает один раз статус SPI и цикл while замирает в бесконечной истине

нужно как то заставить прогу думать, что все это дело волатильное и меняется железом, а значит должно считываться каждый раз при применении операции к ним

Все регистры периферии и так уже volatile

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

регистры да, переменные и функции их использующие не всегда

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

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

Проблема решилась. В SPI1_IRQHandler я считывал данные в цикле, надо было писать в буффер, инкрементируя его индекс. Вроде получается, что пришёл один байт, а я в цикле его BUF_LEN раз читаю. Ещё переписал немного обработчик, доберусь до компа, выложу решение

 

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

2 hours ago, carlogulliani said:

Проблема решилась. В SPI1_IRQHandler я считывал данные в цикле, надо было писать в буффер, инкрементируя его индекс. Вроде получается, что пришёл один байт, а я в цикле его BUF_LEN раз читаю. Ещё переписал немного обработчик, доберусь до компа, выложу решение

 

Вот решение

void SPI1_IRQHandler(void)
{
		if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE))
	    {
		    RX_BUF[index] = SPI_I2S_ReceiveData(SPI1);
	        index++;
	        if(index >= RX_LEN) {
	        	index = 0;
	        	print_buf(32);
	        }
	    }
}

Спасибо за помощь. Код в первом посте действительно с ошибками, так как из проекта выдирал куски и дописывал уже в дело стенде недостающие объявления, где-то глаз замылился

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

Один байт пришел на спи - одно прерывание.

Вошли в обработчик прерывания, сохранили байт в буфер, инкрементировали счетчик буфера, завершили обработчик прерывания.

Вообще внутри обработчика использовать циклы это не лучшее решение.

А в данном примерк еще и ошибка

 

Ps. Инкремент счетчика .. 

#define MAX_BUF  64

uint32_t idx;

 

В прерывании инкремент счетчика кольцевого буфера

idx = (idx+1) % MAX_BUF;

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

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

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

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

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

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

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

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

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

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

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

    • @Romanchek82 в данной ситуации скорее "подложена под ножку стола у дядюшки Ляо")
    • КИТ на Озоне, 2 канала за 500р. РФ. Я сам с конструктора начинал, там и опыт паять получите.  не надо с платы, схемы начинать, блок питания сначала соберёте, КИТ распаяйте, в корпус всё оформите. Одно до ума изделие доведите. Там и поймёте, куда дальше двигаться. А по схеме, ЕЩЁ раз, они есть на любое ОУ в даташит. По "ушникам", пятиножки в бОльшем случае - достаточно (по ссылке) прежде, чем Сухову, Нечаеву,.. в ж. "Радио" писать, готовился, после школы в библиотеку, вечерами за паяльник... пока обратная связь приходила (письма), уже многое исправил, да, не методом тыка, а через понимание физических процессов.  Быстро это только ЕГЭ, описательный, творческий процесс отсутвует (мозгами шевелить, статьи анализировать, углубляться и видеть перспективу).  Паять совет не нужен, ручками, ручками, готовый кит и в корпус.
    • Привет!  Мне не известно, есть ли другие варианты формата прошивки. Почитайте ветку , может кто то и выкладывал.
    • Совершенно точно. И об этом и сам разработчик (ца) подтвердила о силовой плате. Вот файл pdf от разработчика похоже, где то попалось... Сопротивление , импеданс входа операционника надо учитывать как то. По взодам стоят резисторы по 20к (делители), как то многовато для ОУ на биполярниках. В буржуйских схемах и в даташитах начиная с легендарного ОУ 741 стоят не более 10к по входу, пробовал вместо 20к ставить 10к, но это отдельная тема.... И при правильном проектировании замена ОУ на аналогичный не должно влиять на параметры (настройки) схемы. В наем случаее решил заменить ОУ LM324 (бытовку) на LM224 (промышленную) ... Поставил панельку и при смене разных партий 224 и 124 (милитари) настройка тока уходила в ощутимо по индикатору. Ставил LM-ки от томсон и семикондуктор. Семикондуктор резко уходили насторойки, а томсон более близко к заводской настойке. С завода стояла 324 от томсон. Как и что не буду расписывать по этим двум фирмам. Подобрал балее , менее приемлимые 2 корпуса 224, один из частотника, второй из автомобильного реле. Крутить подстроечники на силовой плате не стал особо, т.к. это сложный случай и методики настойки нет и методом тыка не было времени и желания упражнятся. хотя с завода настройка не радует и желательно откорректировать.... термистор поставил  MF52 на 100к и прилепил на термопасту к радиатору VD1. Его тоже заменил на 60А - 100В и падению напряджния по мультиметру в 2 раза меньше, чем заводской. Индуктивность L2 проводом 1,9 мм 19 витков, замер показал 41,5 мкГн (пинцет Smart Tweezers Канада) заменена на на такое же салатовое кольцо (есть в загажнике) намотанно линцедратом 43х0,22 мм. Поместились все 19 витков как задская намотка один в один. Нагрузочный резистор R68 390 Ом заменил на 4 запаралеленных 1,2к 1 Вт. Конденсатор С20 1000х25 заменил на твёрдотельный 1000х50 и вынес от радиаторов. С9 и С10 заменил на твёрдотельные 47х50.  С5 заменил на два в параллель 150х450 и зашунтировал плёночной ёмкостью 2,5х630, чтобы облегчить жизнь электролитам....Поставил синфазный Др на синем колечке по 20 витков МГТФ 0,2 , индуктивность 5 мГн. Добавил варистор 20Т471 (470 В). Добавил сетевой выключательи дополнительную колодку с предохранителем 5А, штатный заменил на твёрдотельный 3,15А. Нагружал на 20А при 12 В - в течении часа всё работает, чуть тёплый воздух из  корпуса. ничего не кипятится не греется критично. ЗУ Вымпел-30.pdf Для справки, вроде как от разработчика(цы), утащено - Вымпел 30 ....
    • 01 — копия.lay6 amp_tda7377.pdf Назовём это "тестовый вариант по турецким мотивам". По идее должно норм ? 1к1 всё равно только рейсфедером нарисуешь.  Решено взять оттуда только УМ, фильтр wm019, питание внешний трансформатор. 
    • Новички собирают схемы из батарейки и лампочки! А не предусилители..
×
×
  • Создать...