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

Slave HAL FreeRTOS

Доброе время суток)

 

Извините за наглость :rolleyes: но вопрос общего характера.

 

Мне нужно написать эмуляцию часов ds1307.

Понятно, что это будет slave, с командами чтения и записи по i2c

Никак в голове никак не складывается концепция реализации на FreeRTOS (имеется ввиду взаимодействие коллбэков и потоков, как распознать команды чтения/записи)

 

На картинке, пример работы с ds1307:

packet#27 - так происходит запись (первый байт куда писать, остальные что писать)

packet#28 - установка указателя для чтения

packet#29 - чтение данных

 

Вообщем протокол стандартный.

post-46094-1517951143_thumb.png

 

Может у кого-то есть опыт или идеи по написанию слэйва под операционкой?

Буду рад любой помощи)

Изменено пользователем simark1979

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Товарищи, помогите плиз подступится к реализации

 

 

Я или устал или это действительно сложно, но никак не могу найти правильный подход к реализации протокола внутри RTOS и HAL.

 

Хоть бы пиночек мне)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не понятно в чем вопрос? В эмуляции i2c, или в эмуляции ds1307?

 

раз не указываете мк, то подразумевается, что аппаратного i2c слейва на борту нет и вам нужен ногодрыг?

 

Ногодрыг можно так реализовать:

из даташита на ds1307 SCL на максимальной частоте (100 кГц). Один так на и2ц 0,01 мс. Про реализацию такого ногодрыга средствами ртос - забудьте. Обычно у ртос системный тик 1 мс.

 

Нужно делать на прерываниях: GPIO + TIM. Делайте глобальные переменные, в которых размешаете текущее состояние и2ц, текущий номер бита, адреса, команды и т.п.

 

Изначально текущее состояние и2ц ESTABLISHED. Ловите прерывание на SCL по заднему фронту, если при этом на SDA было лог "1", то состояние меняете на "Ожидание tHD:STA". Запускаете таймер ждете.... если сработало прерывание на SDA 1-0, то был старт. Если сработал таймер, то возвращаетесь в ESTABLISHED (ожидание старта).

если был старт, то запускаете таймер на ожидание фронта 0-1 на CLK, меняете состояние на "ожидание первого байта адреса". Пришло прерывание 0-1 на CLK - читаете SDA. Таймер перезапускаете на ожидание 1-0 на SCL..... и т.д. Т.о. весь протокол реализуете.... Там, где слейв должен дать АСК, меняете направление порта и дает "0".

По приему Stop можно в RTOS выстовить флаг/евент из прерывания о полученных данных. Задача ожидает флаг/евент.... по флагу разблокируется и обработает данные.

 

Эмуляция ds1307 - тут что не понятно? Заводите структуру как Карта aдресов, что типа такой

typedef union  
{
    struct
      {
          uint8_t sec;
        uint8_t min;
        uint8_t hour;
        uint8_t week;
                uint8_t day;
        uint8_t mount;
                uint8_t year;
                uint8_t control;
                uint8_t array[56];
      }mem;
      uint8_t data[64];
} ds1307Mem;

 

packet#27 - так происходит запись (первый байт куда писать, остальные что писать)

* приходит от мастера к слейву старт условие,

* 7 бит адреса Addr,

* 1 бит команды WRITE,

* слейв передает ACK ( переводит порт SDA в OUT и выдает 0, затем опять переводит порт в in),

* мастер передает в слейв 1-ый байт,

* слейв выдает аск,

* мастер передает в слейв 2 байт,

* слейв выдает аск,

* мастер передает в слейв 3 байт,

* слейв выдает аск,

* мастер передает в слейв 4 байт,

* слейв выдает аск,

....

* мастер выдает стоп.

 

В прерывании при обнаружении STOP условия послать эвент в задачу о приеме n-байт, которые нужно записать по указателю &ds1307Mem.data[Addr]. Естественно проверяем выход Addr за пределю массива ds1307Mem.data[].

Обрабатываем ds1307Mem.mem.control, если вы эмулирете Output control.

Для кошерности запускаем ещё один таймер или же в задаче системного тика эмулируем счет времени и меняем по мере изменения времени данные в ds1307Mem. как-то так.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо Вам, что откликнулись.

 

Извините, что не указал камень (подумал, что для HAL это несущественно)

Камень stm32f407 конечно же там аппаратный i2c

Сейчас как Вы и предлагаете, прихожу к выводу, что HAL тут неуместен и нужен низкий уровень, но возможно ошибаюсь.

 

У меня вопрос был по реализации протокола именно на HAL_I2C

 

Понятно, как ловить генерацию СтартАдрес/Режим w/r со стороны мастера. Для этого есть HAL_I2C_AddrCallback()

 

Но нет понимания в следующем:

Как заставить HAL принять данные от мастера переменной длинны? Все функции HAL_I2C_Slave_Recieve* требую передать количество ожидаемых байт

Либо/и

Как ловить очередной принятый от мастера байт для анализа?

UPD: как поймать Stop.

 

Реализация HAL Slave, очень ограничена, или я чего-то не догоняю?

Изменено пользователем simark1979

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

с халом HALом косяк. См исходники

В блокирующем чтении да, не выйдем из приема, пока не получим нужное кол-во байт, либо по таймауту.

в неблокирующем режиме по прерываниям, тут ворде можно можно получить переменное кол-во байт.

Вызывайте HAL_I2C_Slave_Receive_IT(hi2c, pData, 255);

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

хал в прерывании автоматом будет ловить принятые байты и стоп.

После появления STOP-условия прием завершиться и будет вызвана функция void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c).

Определите у себя в коде void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c). Кол-во принятых байт 255 - hi2c->XferSize. Из неё отправляйте в таск эвент/флаг/сигнал/мессадж...

 

 

ps на сколько я понял, если вызвать HAL_I2C_Slave_Receive_IT(hi2c, pData, 2);, а мастер передаст больше 2-х байт, стрельните себе HAL выстрелит вам в ногу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

с халом HALом косяк. См исходники

В блокирующем чтении да, не выйдем из приема, пока не получим нужное кол-во байт, либо по таймауту.

в неблокирующем режиме по прерываниям, тут ворде можно можно получить переменное кол-во байт.

Вызывайте HAL_I2C_Slave_Receive_IT(hi2c, pData, 255);

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

хал в прерывании автоматом будет ловить принятые байты и стоп.

После появления STOP-условия прием завершиться и будет вызвана функция void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c).

Определите у себя в коде void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c). Кол-во принятых байт 255 - hi2c->XferSize. Из неё отправляйте в таск эвент/флаг/сигнал/мессадж...

 

 

ps на сколько я понял, если вызвать HAL_I2C_Slave_Receive_IT(hi2c, pData, 2);, а мастер передаст больше 2-х байт, стрельните себе HAL выстрелит вам в ногу.

 

Действительно, по получению меньшего количества данных, приём заканчивается, но вызова HAL_I2C_SlaveRxCpltCallback увы нет, проверил

 

Так что видимо придется использовать LL драйвер

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А вы никогда не пробовали в Кубе включить для i2c LL драйвер, вместо HAL?

 

Тут какое-то чудо)

Я так и не врубился, но слэйв заработал сам собой, своего кода я совершенно не добавлял.

 

 

Вот так куб мне сгенерил инициализацию:

void MX_I2C2_Init(void)
{
  LL_I2C_InitTypeDef I2C_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;
  
  /**I2C2 GPIO Configuration  
  PF0   ------> I2C2_SDA
  PF1   ------> I2C2_SCL 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0|LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(GPIOF, &GPIO_InitStruct);

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C2);

    /**I2C Initialization 
    */
  LL_I2C_DisableClockStretching(I2C2);

  I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
  I2C_InitStruct.ClockSpeed = 100000;
  I2C_InitStruct.DutyCycle = LL_I2C_DUTYCYCLE_2;
  I2C_InitStruct.OwnAddress1 = 208;
  I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
  I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
  LL_I2C_Init(I2C2, &I2C_InitStruct);

  LL_I2C_SetOwnAddress2(I2C2, 0);

  LL_I2C_DisableOwnAddress2(I2C2);

  LL_I2C_DisableGeneralCall(I2C2);

}

 

Кроме инициализации я ничего не делаю, но после этого у меня лог анализатор начал ловить ответы по шине :)

 

Cлэйв начал отвечать: забирает и отдает данные (но пока непонятно что это за данные)

И непонятно, каким образом это работает при отключенном в Кубе прерываний для I2C2 или в МК реализован полный аппаратный slave автомат.... :blink:

post-46094-1518098671_thumb.png

Изменено пользователем simark1979

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вобщем написал я эмулятор)

 

Для i2c slave подобного типа получилось написать только на LL, HAL оказался непригоден.

 

В сети на LL примеров практически нет, поэтому примеры ищите в \STM32Cube_FW_L4_V1.11.0\Projects\NUCLEO-L476RG\Examples_LL\

Там много чего есть)

 

Вопрос закрыт, всем спасибо)

Изменено пользователем simark1979

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...