simark1979 0 6 февраля, 2018 Опубликовано 6 февраля, 2018 (изменено) · Жалоба Доброе время суток) Извините за наглость :rolleyes: но вопрос общего характера. Мне нужно написать эмуляцию часов ds1307. Понятно, что это будет slave, с командами чтения и записи по i2c Никак в голове никак не складывается концепция реализации на FreeRTOS (имеется ввиду взаимодействие коллбэков и потоков, как распознать команды чтения/записи) На картинке, пример работы с ds1307: packet#27 - так происходит запись (первый байт куда писать, остальные что писать) packet#28 - установка указателя для чтения packet#29 - чтение данных Вообщем протокол стандартный. Может у кого-то есть опыт или идеи по написанию слэйва под операционкой? Буду рад любой помощи) Изменено 7 февраля, 2018 пользователем simark1979 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
simark1979 0 7 февраля, 2018 Опубликовано 7 февраля, 2018 · Жалоба Товарищи, помогите плиз подступится к реализации Я или устал или это действительно сложно, но никак не могу найти правильный подход к реализации протокола внутри RTOS и HAL. Хоть бы пиночек мне) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 10 8 февраля, 2018 Опубликовано 8 февраля, 2018 · Жалоба Не понятно в чем вопрос? В эмуляции 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. как-то так. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
simark1979 0 8 февраля, 2018 Опубликовано 8 февраля, 2018 (изменено) · Жалоба Спасибо Вам, что откликнулись. Извините, что не указал камень (подумал, что для HAL это несущественно) Камень stm32f407 конечно же там аппаратный i2c Сейчас как Вы и предлагаете, прихожу к выводу, что HAL тут неуместен и нужен низкий уровень, но возможно ошибаюсь. У меня вопрос был по реализации протокола именно на HAL_I2C Понятно, как ловить генерацию СтартАдрес/Режим w/r со стороны мастера. Для этого есть HAL_I2C_AddrCallback() Но нет понимания в следующем: Как заставить HAL принять данные от мастера переменной длинны? Все функции HAL_I2C_Slave_Recieve* требую передать количество ожидаемых байт Либо/и Как ловить очередной принятый от мастера байт для анализа? UPD: как поймать Stop. Реализация HAL Slave, очень ограничена, или я чего-то не догоняю? Изменено 8 февраля, 2018 пользователем simark1979 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
juvf 10 8 февраля, 2018 Опубликовано 8 февраля, 2018 · Жалоба с халом 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 выстрелит вам в ногу. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
simark1979 0 8 февраля, 2018 Опубликовано 8 февраля, 2018 · Жалоба с халом 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 драйвер Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
simark1979 0 8 февраля, 2018 Опубликовано 8 февраля, 2018 (изменено) · Жалоба А вы никогда не пробовали в Кубе включить для 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: Изменено 9 февраля, 2018 пользователем simark1979 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
simark1979 0 9 февраля, 2018 Опубликовано 9 февраля, 2018 (изменено) · Жалоба Вобщем написал я эмулятор) Для i2c slave подобного типа получилось написать только на LL, HAL оказался непригоден. В сети на LL примеров практически нет, поэтому примеры ищите в \STM32Cube_FW_L4_V1.11.0\Projects\NUCLEO-L476RG\Examples_LL\ Там много чего есть) Вопрос закрыт, всем спасибо) Изменено 9 февраля, 2018 пользователем simark1979 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться