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

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?

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

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

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

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

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

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

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

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

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

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

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 пользователей онлайн

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

    • @korsaj Так сейчас вчитывался ещё раз и ничего как то на ум так и не идёт. Сложновато получается. Данную функцию да тем более с массивами на сравнение я точно не осилю.
    • Собрал тиристорное зарядное. И не совсем понятно как измерять его ток заряда. Вот так показывают два неплохих мультиметра на измерении постоянного тока  (уверен что стрелочный покажет то же самое )   но , Мы же понимаем , что после тиристора идёт не постоянный ток , а импульсный  и вот такое теперь показывают мультиметры в режиме измерения переменного тока  один РМС, другой не рмс.   Что считать правдой ?
    • А чего бы не поставить рядом с DS18B20 некрупный конденсатор между VDD и GND? 
    • Я вскрываю металлической линейкой. Очень часто у таких блоков есть паз вдоль шва, туда вставляю линейку по всей длине шва и как рычагом вскрываю корпус. Если вначале немного промять корпус вдоль шва (бывает достаточно пальцами), то шансы легко вскрыть значительно увеличиваются.
    • При включении в ранее опубликованном виде устройство стало генератором. Частота до 140кГц, на индуктивности размах (от мин. до макс.) до 20В при напряжении на испытуемом БП ~3В. Добавил несколько конденсаторов, в том числе как рекомендовали Z_VIP и Starichok. Немного изменил номиналы резистора в компараторе. Удалил шунтирующее сопротивление на резисторе регулятора тока. Компаратор буду еще отлаживать - в приведённой ниже схеме срабатывает при 4В, но не не отпускает при отсутствии напряжения.  Ограничение тока выставил на 4А. Регулировка тока плавная от 0 до  4А. Выставленный ток стабильный при изменении напряжения от 1В до 10В. Напряжение выше 10В пока не поднимал. Ниже 0,95В ток снижается пропорционально напряжению. Схема в приложении. Все изменения-дополнения делал на существующей плате навесным монтажом. Позже выложу актуальную разводку. Отлаживал с транзисторами AP60T30 (ранее выкладывал фото транзисторов на радиаторе) - без обдува при комнатной температуре длительно держат 20Вт (10В, 2А) при температуре радиатора ~40гр.C. Приобрёл транзисторы IRFP460. С радиатором пока не определился. Схема и фото отладки на текущий момент времени. На фото стрелочный прибор - амперметр с током полного отклонения 6А. и в DipTrace:  
    • Ведь этож надо... такое совпадение. И я тоже делаю без сверлений. Более того, прилагаю фото имеющихся у меня  "хоть чуток острых предметов", несмотря на наличие которых, столь несложный и общедоступный  способ зачистки, к.м.к.  имеет право на демонстрацию в данной теме. Вдруг кому-то пригодится... Тем более, что тема простаивает аж с марта, будто на белом свете нет ничего, что можно было бы сюда  разместить,  хотя бы для сведения. 
×
×
  • Создать...