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

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

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