Jump to content
carlogulliani

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

Recommended Posts

в 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");
    }
}

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

Edited by carlogulliani

Share this post


Link to post
Share on other sites

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

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

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.
 

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites

Высококачественные конденсаторы Panasonic для надежности вашей электроники!

Электролитические алюминиевые конденсаторы Panasonic отличаются повышенной надежностью, длительным сроком службы, низким импедансом и выдерживают большой ток пульсаций, в то время как семейства полимерных конденсаторов Panasonic SP-CAP, POSCAP, OS-CON и HYBRID характеризуют сверхнизкий ESR и увеличенная емкость, работа при высоких напряжениях и в расширенном температурном диапазоне. Приобретая продукцию Panasonic, вы гарантированно получаете самое передовое решение для ваших задач. Для облегчения вашего выбора, мы подготовили подборку полезных материалов.

Читать статьи

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

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

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

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

Share this post


Link to post
Share on other sites
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.

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

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

Share this post


Link to post
Share on other sites
                     

Вебинар "Как создать BLE-устройство на базе новейшего беспроводного микроконтроллера STM32WB55"

27 ноября 2019 года компания КОМПЭЛ приглашает разработчиков, технических руководителей и энтузиастов беспроводной связи на вебинар, посвященный новинке 2019 года – мультипротокольному беспроводному микроконтроллеру STM32WB55, который позволяет создавать устройства на базе стандартов BLE 5.0; BLE Mesh; 802.15.4/ZigBee и Thread. На вебинаре мы покажем, как с помощью привычных инструментов STM32Cube и STM32CubeMX можно создать свое первое, надежно работающее BLE-приложение.

Зарегистрироваться на вебинар

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

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

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

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

Share this post


Link to post
Share on other sites

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

 

Share this post


Link to post
Share on other sites
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);
	        }
	    }
}

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

Share this post


Link to post
Share on other sites

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

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

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

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

 

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

#define MAX_BUF  64

uint32_t idx;

 

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

idx = (idx+1) % MAX_BUF;

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...