Jump to content
Cujo

Realtime SSI slave 18bit+ STM3L4

Recommended Posts

Собственно задача соорудить SSI slave Transmit only с асинхронным обновлением в реальном времени.
Контроллер сейчас STM32L433CC. 
Дано один вход SCLK до 2 МГц, один выход SOUT(он жеMISO). Формат посылки 18, 20,22 бит.
Обновление через ~15мкс после последнего такта SCLK, либо каждые 7,5 мкс.
Стандартный интерфейс для быстрых датчиков.
Как делал 
1: Без DMA и прерываний, по прерываниям от TIM_ETR на SCLK. Режим без NSS, с NSS по первому SCLK и сбросом.
Проблема нельзя узнать, записались ли данные в SPI->DR, соответственно первые байты могли быть не обновлены, а следующие обновлялись.
2: DMA с NSS по таймеру. Проблема: DMA не обновляет SPI в реальном времени, а только после отправки прошлой посылки, а нужно обновлять каждые 7,5 мкс вне зависимости есть ли запросы. 
Если пробовать сбрасывать SPI и обновлять DMA, случаются смещения на 1 такт, а потому как реакция ядра на запрос ~ 150-200 нс, + обновление ~ 150-200нс. В итоге облом.

Какие есть решения, может кто сталкивался?

 

Снимок.PNG

Edited by Cujo

Share this post


Link to post
Share on other sites
В 10.11.2018 в 21:31, Cujo сказал:

нельзя узнать, записались ли данные в SPI->DR,

Это как это? Если инструкция записи выполнена - данные записались!

Для того чтобы узнать когда они вышли на шину полностью есть флаг прерывания, что еще надо???

Вообще зачем нужен таймер? Используйте флаг опустошения буфера! Скорость SPI- настраивается, в том числе через задержки между байтами.

И для ДМА, кстати, тоже флаг опустошения буфера нужно использовать.

Edited by ruhi
дополнено

Можно сделать все! Но чем больше можно, тем больше нельзя!

Share this post


Link to post
Share on other sites

Изготовление 2-х слойных плат от 2$, а 4-х слойных от 5$!

Быстрое изготовление прототипа платы всего за 24 часа! Прямая доставка с нашей фабрики!

Смотрите видео о фабрике JLCPCB: https://youtu.be/_XCznQFV-Mw

Посетите первую электронную выставку JLCPCB https://jlcpcb.com/E-exhibition чтобы получить купоны и выиграть iPhone 12, 3D-принтер и так далее...

на самом деле проблема уже решилась с помощью трех таймеров, программного прерывания 3х внешних соединений и кучи костылей, подробнее описал на easyelectronics, если интересно

 

Проблема в том что SPI->DR обновлять можно. Те на SPI будут выдаваться последний записанных DR. Но когда приходит такт SCLK, регистр блокируется и запись в него не осуществляется. И узнать об этом никак нельзя. А обновлять данные нужно не дожидаясь прихода SCLK, он может не приходить сколь угодно долго, но данные должны быть все время актуальные.

Дело в том, что на этом контроллере есть еще 2 потока с периодом вызова 7 и 14 мкс. Останавливать их никак нельзя, и главные прежде всего они. SPI нужен чтобы вывести информацию о их вычислениях, те о угле либо положении.  Стандартный протокол выдачи в таких случаях - это SSI, есть еще BISS C - тот же проприетарный SSI с контрольной суммой. 

В общем, тему можно закрывать. Плодить костыли по интернету как-то не хочется. STM сказал: "он не рекомендует использовать их МК для реализации SSI и работоспособность такой системы не гарантирована".

Share this post


Link to post
Share on other sites

Вебинар «Практическое использование TrustZone в STM32L5»(10.12.2020)

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

Подробнее

Guest Kost286

Если ещё интересно. Недавно пришлось с датчиком линейного перемещения столкнуться. Протокол SSI.

Сделал 2 варианта: 1)с использованием таймера и внешнего прерывания ; 2) с использованием SPI. 

С SPI конечно получше, больше времени на выполнение других задач остаётся, так как вместо 48 прерываний по передаче и ещё 25 прерываний по приёму для получения одного значения это очень много.

При использовании SPI только 2 прерывания. Или вообще без них.

Вот пример с SPI:

Настройка SPI

void Init_SPI2(void){
	/* Configure SPI pins: SCK and MOSI with default alternate function (not re-mapped) push-pull */
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_13 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	/* Configure MISO as Input with internal pull-up */
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	// SPI configuration
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 64000kHz/256=250kHz
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	//SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI2, &SPI_InitStructure);
	SPI_CalculateCRC(SPI2, DISABLE);
	SPI_NSSInternalSoftwareConfig(SPI2, SPI_NSSInternalSoft_Set);
	SPI_Cmd(SPI2, ENABLE);
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) { ; }
}

Обработка

volatile uint32_t u32ssiResult;
 uint16_t u16singleTurn;
 uint16_t u16multiTurn;
 char cBuffer[32];
volatile uint32_t u32result = 0;

uint32_t spiReadSSI( void )
{
 uint8_t u8byteCount;

 for (u8byteCount=0; u8byteCount<2; u8byteCount++)
 {
	 // отправить что-нибудь для запуска тактирования
	 SPI_I2S_SendData(SPI2, 0xffff);
	 u32result <<= 16; 
	 while( !(SPI2->SR & SPI_SR_TXE) || SPI2->SR & SPI_SR_BSY);
	 u32result |= SPI_I2S_ReceiveData(SPI2);
 }

 u32result >>= 7; // убираю часть LSBs, можно не убирать, просто у меня мусор там
 return u32result;
}
int main(void)
{
	 Init_clock();
	 Init_pins();
	 Init_SPI2();
	 Init_UART2();
	 delay_ms(50);
	 Init_IWDG(2000);
	 printf("START\r");

while(1)
    {
		delay_ms(100);
		IWDG_ReloadCounter();

		if (inc_sec++ >= 5){
			inc_sec = 0;
			u32ssiResult = spiReadSSI();
			
			u16singleTurn = u32ssiResult & 0x0000FFFF; //здесь я млачшую часть взял 2 байта
			u16multiTurn = (u32ssiResult >> 16) & 0x0000000F; //старшый, только 1 байт беру, дальше цифры уже не меняются когда линейка до краёв доходит
			//Это я разделил, чтобы по 2 байта передать по юарту
             printf( "MSB = " );
             sprintf( cBuffer, "%u ", u16multiTurn);
             printf( cBuffer );
             printf( "LSB = " );
             sprintf( cBuffer, "%u ", u16singleTurn);
             printf( cBuffer );
             printf(" \r");
		}
    }
}

Вот и всё. Схема подключения - просто через модули на MAX485. Только нужно с передающего выпаять если есть резистор на 120 ом, который параллельно A-B впаян и питать его 5 вольтами. И не забыть подать питание на выбор режима работы - передача.

Из приёмного модуля можно ничего не выпаявать, питать 3.3 вольтами, выбор режима работы подсоединить к минусу питания.

Share this post


Link to post
Share on other sites

Снижена цена на AC/DC и DC/DC преобразователи Mornsun в Компэл!

Компэл и компания Mornsun снизили цены на преобразователи AC/DC-преобразователи семейств LS и LDE. По привлекательной цене также предлагаются DC/DC-преобразователи изолированных семейств поколений R2 и R3 различного конструктивного исполнения.

Подробнее

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

  • Сообщения

    • Не забудьте при этом ещё, чтобы экран осциллографа попал в кадр в нормальном качестве, не квадратиками или размыто (не в фокусе и т.д.).
    • Но в таком датчике магнит сбоку. Кстати, в увлажнителях воздуха именно такие поплавковые датчики.
    • Приветствую всех. Собрал этот БП еще в 2016г.  по самой первой схеме. Все работает и все нравится. Спасибо Владимиру65 за схему и помощь в настойке. Понадобился еще один бп. Так как скопилось много IGBT хотелось бы попробовать их использовать. Какие изменения надо внести в схему(30в 2а)? (Самую  первую)
    • Забыл написать - ТП 0.2мА, 7.5мА и 113мА. Первые три картинки ХММ2. Я написал - эмиттерные резисторы 0.1 Ом. Чисто мультик амперметром - не охота переделывать и он может влиять в таком режиме. Вторые 3 картинки - 1Вт на выходе при соответствующем токе по порядку. Нижние (вторые) 3 картинки - смотреть только окошко фурье, т.к. остальное - остатки настройки в интерактивном моделировании и при анализе Фурье не работают. Можно было при 1Вт только окошки Фурье выложить, но, чтобы не было разночтений откуда картинка - выложил скрины. Надо было закрыть лишнее при фурье... Сорри. Симу (ли плоттеру Боде) пофиг. Он сразу АЧХ рисует. Наверно, при определённом сигнале проверяет. От уровня сигнала на входе ничего не меняется на графике. С плоттером в любой схеме так. АЧХ и иже с ней рисует сразу, никаких выходов на режимы, ожиданий, уровней и т.п. А вообще - это настройка ТП. Не заморачиваюсь с замыканием выхода, отключением генератора и т.д. И плоттер ведь сравнивает вход с выходом - чего сравнивать с КЗ на входе, если генератор отключать и замыкать вход? А резистор ставить, эквивалентный выходному генератора - гемор, да и зачем, если сам генератор уже подключен. Ставлю 0 амплитуды - он сам рисует 1фВ. Нельзя чисто 0 на входе. Настроил ТП - на вход 135мВ и режим Фурье (на выходе (или где надо смотреть Фурье)должен быть пробник) - пуск.
    • С чего это, если ГСТ питаются от стабов на 18В? Там мощность не меняется 0,1Вт.
    • А в нашем доме где сейчас живу(6 этажей)по пректу есть мусоропровод и шахты лифтов,но они превращены в кладовки на момент строительства, а поэтому есть купленная  кладовка (лифт)и незаконная кладовка где труба мусоропровода без открывашки, каждая 2х2 метра, я очень рад этому, весь гемор там
    • Есть мысли купить с ними приборчик, но вот незадача, говорят все old-stock оптопары этой серии мертвые by default. Очень интересно, где их можно заказать, не подскажите? Это-же как РВ-5А... купить можно, но только заведомо труп. По этому на их место сразу ставлю панасоники. С первыми двумя приборами мне повезло, в них РВ-5А живые, а вот в третьем трупики... отдам на переработку...

  • Блоки питания DC5V2A; DC12V/1A; DC24V/0.5A

  • Similar Content

    • By n_angelo
      Привет, знатоки. Написал свою первую программу для контроллера STM8L152C6T6 (STM8L-Discovery). Это, собственно, моя первая программа для контроллеров вообще. Я многого не знаю и не понимаю. Возможно ваш ответ на мой вопрос будет банален.
      Используемая периферия: DAC, DMA, TIM4, CLK, GPIO
      Задача у программы такая:
      В EEPROM зашит один период синусоиды с дискретизацией 44100Гц. Период занимает ровно 101 байт, что по сути должно быть равно 2,29мс (1/44100*101). В коде программы только конфигурация периферии, одно прерывание на кнопке и пустой бесконечный цикл, который ничего не делает. Всю работу выполняет таймер, который настроен выдавать запрос к DMA на каждые 1/44100 (ядро тактируется 2мГц, таймер считает до 45). В свою очередь DMA забирает из EEPROM по одному байту на каждый запрос от таймера и передаёт его в DAC. Далее DAC выводит бесконечную синусоиду на ногу PF0. Прерывание на кнопке запускает весь этот механизм и зажигает светодиод.
      Проблема:
      Измеряя ногу PF0 осциллографом было замечено, что период синусоиды занимает около ≈4мс. Фото под катом.
      Меня это расстроило. Экспериментально выяснилось, что стоит только вписать в бесконечный цикл какую-нибудь проверку, например, [если значение текущего байта синусоиды = 0xFF, то зажечь светодиод, если 0x00, то потушить], то осциллограф показывает правильный тайминг в 2(с копейками)мс. В принципе в теле цикла может быть что угодно, кроме пустоты, и тайминг налаживается.
      Я не могу отдебажить дизассемблер, т.к. его не знаю. Это у меня в планах. Но я очень хочу понять, что происходит и почему пустой цикл рушит тайминг.
      Спасибо.
       
       
       
       
    • By Леонид:3
      Здравствуйте!
      Имеется задача записывать данные на какую либо флешку, для дальнейшего чтения этих данных на ЭВМ. Я решил вести запись на SD карточку через SPI(SDIO нет в моём камне), использую STM32f103c8. Пишу посекторно, без файловой системы(для проверки драйвера)
      Однако, столкнулся с проблемами
      1 - (sandisk 2 gb) на первой карточке время записи не одинаковое, от 5 до 65 миллисекунд, причем прослеживается хорошая такая периодичность, каждый 8й сектор пишется 65 миллисекунд, остальные по 5 мс. И черт бы с ним, но мне нужно писать на карту по 500 байт с частотой 100 гц. И задержка все портит. Так же спустя какое то время(секунды) непрерывной записи карточка отключается и её нужно снова инициализировать.
      2 - (Kingston 8gb)на второй карте, карта так же отключается спустя некоторое время(десятки секунд) непрерывной записи, и её снова нужно инициализировать.
      3 - (elite pri 4gb)на этой карте время записи само по себе большое(20 мс), и так же отключается при непрерывной записи.
      А так же есть карточка на 16 гб, которую не могу инициализировать.
      Может быть кто-нибудь подскажет куда капать.
      Имею предположение что кривой драйвер, но найти косяки не хватает знаний и опыта....
      Прикрепляю используемый драйвер.
      stm32_f10x_SD_SPI.c stm32_f10x_SD_SPI.h
    • By mr_smit
      Вынес то что не получается в упрощенной форме в отдельный проект. Среда разработки CooCox 1.7.8, микроконтроллер STM32F103C8T6.
      Нужно раз в ~100 мсек формировать на ножке МК, например,такую последовательность:

      Стартовую длительность формирует таймер, в первом же своем прерывании по совпадению активирует DMA и дальше уже DMA по запросу таймера загружает значение CCR из массива. Что то похожее на управление светодиодами WS2812B. То что я сочинил выдает на пин:

      Но только один раз при первом вызове. При последующих вызовах данные из массива выдаются без первоначальной длительности в 150 мкс.
      Не могу найти ошибку. 
       
      #include <stm32f10x.h> #include <stm32f10x_conf.h> #include <stm32f10x_gpio.h> #include <stm32f10x_rcc.h> #include <stm32f10x_tim.h> #include <stm32f10x_dma.h> GPIO_InitTypeDef PIN; TIM_TimeBaseInitTypeDef TIM_Config; TIM_OCInitTypeDef TIM_OCConfig; DMA_InitTypeDef DMA_Setting; uint8_t Test_Buf[] = {15,30,30,30,15}; void delay_ms(uint32_t ms) { volatile uint32_t nCount; RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq (&RCC_Clocks); nCount = (RCC_Clocks.HCLK_Frequency/10000)*ms; for (; nCount != 0; nCount--); } void Init_GPIO(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); PIN.GPIO_Pin = GPIO_Pin_11; // PA11 -> TIM1 Channel4 PIN.GPIO_Mode = GPIO_Mode_AF_PP; PIN.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &PIN); } void Init_TIM_Transmit(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); TIM_TimeBaseStructInit(&TIM_Config); // настройки по дефолту TIM_Config.TIM_Prescaler = 72-1; // Запускаем таймер на тактовой частоте 1 MHz (72000000/(72-1)) TIM_Config.TIM_Period = 150-1; // Период - 150 мкс TIM_Config.TIM_ClockDivision = 0; // частоту дополнительно не делим TIM_Config.TIM_CounterMode = TIM_CounterMode_Up; // считаем вверх TIM_TimeBaseInit(TIM1, &TIM_Config); // Инициализируем TIM1 TIM_OCStructInit(&TIM_OCConfig); // настройки по дефолту TIM_OCConfig.TIM_OCMode = TIM_OCMode_PWM1; // Конфигурируем как ШИМ (выравнивание по границе) TIM_OCConfig.TIM_OutputState = TIM_OutputState_Enable; // Включаем выход TIM_OCConfig.TIM_Pulse = 0; // CCR до старта пока нулевой TIM_OCConfig.TIM_OCPolarity = TIM_OCPolarity_High; // Полярность TIM_OCConfig.TIM_OCIdleState = TIM_OCIdleState_Reset; // состояние выхода по совпадению CCR (сброс) TIM_OC4Init(TIM1, &TIM_OCConfig); // Инициализируем 4-й выход таймера, это PA11 TIM_ARRPreloadConfig(TIM1,ENABLE); // Предзагрузка периода (ARR) TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); // Предзагрузка длины импульса CCR 4-го канала // (даем досчитать до конца и только потом значение меняется на новое) TIM_DMACmd(TIM1,TIM_DMA_CC4,DISABLE); // выключаем пока запрос к DMA от таймера TIM1 по достижении CCR) TIM_CtrlPWMOutputs(TIM1, ENABLE); // включаем выходы (это только для TIM1) TIM_CCxCmd(TIM1,TIM_Channel_4,TIM_CCx_Enable); // разрешаем таймеру управлять выводом PA11 TIM_ITConfig(TIM1, TIM_IT_CC4, DISABLE); // запрещаем пока таймеру генерировать прерывание по совпадению NVIC_EnableIRQ(TIM1_CC_IRQn); // разрешаем прерывания TIM_Cmd(TIM1, DISABLE); // Выключаем таймер (пока ждем) } void TIM1_CC_IRQHandler(void) // прошло 130 мкс { if (TIM_GetITStatus(TIM1, TIM_IT_CC4) != RESET) { // по совпадению TIM_ClearITPendingBit(TIM1,TIM_IT_CC4); // сбрасываем флаг прерывания TIM1 по совпадению } NVIC_EnableIRQ(TIM1_CC_IRQn); // выключаем прерывания от таймера TIM_ITConfig(TIM1, TIM_IT_CC4, DISABLE); // TIM1->ARR = 40-1; // устанавливаем период 40 мкс TIM1->CCR4 = Test_Buf[0]; // ширину из массива для следующего импульса DMA1_Channel4->CNDTR = 4; // длина данных для DMA на 1 меньше т.к. уже установили выше 1 элемент TIM_DMACmd(TIM1,TIM_DMA_CC4,ENABLE); // разрешаем таймеру делать запрос к DMA по совпадению CCR DMA_Cmd(DMA1_Channel4, ENABLE); // включаем DMA } void Init_DMA(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // включаем тактирование DMA1 DMA_Setting.DMA_PeripheralBaseAddr = (uint32_t) &TIM1->CCR4; // куда копировать DMA_Setting.DMA_MemoryBaseAddr = (uint32_t) &Test_Buf[1]; // что копировать DMA_Setting.DMA_DIR = DMA_DIR_PeripheralDST; // копируем в периферию (Peripheral Destination, точка назначения - периферия) DMA_Setting.DMA_BufferSize = 0; // количество передаваемых данных DMA_Setting.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // адрес периферии постоянный DMA_Setting.DMA_MemoryInc = DMA_MemoryInc_Enable; // адрес в памяти увеличиваем DMA_Setting.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // периферия 16 бит DMA_Setting.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // массив 8 бит DMA_Setting.DMA_Mode = DMA_Mode_Normal; // режим обычный DMA_Setting.DMA_Priority = DMA_Priority_Medium; // приоритет средний DMA_Setting.DMA_M2M = DMA_M2M_Disable; // MemoryToMemory откл. DMA_Init(DMA1_Channel4, &DMA_Setting); // TIM1_CH4 относится к 4-му каналу DMA1 DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); // настраиваем прерывание по окончанию передачи NVIC_EnableIRQ(DMA1_Channel4_IRQn); // включаем прерывания от 4-го канала DMA1 DMA_Cmd(DMA1_Channel4, DISABLE); // пока выключаем 4-ый канал DMA1 } void DMA1_Channel4_IRQHandler(void) // закончили передавать { if (DMA_GetITStatus(DMA1_IT_TC4) != RESET) { // по совпадению DMA_ClearITPendingBit(DMA1_IT_TC4); // сбрасываем флаг прерывания DMA1 Channel4 transfer complete } if (TIM_GetITStatus(TIM1, TIM_IT_CC4) != RESET) { // по совпадению TIM_ClearITPendingBit(TIM1,TIM_IT_CC4); // сбрасываем флаг прерывания TIM1 на всякий случай } TIM1->ARR = 150-1; // вновь настраиваем на период 150 мкс TIM1->CCR4 = 0; // и ждем следующею передачу TIM1->CNT = 0; // TIM_DMACmd(TIM1,TIM_DMA_CC4,DISABLE); // всё выключаем DMA_Cmd(DMA1_Channel4, DISABLE); // TIM_Cmd(TIM1, DISABLE); // TIM_ITConfig(TIM1, TIM_IT_CC4, DISABLE); // TIM_CCxCmd(TIM1,TIM_Channel_4,TIM_CCx_Disable); } int main(void) { Init_GPIO(); Init_TIM_Transmit(); Init_DMA(); delay_ms(1000); while(1) { TIM1->CCR4 = 130-1; // до включения линия удерживается в 0 (CCR=0) TIM_ITConfig(TIM1, TIM_IT_CC4, ENABLE); TIM_CCxCmd(TIM1,TIM_Channel_4,TIM_CCx_Enable); TIM_Cmd(TIM1, ENABLE); delay_ms(100); } }  
      TEST_TIM_DMA.zip
    • By Grampus
      Добрый день!
      ПОМОГИТЕ ПОЖАЛУЙСТА!
      в описании для одного дисплея нашел код для STM на СИ
      там есть строчка которая мне не понятна, точнее смысл ее понятен но нет объявления аргументов функции
      помогите пожалуйста. В общем ситуация такая 
       
      spi_write ( DTA, 0x00 )                                     spi_write ( CMD, 0x01) 
      вот эта функция
      DTA - выполняет установку пина в 1 ,    CMD -  выполняет установку пина в 0 
      0x00 , 0x01, .........0xFF     это либо данные либо команда. 
       и все бы ничего но все это нужно передать по HAL_SPI_Transmit 
      помогите написать эту функцию с описанием аргументов и всех действий.
    • By voltex
      Всем привет! Подскажите, пожалуйста, как правильно считать данные с внешней eeprom по шине spi, в данном случае 25LC256.
      Написал код ссылаясь на даташит. Собрал схему в протеусе, подключил spi отладчик и вот что получил в итоге. Так же не могу проверить получается записать данные в память или нет. Весь код прикрепил.
       

      main.c
×
×
  • Create New...