Easyelectronics.ru

Электроника для всех
Текущее время: 29 янв 2022, 15:45

Часовой пояс: UTC + 5 часов



JLCPCB – Прототипы печатных плат за $2/5шт. два слоя. $5/5шт. четыре слоя
Крупнейший производитель печатных плат и прототипов. Более 600000 клиентов и свыше 10000 заказов в день!
Получите скидку на почтовую отправку при первом заказе в JLCPCB!

Начать новую тему Ответить на тему  [ 1 сообщение ] 
Автор Сообщение
 Заголовок сообщения: STM8 как ведомое устройство SPI (SPL)
СообщениеДобавлено: 30 янв 2021, 15:17 
Здравствуйте!

Зарегистрирован: 30 янв 2021, 15:01
Сообщения: 2
Всем привет, домучил МК (stm8s003f3p6), Удалось установить стабильную качественную связь, даже почти "аппаратно".

Так как аппаратного NSS почему-то нету (либо я что-то делал не так), и это писали на большинстве зарубежных форумах, в том числе и про 32 камни, было реализовано внешнее прерывание на ногу которую обозначают в "даташитах" NSS (я думаю можно использовать любую). Прерывание настраивается на срабатывание по фронтам (переходам с высокого на низкий уровень и наоборот).

Код:
static void EXTI_Config(void)
{
  EXTI_DeInit();
  GPIO_Init(GPIOA, GPIO_PIN_3, GPIO_MODE_IN_PU_IT); // настраиваем как вход с подтяжкой к питанию
  EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOA, EXTI_SENSITIVITY_RISE_FALL); //прерывание по фронтам
}


В функции прерывания пишем обработчик, который в зависимости от фронта будет включать или выключать SPI:

Код:
INTERRUPT_HANDLER(EXTI_PORTA_IRQHandler, 3)
{
  disableInterrupts();
  if (!System_SPI_NSS) //смотрим наш статус (при пуске контроллера тут лежит FALSE)
//этот флаг делаем сами, но можно и по другому реализовать, наверно
  {
    nop(); // можно убрать
    System_SPI_NSS = TRUE;
    SPI_NSSInternalSoftwareCmd(DISABLE); // ключевая функция благодаря которой все работает
    SPI_Cmd(ENABLE); //включаем SPI
  }
  else
  {
    nop(); // можно убрать
    System_SPI_NSS = FALSE;
    SPI_NSSInternalSoftwareCmd(ENABLE);
    SPI_Cmd(DISABLE);
  }
  enableInterrupts();
}


Код инициализации SPI:

Код:
static void SPI_Config(void)
{
  SPI_DeInit();
  SPI_Init(SPI_FIRSTBIT_MSB,
           SPI_BAUDRATEPRESCALER_2,
           SPI_MODE_SLAVE,
           SPI_CLOCKPOLARITY_LOW,
           SPI_CLOCKPHASE_1EDGE,
           SPI_DATADIRECTION_2LINES_FULLDUPLEX,
           SPI_NSS_SOFT,
           0x07);
  SPI_ITConfig(SPI_IT_RXNE, ENABLE);
  SPI_ITConfig(SPI_IT_TXE, ENABLE);
  SPI_NSSInternalSoftwareCmd(ENABLE);
  //SPI_Cmd(ENABLE);
}


А в обработчике прерывания SPI просматриваем статусы буферов отправки и приема, и уже относительно этого производим чтение или запись:

Код:
INTERRUPT_HANDLER(SPI_IRQHandler, 10)
{
  disableInterrupts();
  if (SPI_GetFlagStatus(SPI_FLAG_RXNE))
  {
    system_buff_mb_msg_rx[0] = SPI_ReceiveData();
  }
 
  if (SPI_GetFlagStatus(SPI_FLAG_TXE))
  {
    SPI_SendData(system_buff_mb_msg_rx[0]);
  }
  enableInterrupts();
}


Весь код целиком:

Код:
/* Includes ------------------------------------------------------------------*/
#include "stm8s.h"

/* Private defines -----------------------------------------------------------*/
#define TIM4_PERIOD 124
#define USER_TIME_CYCLE 10

unsigned int System_plc_time = USER_TIME_CYCLE;
bool System_SPI_NSS = FALSE;
uint8_t system_buff_mb_msg_rx[10];

/* Private function prototypes -----------------------------------------------*/
static void CLK_Config(void);
static void TIM4_Config(void);
static void SPI_Config(void);
static void EXTI_Config(void);

/* Private functions ---------------------------------------------------------*/
static void CLK_Config(void)
{
  CLK_DeInit();
  CLK_HSICmd(ENABLE);
  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
  CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
  //CLK_CCOConfig(CLK_OUTPUT_MASTER);
}

static void TIM4_Config(void)
{
  TIM4_DeInit();
  TIM4_TimeBaseInit(TIM4_PRESCALER_128, TIM4_PERIOD);
  TIM4_ClearFlag(TIM4_FLAG_UPDATE);
  TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
  TIM4_Cmd(ENABLE);
  TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
}

static void SPI_Config(void)
{
  SPI_DeInit();
  SPI_Init(SPI_FIRSTBIT_MSB,
           SPI_BAUDRATEPRESCALER_2,
           SPI_MODE_SLAVE,
           SPI_CLOCKPOLARITY_LOW,
           SPI_CLOCKPHASE_1EDGE,
           SPI_DATADIRECTION_2LINES_FULLDUPLEX,
           SPI_NSS_SOFT,
           0x07);
  SPI_ITConfig(SPI_IT_RXNE, ENABLE);
  SPI_ITConfig(SPI_IT_TXE, ENABLE);
  SPI_NSSInternalSoftwareCmd(ENABLE);
  //SPI_Cmd(ENABLE);
}

static void EXTI_Config(void)
{
  EXTI_DeInit();
  GPIO_Init(GPIOA, GPIO_PIN_3, GPIO_MODE_IN_PU_IT);
  EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOA, EXTI_SENSITIVITY_RISE_FALL);
}

INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{
   disableInterrupts();
   System_plc_time--;
   if (System_plc_time > USER_TIME_CYCLE)
   {
     System_plc_time = USER_TIME_CYCLE;
   }
   TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
   enableInterrupts();
}

INTERRUPT_HANDLER(SPI_IRQHandler, 10)
{
  disableInterrupts();
  if (SPI_GetFlagStatus(SPI_FLAG_RXNE))
  {
    system_buff_mb_msg_rx[0] = SPI_ReceiveData();
  }
 
  if (SPI_GetFlagStatus(SPI_FLAG_TXE))
  {
    SPI_SendData(system_buff_mb_msg_rx[0]);
  }
  enableInterrupts();
}

INTERRUPT_HANDLER(EXTI_PORTA_IRQHandler, 3)
{
  disableInterrupts();
  if (!System_SPI_NSS)
  {
    nop();
    System_SPI_NSS = TRUE;
    SPI_NSSInternalSoftwareCmd(DISABLE);
    SPI_Cmd(ENABLE);
  }
  else
  {
    nop();
    System_SPI_NSS = FALSE;
    SPI_NSSInternalSoftwareCmd(ENABLE);
    SPI_Cmd(DISABLE);
  }
  enableInterrupts();
}

void main(void)
{
  CLK_Config();
  TIM4_Config();
  SPI_Config();
  EXTI_Config();
  enableInterrupts();
 
  /* Infinite loop */
  while (1)
  {   
    if(!System_plc_time)
    {
      System_plc_time = USER_TIME_CYCLE;
    }
  }
 
}


#ifdef USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param file: pointer to the source file name
  * @param line: assert_param error line source number
  * @retval : None
  */
void assert_failed(u8* file, u32 line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif



В данном случае пример реализует прием и передачу одного байта, "эхо", что принял, то и отправляет.
Тестировал с одним ведомым камнем пока. Ведущий RPI zero W.
Частота тактирования: 8МГц
Длина линии: 20см (имеется 3 соединения)
при прокладке линии NSS рядом с линиями данных и тактирования, возникает помеха, приходят недостоверные данные. Тактовая линия на линии данных не влияет. Частота опроса ведомого была 1мс. В дальнейшем планируется без задержек, но ведомых будет много, минимум 4 штуки.
Для устранения "помех" просто провод NSS убирал в сторону.
Так что при разводке плат в данном случае я бы линию выбора ведомого устройства прокладывал дальше от линий данных и тактирования. В интернете есть рекомендации по разводке линий данных. Советую изучить.

Ссылка на эстэшные (ST) рекомендации по разводке:
https://www.st.com/resource/en/application_note/dm00403849-layout-recommendations-for-the-design-of-boards-with-st25r3911bst25r391x-devices-stmicroelectronics.pdf


Вложения:
Комментарий к файлу: В прошлый раз не тот файл прикрепил, прошу прощения
main.c [3.41 Кб]
Скачиваний: 107
Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ 1 сообщение ] 


Часовой пояс: UTC + 5 часов


Кто сейчас на конференции

Сейчас этот форум просматривают: Google [Bot]


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB