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

Запускаем новый BLE 5.2-чип BlueNRG-LP от STMicroelectronics

Любая разработка начинается с чтения документации и изучения доступных средств разработки. Данный материал целиком посвящен средствам разработки, включая детальные инструкции по запуску вашего первого приложения на BlueNRG-LP. Описана работа с отладкой STEVAL-IDB011V1, набором инструментов и пакетом ПО позволяющим разработчику быстро войти в курс дела.

Подробнее

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

Революция в силовой электронике. Начало

Что привлекает в SiC по сравнению с кремнием, и какие особенности делают компоненты SiC часто используемыми, несмотря на более высокую стоимость в сравнении с кремниевыми высоковольтными устройствами? – Объясняет специалист ведущего разработчика силовых приборов из карбида кремния, компании Infineon.

Подробнее

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

Материалы вебинара Практическое использование TrustZone в STM32L5

Материалы вебинара, посвященного экосистеме безопасности и возможностях, которые дает новая технология TrustZone в МК STM32L5, содержат две подробные практические работы: создание простого приложения с изоляцией в TrustZone, и пример отладки и тестирования TFM-SBSFU. Программа рассчитана на технических специалистов и тех, кто уже знаком с основами защиты ПО в STM32.

Подробнее

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

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

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

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

Share this post


Link to post
Share on other sites

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


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

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...

  • Сообщения

    • Я написал про это выше про тему про коллайдер.                                                                                                                                   Там разогнанные электроны полем могут врезаться в пластину, и застревая в ней, могут накапливаться и этим увеличивать в ней количество заряда. Разгон разностью потенциалов электрона в вакууме затруднительно в моём случае, потому что моя пластина в районе одного квадратного метра, а может в процессе наладки схемы быть и больше. И это будет кинескоп от телика на несколько метров из толстого стекла    Потребуется очень дорогой заказ со стекольного завода такого произведения , плюс многие квадратные метры для расположения этого. Вот я думаю, можно электроны разгонять высоким напряжением вырванным из атомов воздуха, или из нагретой до красна тены, или всё вместе. Например, есть такая схема.                                                                                                         В лампе между горячим катодом и сеткой её создаётся высокое напряжение. Электроны испаряемые горячим катодом сильно притягиваются сеткой и этим самым сильно притягиваются к ней. Потом при подходе к ней их скорость доходит почти до скорости света. И они из-за этого не успевают на ней затормозить и пролетают сквозь неё на анод лампы. Этим анодом можно сделать одиночную пластину для накачивания этими электронами, то есть большим количеством заряда. То есть надо просто поставить в воздухе сетку заряженную высоки положительным потенциалом, например Вольт 100 000. А за ней расположить пластину. Может перед этой заряженной сеткой добавить нагретую спираль для подсоса с ней электронов. Часть электронов эта сетка буде брать из этой спирали нагретой а часть из воздуха. И эти разогнанные электроны сеткой, будут не успевать затормозить на ней и будут пролетать к пластине в которую будут врезаться и накапливаться там, повышая этим там количество заряда.               Но эта положительная сетка будет с этой отрицательной пластиной образовывать ведь конденсатор, опят двух сторонняя поляризация, а нужна односторонняя, одиночной пластины. Получается какой то замкнуты круг
    • Kodkey, а какая задача стоит? Может можно проще сделать?
    • Да это идея, мне нужна как раз заряженная отрицательно пластина.                                                                                              Работа коллайдера основана на разгоне частиц. Так можно последовательными высоковольтными обмотками разгонять электроны в одиночную пластину. Они же будут ударяясь об неё застревать в ней. Так они со временем накопятся в ней, что даст большое количество заряда на этой одиночной пластине.      Да Ван дер Граф накачивает заряды, но он их накачивает по уровню их (потенциал их, напряжение), а не по количеству, на одиночную пластину (шар) его.  Увеличивающееся напряжение там приводит, к увеличению количества заряда. Например по логике, глубина дырки атома это уровень напряжения. Чем глубже дырка атома тем большее количество электронов там не хватает. А это большее количество их и есть большее количество зарядов, что доказывает, что повышение напряжения (потенциала пластины) приводит к повышению на ней количества заряда, без всякого внешнего приложенного поля конденсатора. Но это на напряжении до 7ми миллионов Вольт. Но по логике если стеклянной щёткой терануть в нём по резиновой ленте, то мы в ней выдерем электроны в ней. Дырки образующиеся на атомах резиновой ленты прикладываются через вторую щётку на шар одиночную пластину. С этого шара электроны стекают (заряжая его положительно), на эти дырки атомов в резиновой ленте. Так постепенно идёт отсос электронов с этого шара одиночной пластины до очень глубоких дырок его атомов, глубиной в 7 миллиардов Вольт. Но этот отсос осуществляется с шара противовесом поля второй пластиной (вторым шариком) как в конденсаторе, на которую уходят эти электроны. Без этого разгона внешнего поля, тирки щётки всего на десятки Вольт не перейдут на 5-7 миллионов Вольт шара. Так же эта вторая пластина (небольшой шарик), на рисунке номер 8, служит для проскакивания искры, демонстрации набранного большого напряжения:   Так, что видно на этом рисунке, что здесь не одиночная пластина, а конденсатор. А мне нужна одиночная пластина для односторонней поляризации. Может быть этот Вандеграфф может набрать количество заряда, и без второй пластины (шарика)       Я про это забыл, спасибо что напомнили Вот если по этому методу, одиночную пластину сделать в виде банки (шара),  а вторую пластину поместить в центр её. Тогда внутренняя пластина в банке будет воздействовать своим полем на эту банку, накачивая туда заряды по количеству как в конденсаторе. А эта  внешняя банка, по правилу: -"Внутри проводящих материалов поля нет",  не будет создавать поле на внутреннюю пластину. И тогда эта внешняя банка накачается большим количеством заряда как в конденсаторе, но она будет вести себя как одиночная пластина. Поле у неё будет только с одной стороны, снаружи. Будет нужная  односторонняя поляризация, как в одиночной пластине которая нужна.
    • @stixo ,подсказать тут невозможно от слова вообще. Приборы-опыт в ремонте есть? Судя по написанному без огорчений дешевле и проще обратитесь к внятному специалисту,сэкономите время и дензнаки. Удачи! Дефект поддается ремонту
    • Прогар убирай,все верно. Непредсказуемые последствия 101% точно. Ну и секс с паяльником по закону подлости как только установишь это в авто
    • Жив еще курилка Тембр то? Это хорошо. Попробуйте тут ознакомиться http://rt22.ru/viewtopic.php?f=14&t=30195 https://rt20.getbb.ru/viewtopic.php?f=5&t=18401&start=50&st=0&sk=t&sd=a Может что отыщется. Навскидку сложно сказать,слишком много времени прошло с последнего Тембра  
×
×
  • Create New...