По существу - кто какими средами разработки пользуется?
Я тут пересел на рабочем ноуте на Linux, пришлось перелезать с Keil. Сначала писал в саблиме, собирал make, потом чуть попарился и прикрутил Qt Creator и Qbs для сборки. Код arm-none-eabi-gcc собирается чуть побольше, чем Keilом, либо линкую коряво, но в целом прикольно. По сравнению с креатором в плане интерфейса, кеил - блокнот прошлого века, но проект создавать не очень удобно, ручками всё-таки приходится прописывать мелочи в qbs-ном файле. Если кто хочет попробовать, могу свой qbs шаблон скинуть.
Andy, тут же много любителей, я например. Нарисуйте мне плиз код i2c на STM32F303ccT6 чтоб eeprom 23lc64 завёлся, раз вы такой крутой. Только плиз без HAL и SPL.
Andy, тут же много любителей, я например. Нарисуйте мне плиз код i2c на STM32F303ccT6 чтоб eeprom 23lc64 завёлся, раз вы такой крутой. Только плиз без HAL и SPL.
Читал, спасибо. Мысли одинаковые во всех статьях и код в разных вариантах уже вылизан по даташиту, но не работает. Хз, надо смотреть что на пинах, экспериментировать надоело.
С процессором понятно, а какую конкретно плату мучаете? У меня были nucleo на 303k8 у них для совместимости с наной i2c был закорочен на выходы A5 A4 и когда а перевёл их в режим DAC i2c перестал работать. Отпаял на плате перемычки - всё заработало. И DAC и i2c/
Да не, плата от robotdyn, всегда от них беру, и рядом контакты всегда проверяю на кз
#Aliexpress 345,44 руб. | STM32F303CCT6 256KB STM32, загрузчик совместим с Arduino IDE или STM прошивкой, ARM Cortex-M4 мини системная плата https://a.aliexpress.ru/_eKhvwu
мой код рабочий , но как и ранее говорил нужен ла.
Тайминги отличаются , подтяжка нужна .внешняя.
дождусь логического анализатора, что то никак не выходит. Резисторы подтяжки и сменить на другой чип eeprom (вдруг дохлый) смогу только после четверга.
#define EEPROM_OWN_ADDRESS (0x50)
uint8_t eeprom_read(uint16_t address) {
uint8_t temp=0;
I2C1->CR2 = (2<<16) | (EEPROM_OWN_ADDRESS<<1);
while (!(I2C1->ISR & I2C_ISR_TXE) );
I2C1->TXDR = (uint8_t) (address>>8); /* Byte to send */
I2C1->CR2 |= I2C_CR2_START; /* Go */
while (!(I2C1->ISR & I2C_ISR_TXIS) );
I2C1->TXDR = (uint8_t) (address &0x00FF); /* Byte to send */
while (!(I2C1->ISR & I2C_ISR_TC) ){};
I2C1->CR2 = I2C_CR2_AUTOEND | (1<<16) | (EEPROM_OWN_ADDRESS<<1) | I2C_CR2_RD_WRN | I2C_CR2_NACK;
I2C1->CR2 |= I2C_CR2_START; /* Go */
while (!(I2C1->ISR & I2C_ISR_RXNE) ){};
temp = I2C1->RXDR ;
return temp;
}
void eeprom_write(uint16_t address, uint8_t data) {
I2C1->CR2 = I2C_CR2_AUTOEND | (3<<16) | (EEPROM_OWN_ADDRESS<<1);
while (!(I2C1->ISR & I2C_ISR_TXE) ); /* Check Tx empty */
I2C1->TXDR =(uint8_t) (address>>8); /* Byte to send */
I2C1->CR2 |= I2C_CR2_START; /* Go */
while (!(I2C1->ISR & I2C_ISR_TXIS) );
I2C1->TXDR = (uint8_t) (address &0x00FF); /* Byte to send */
while (!(I2C1->ISR & I2C_ISR_TXIS) );
I2C1->TXDR = data ; /* Byte to send */
}
void testeeprom(void) {
unsigned int addrone = 1024;
eeprom_write(addrone,204);
unsigned char btr = eeprom_read(addrone);
if (btr) {
writeLOGstr((unsigned char*)"Read EEPROM byte from "); writeLOGint(addrone); writeLOGstr((unsigned char*)" addr = "); writeLOGint(btr); writeLOGstr((unsigned char*)"\r\n");
}
}
void initEEPROMi2c(void) {
// I2C1 SCL PB8
GPIOB->MODER |= GPIO_MODER_MODER8_1; // 10 — режим альтернативной функции.
GPIOB->MODER &= ~GPIO_MODER_MODER8_0; // 10 — режим альтернативной функции.
GPIOB->OTYPER |= GPIO_OTYPER_OT_8; // 1 - open-drain
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR8_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR8_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_1; // 11 — 50 MHz
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_0; // 11 — 50 MHz
GPIOB->AFR[1] |= (0x04); // Назначаем PB8 выводу альтернативную функцию AF4
// I2C1 SDA PB9
GPIOB->MODER |= GPIO_MODER_MODER9_1; // 10 — режим альтернативной функции.
GPIOB->MODER &= ~GPIO_MODER_MODER9_0; // 10 — режим альтернативной функции.
GPIOB->OTYPER |= GPIO_OTYPER_OT_9; // 1 - open-drain
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR9_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR9_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_1; // 11 — 50 MHz
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_0; // 11 — 50 MHz
GPIOB->AFR[1] |= (0x04 << 4); // Назначаем PB9 выводу альтернативную функцию AF4
// I2C1 init
I2C1->CR1 = 0;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
RCC->CFGR3 |= RCC_CFGR3_I2C1SW;
I2C1->TIMINGR = (uint32_t)0x2000090E;
I2C1->OAR1 = 0;
I2C1->OAR2 = 0;
I2C1->CR1 = I2C_CR1_PE;
#ifdef DEBUG_MODE
testeeprom();
#endif
}
Убирая программную подтяжку pull-up - МК намертво зависает, что логично, т к не отрабатывают флаги TXE TXIS. Пробовал прописывал timeout на while циклах, зависания нет, но и данные не пишутся.
Update: установка резисторов на SCL SDA ни к чему не привела :(
Если убрали программную подтяжку PULLUP и МК намертво зависло - такое возможно только в случае, если внешние подтягивающие резисторы не работают - подпаяны не правильно или вообще контакта нет. Без них, только внутренней подтяжке, i2c не работает правильно.
Последние пришедшие блюпилы оказались с резистором 1.5к на USB. Полез перепаивать и не пришлось. Маленькая, но радость.
nik182, какие нафиг контакты, естественно я все проверил, выше почитайте - конечно он виснет без поддержки и это тоже описано. Какой блин блюпил, чип то другой и плата другая (. Не стоит смешивать опят разных систем.
Жду лог анализатор, дальнейшие эксперименты думаю бесполезны.
Блюпилл это моя радость по поводу USB резистора, к Вам отношения не имеет. У меня никогда не было проблем с i2c на stm. Я пользовал F1,F3,F4 на SPL, HAL и LL. Про подтяжку прочитал после отправки предыдущего поста. Меня удивляет Ваш способ поиска проблемы. У меня в похожих условиях уходит пару часов на локализацию и решения проблемы. Обычный путь - деление проблемы на составляющие. Сначала подключаю к точно работающую железку. Делаю тестовый проект в котором есть только i2c на HAL. С ним у i2c у меня проблем никогда не было. Если всё заработало, меняю железку на проблемную. Если не заработало, меняю процессор и проверяю шины. В отладчике, до старта программы на шинах i2c должно быть напряжение питания и просаживаться не до нуля при замыкании шины резистором 1к. Обычно этого хватает что бы устранить причину траблов. В одной из библиотек встретился модуль i2c скана на HAL, выдает результат в uart, поэтому его тоже приходится поднимать. Чаще под отладчиком комментирую вывод и результат смотрю в переменной i2cnum. Я помню про Вашу не любовь к обёрткам, но их можно использовать просто как инструмент отладки.
Ужас, это когда включил и не работает как надо. А если включил повесив LCD BME820 и флэш и всё работает с первого раза и даже без подстроек какойже это ужас? А еррату я всегда читаю, и даже в этой теме ссылки приводил. У Вас были проблемы с i2c которые ерратой решать пришлось? Версия драйверов куба для 103 1.8.0 c 18 года сильно подросла. Может в этом дело?
nik182, хватит уже хвалиться и воздух сотрясать, толку от того что вы правильно проверяете железо с помощью библиотек (hal, spl и т д) - ноль!
PS не в тему конечно, но ни разу не было проблем в железе, и почти всегда собирал пайкой устройство и работало, а если у вас железо сразу не работает --может проблема в руках или в производителе/поставщике.
Проблемы были только с битым железом, которое после замены работало исправно. Как найти причину не работы я уже писал. И почему толку ноль? Вы пробовали? Тоже не работает? Не верю. И про хвалюсь это Вы загнули. Я предлагаю пути поиска проблемы исключительно на собственном положительном опыте. Хотите пройти по собственным граблям - пожалуйста.
andycat - наблюдаю дискуссию со стороны, мне кажется у вас ступор случился. Вы только и твердите. что все сделали правильно и отвергаете любые советы. Но практика - она такова. что у вас не работает.
Вот вам дали рабочий код в SPL. Несмотря на всю вашу ненависть к библиотекам - может все таки его попробовать? Исключительно ради проверки? - ведь если оно заработает, вы будете точно знать что косяк в вашем коде. А если нет - вероятно проблемы с железом или сборкой
nik182, помощью ЛА или хотя бы осцилографа можно решить любую проблему с шинами i2c и spi, даже те что вносят ваши г.либы а вы их не замечаете...
Я практик, а не исследователь протоколов. Подключил. Работает. И хрен с ним что там есть несоответствие стандарту. Пока на проблемы натыкался только из за битого железа.
Это не практик это потребитель, в самом плохом смысле...
Про плохой смысл хотелось бы по подробнее. Я не отношусь к проектировщикам МК. Я физик экспериментатор и большинство датчиков для эксперимента мы изобретаем и делаем своими руками. МК в них используется чисто утилитарно, как устройства сбора и передачи информации и управления режимами экспериментальной установки. Поставили - работает - отлично. Где здесь время на исследование протоколов?
Ну вот и сделал. В целом доволен. Правда тот делитель случайно сжег, в итоге собрал на SAB6456, но суть та же, те же глюки: частота на выходе делителя зависит от уровня сигнала на входе, при низком ерунду показывает. Усилители разные по входу ставил, усиливает же все подряд, лучше не становится. От батарейного питания отказался, делитель жрет как мой кот)) Суммарное потребление под 100мА.
Заработал код с I2C с EEPROM 24LC64, достаточно было добавить паузу согласно даташита Write Cycle 5 ms, позже этот костыль буду убирать, смотреть что там выдает или не выдает eeprom
#define EEPROM_OWN_ADDRESS (0x50)
uint8_t eeprom_read(uint16_t address) {
uint8_t temp=0;
I2C1->CR2 = (2<<16) | (EEPROM_OWN_ADDRESS<<1);
while (!(I2C1->ISR & I2C_ISR_TXE) );
I2C1->TXDR = (uint8_t) (address>>8); /* Byte to send */
I2C1->CR2 |= I2C_CR2_START; /* Go */
while (!(I2C1->ISR & I2C_ISR_TXIS) );
I2C1->TXDR = (uint8_t) (address &0x00FF); /* Byte to send */
while (!(I2C1->ISR & I2C_ISR_TC) ){};
I2C1->CR2 = I2C_CR2_AUTOEND | (1<<16) | (EEPROM_OWN_ADDRESS<<1) | I2C_CR2_RD_WRN | I2C_CR2_NACK;
I2C1->CR2 |= I2C_CR2_START; /* Go */
while (!(I2C1->ISR & I2C_ISR_RXNE) ){};
temp = I2C1->RXDR ;
return temp;
}
void eeprom_write(uint16_t address, uint8_t data) {
I2C1->CR2 = I2C_CR2_AUTOEND | (3<<16) | (EEPROM_OWN_ADDRESS<<1);
while (!(I2C1->ISR & I2C_ISR_TXE) ); /* Check Tx empty */
I2C1->TXDR =(uint8_t) (address>>8); /* Byte to send */
I2C1->CR2 |= I2C_CR2_START; /* Go */
while (!(I2C1->ISR & I2C_ISR_TXIS) );
I2C1->TXDR = (uint8_t) (address &0x00FF); /* Byte to send */
while (!(I2C1->ISR & I2C_ISR_TXIS) );
I2C1->TXDR = data ; /* Byte to send */
delay(5UL);
}
void testeeprom(void) {
unsigned char btr = eeprom_read(1024);
if (btr) {
writeLOGstr((unsigned char*)"Read EEPROM byte from "); writeLOGint(1024); writeLOGstr((unsigned char*)" addr = "); writeLOGint(btr); writeLOGstr((unsigned char*)"\r\n");
}
unsigned int addrone = 2048;
eeprom_write(addrone,14);
btr = eeprom_read(512);
if (btr) {
writeLOGstr((unsigned char*)"Read EEPROM byte from "); writeLOGint(512); writeLOGstr((unsigned char*)" addr = "); writeLOGint(btr); writeLOGstr((unsigned char*)"\r\n");
}
btr = eeprom_read(addrone);
if (btr) {
writeLOGstr((unsigned char*)"Read EEPROM byte from "); writeLOGint(addrone); writeLOGstr((unsigned char*)" addr = "); writeLOGint(btr); writeLOGstr((unsigned char*)"\r\n");
}
}
void initEEPROMi2c(void) {
// I2C1 SCL PB8
GPIOB->MODER |= GPIO_MODER_MODER8_1; // 10 — режим альтернативной функции.
GPIOB->MODER &= ~GPIO_MODER_MODER8_0; // 10 — режим альтернативной функции.
GPIOB->OTYPER |= GPIO_OTYPER_OT_8; // 1 - open-drain
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR8_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR8_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_1; // 11 — 50 MHz
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_0; // 11 — 50 MHz
GPIOB->AFR[1] |= (0x04); // Назначаем PB8 выводу альтернативную функцию AF4
// I2C1 SDA PB9
GPIOB->MODER |= GPIO_MODER_MODER9_1; // 10 — режим альтернативной функции.
GPIOB->MODER &= ~GPIO_MODER_MODER9_0; // 10 — режим альтернативной функции.
GPIOB->OTYPER |= GPIO_OTYPER_OT_9; // 1 - open-drain
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR9_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR9_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_1; // 11 — 50 MHz
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_0; // 11 — 50 MHz
GPIOB->AFR[1] |= (0x04 << 4); // Назначаем PB9 выводу альтернативную функцию AF4
// I2C1 init
I2C1->CR1 = 0;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
RCC->CFGR3 |= RCC_CFGR3_I2C1SW;
I2C1->TIMINGR = 0x2000090E;
I2C1->OAR1 = 0;
I2C1->OAR2 = 0;
I2C1->CR1 = I2C_CR1_PE;
#ifdef DEBUG_MODE
testeeprom();
#endif
}
всем огромное спасибо.
P.S. Работает и с программной подтяжкой выводов к плюсу.
Внешние резюки оставь, ты же не можешь гарантировать, что емкость монтажа не будет превышать 50 пФ или внутренние подтяжки никогда не будут больше 40К. Во все циклы добавь таймаут. Работа МК не должна зависеть от внешних факторов.
По поводу задержек, остальной код должен быть неблокирующим. Иначе 1 задержка погоды не сделает.
Друзья, что то застрял на таймерах с внешним тактированием, просьба подсказать.
МК STM32F303CCT6, к пину PA8 подключена пока кнопка с подтяжкой, с которой считаются импульсы.
- пин входной настроен как альтернативная функция AF10 для счета TIM4_ETR
void initPinInExec3device(void) { // init PA8 pin as IN // инициализация пина - вход от домофона
GPIOA->MODER |= GPIO_MODER_MODER8_1; // 10 — режим альтернативной функции.
GPIOA->MODER &= ~GPIO_MODER_MODER8_0; // 10 — режим альтернативной функции.
//GPIOA->MODER &= ~GPIO_MODER_MODER8; // 00 in // режим входа
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR8_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR8_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_1; // 11 - 50MHz // частота / быстродействие
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_0; // 11 - 50MHz // частота / быстродействие
GPIOA->AFR[1] |= (0x0A); // Назначаем PA8 выводу альтернативную функцию AF10 - внешнее тактирование таймера TIM4_ETR
}
- сами таймеры, TIM3 считает каждую секунду и тупо выводит TIM4->CNT
//dmfmod.c // модуль работы с домофоном
#include "dmfcomm.h"
#include "stm32f30x.h"
#include "dmfuart.h"
#define TIMx_CLK SystemCoreClock // Hz // частота работы МК
#define TIMx_Internal_Frequency 20000UL // внутрення частота
#ifdef DEBUG_MODE // в режиме отладки 1 секунду ждем, чтоб кнопкой успечать нажимать кучу раз
#define TIMx_Out_Frequency 1UL // в режиме отладки 1 секунду ждем, чтоб кнопкой успечать нажимать кучу раз
#else
#define TIMx_Out_Frequency 4UL // Hz // частота таймера - 250 миллисекунд - за этот период считываются все импульсы домофона
#endif
#define Prescaler (TIMx_CLK/TIMx_Internal_Frequency)-1UL // предделитель
#define Period (TIMx_Internal_Frequency/TIMx_Out_Frequency)-1UL // период
void initTIM3(void) { // инициализация таймера
TIM3->CR1 &= ~TIM_CR1_CEN; // отключить таймер
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // включить тактирование TIM3
TIM3->PSC = Prescaler; // предделитель
TIM3->ARR = Period; // период
TIM3->DIER |= TIM_DIER_UIE; // разрешить прерывание по перезагрузке тиймера
TIM3->CR1 &= ~TIM_CR1_DIR; // считаем вверх
//TIM3->CR1 |= TIM_CR1_OPM; // досчитав до конца таймер выключается
NVIC_SetPriority(TIM3_IRQn,0); // ставим приоритет
NVIC_EnableIRQ(TIM3_IRQn); // прописываем прерывание
TIM3->CR1 |= TIM_CR1_CEN; // включаем таймер
}
#ifdef __cplusplus
extern "C" {
#endif
void TIM3_IRQHandler(void) { // прерывание срабатывает через 250 мсек после запуска
TIM3->SR = ~TIM_SR_UIF; // сбросим флаг прерывания
TIM4->CR1 &= ~TIM_CR1_CEN; // стоп таймер
writeLOGint(TIM4->CNT); writeLOGstr((const unsigned char *)"\r\n");
TIM4->CR1 |= TIM_CR1_CEN; // запускаем таймер
}
#ifdef __cplusplus
}
#endif
void initTIM4etr(void) { // инициализация таймера подсчета импульсов
TIM4->CR1 &= ~TIM_CR1_CEN; // стоп таймер
RCC->AHBENR |= RCC_APB1ENR_TIM4EN; // включаем тактирование прерывания
TIM4->PSC = 0; // предделитель
TIM4->CNT = 0; // сбрасываем счетчик
TIM4->ARR = 0; // период
TIM4->CR1 &= ~TIM_CR1_DIR; // считаем вверх
TIM4->SMCR &= ~TIM_SMCR_ETF; // настраиваем фильтр
TIM4->SMCR &= ~TIM_SMCR_ETPS; // предделитель 0
TIM4->SMCR |= TIM_SMCR_ETP; // убывающий фронт
TIM4->SMCR |= TIM_SMCR_ECE; // внешнее тактирование
TIM4->CR1 |= TIM_CR1_CEN; // запускаем таймер
}
void initDMFint(void){ // запускаем прерывание на подачу отрицательного импульса
initTIM3(); // инициализируем таймер отсчета времени
initTIM4etr(); // инициализируем таймер подсчета импульсов на пине PA8
}
unsigned short processDMFappart(void) { // выдаем номер квартиры
return 0; // выдаем номер квартиры
}
- но выводит нули :(
Само железо однозначно рабочее, т к этот же функционал, написанный по внешним прерываниям EXTI отлично работает, т е первое нажатие запускает таймер отсчета времени, все остальные импульсы с пина просто увеличивают счетчик. Но хочеться написать без IRQ т е чтоб само все считалось.
Update: Самое непонятное - если я счетчику пропишу некоторое начальное значение TIM4->CNT = 100; то потом при чтении его получаю ноль, т е оно где то обнуляется :(
Update 2: Все заработало, ошибся в написании тактирования таймера :(
Есть домофон, он шлет импульсы на PA8, их необходимо подсчитать и вывести номер вызывной квартиры. Таймер TIM4 master считает импульсы, если импульсов больше трех, по сравнению TIM4_CNT=TIM4_CCR4 OC4REF -> TRGO запускает slave таймер TIM3 отсчет времени, по окончании отсчета времени срабатывает прерывание, счетчик TIM4_CNT заноситься в переменную, обнуляются счетчики, заново запускается прерывание master TIM4 и все идет по кругу.
Проблема в том, что slave TIM3 таймер запускается только один раз :(
volatile unsigned short outAppNum; // номер квартиры
void initTIM4etr(void) { // инициализация таймера подсчета импульсов
TIM4->CR1 &= ~TIM_CR1_CEN; // стоп таймер импульсов
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // включить тактирование TIM3
TIM4->PSC = 35; // предделитель -> 5кГц выходная частота, если будут сбои на номерах квартир более сотни - увеличивать
TIM4->CNT = 0; // сбрасываем счетчик
TIM4->ARR = 399; // период -> 5кГц выходная частота, но увеличивать при условии что ARR более 300
TIM4->CR1 &= ~TIM_CR1_DIR; // считаем вверх
TIM4->SMCR &= ~TIM_SMCR_ETF; // настраиваем фильтр
TIM4->SMCR &= ~TIM_SMCR_ETPS; // предделитель 0
TIM4->SMCR |= TIM_SMCR_ETP; // убывающий фронт
TIM4->SMCR |= TIM_SMCR_ECE; // внешнее тактирование
TIM4->CR2 |= TIM_CR2_MMS_2; // OC4REF -> TRGO
TIM4->CR2 |= TIM_CR2_MMS_1; // OC4REF -> TRGO
TIM4->CR2 |= TIM_CR2_MMS_0; // OC4REF -> TRGO
TIM4->CCR4 = 3; // число импульсов, после которых запускается таймер TIM3 отсчета времени
TIM4->CCMR2 &= ~TIM_CCMR2_OC4M_2; // 011 режим сравнения, срабатывание при TIM4_CNT=TIM4_CCR4
TIM4->CCMR2 |= TIM_CCMR2_OC4M_1; // 011 режим сравнения, срабатывание при CNT=CCR4
TIM4->CCMR2 |= TIM_CCMR2_OC4M_0; // 011 режим сравнения, срабатывание при CNT=CCR4
TIM4->CCMR2 &= ~TIM_CCMR2_OC4PE; // отключение предзагрузки регистров
TIM4->CCER |= TIM_CCER_CC4E; // включение сравнения OC4REF
TIM4->CR1 |= TIM_CR1_CEN; // запускаем таймер
}
#define TIMx_CLK SystemCoreClock // Hz // частота работы МК
#define TIMx_Internal_Frequency 20000UL // внутрення частота
#define TIMx_Out_Frequency 4UL // Hz // частота таймера - 250 миллисекунд - за этот период считываются все импульсы домофона
#define Prescaler (TIMx_CLK/TIMx_Internal_Frequency)-1UL // предделитель
#define Period (TIMx_Internal_Frequency/TIMx_Out_Frequency)-1UL // период
void initTIM3(void) { // инициализация таймера
TIM3->CR1 &= ~TIM_CR1_CEN; // отключить таймер времени
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // включить тактирование TIM3
#ifdef DEBUG_MODE // в режиме отладки дольше ждем, чтоб кнопкой успечать нажимать кучу раз
TIM3->PSC = 40799; //Prescaler; // предделитель
TIM3->ARR = Period; // период
#else
TIM3->PSC = Prescaler; // предделитель
TIM3->ARR = Period; // период
#endif
TIM3->DIER |= TIM_DIER_UIE; // разрешить прерывание по перезагрузке тиймера
TIM3->CR1 &= ~TIM_CR1_DIR; // считаем вверх
TIM3->CR1 |= TIM_CR1_OPM; // досчитав до конца таймер выключается
TIM3->CNT = 0; // обнуляем таймер времени
TIM3->SMCR &= ~TIM_SMCR_TS_2; // 011 ITR3
TIM3->SMCR |= TIM_SMCR_TS_1; // 011 ITR3
TIM3->SMCR |= TIM_SMCR_TS_0; // 011 ITR3
TIM3->SMCR |= TIM_SMCR_SMS_2; // 110 trigger mode
TIM3->SMCR |= TIM_SMCR_SMS_1; // 110 trigger mode
TIM3->SMCR &= ~TIM_SMCR_SMS_0; // 110 trigger mode
TIM3->EGR |= TIM_EGR_UG; // update event // enable UEV
NVIC_SetPriority(TIM3_IRQn,0); // ставим приоритет
NVIC_EnableIRQ(TIM3_IRQn); // прописываем прерывание
}
#ifdef __cplusplus
extern "C" {
#endif
void TIM3_IRQHandler(void) { // прерывание срабатывает через XXX мсек после запуска
static _Bool firstStart = 1; // костыль - обход запуска прерывания при старте МК
TIM4->CR1 &= ~TIM_CR1_CEN; // стоп таймер подсчета импульсов
if (!firstStart) outAppNum = ((TIM4->CNT * 2 - 2) / 4); // считаем номер квартиры
TIM3->CNT = 0; // обнуляем таймер времени
TIM4->CNT = 0; // обнуляем счетчик импульсов
writeLOGstr((const unsigned char *)"-------------------TIM3-------------------\r\n");
firstStart = 0; // первое прерывание прошло
TIM3->SR &= ~TIM_SR_UIF; // сбросим флаг прерывания таймера времени UIF
TIM4->CR1 |= TIM_CR1_CEN; // запускаем таймер подсчета импульсов
}
#ifdef __cplusplus
}
#endif
void initDMFint(void){ // запускаем прерывание на подачу отрицательного импульса
initTIM3(); // инициализируем таймер отсчета времени
initTIM4etr(); // инициализируем таймер подсчета импульсов на пине PA8
}
unsigned short processDMFappart(void) { // выдаем номер квартиры
unsigned short tmApp = 0; // временная переменная
if (outAppNum) { // если есть номер квартиры
tmApp = outAppNum; outAppNum = 0; // присваиваем переменной номер квартиры // обнуляем номер квартиры
}
return tmApp; // выдаем номер квартиры
}
-
апноуты прочитал, разные режимы попробовал, есть подозрение что TIM4 master в принципе не может в таком режиме работать, т к нашел режим в TIM1 который наверное больше бы мне подошел, но на пине PA8 нет TIM1_ETR :(
011: Compare Pulse - The trigger output send a positive pulse when the CC1IF flag is to be
set (even if it was already high), as soon as a capture or a compare match occurred.
(TRGO).
балуюсь с Ethernet модулем, получился простенький сервер
STM32F303CCT6 + W5500 обмен SPI через DMA
- пробник
//main.c - основное тело программы
#include "stm32f30x.h"
#include "f303u2log.h" // работа с UART
#include "f303spi2.h" // работа с SPI
#include "w55v2.h" // работа с W5500
unsigned long currentMillis;
static unsigned char bufHttpRead[16]; // буфер через который мы будем читать данные
static unsigned char modeHttpServer; // режим работы сервера
// 0 - ничего не делаем
// 1 - пришли данные - запускаем чтение
// 2 - читаем, ожидаем окончание чтения
// 3 - все вычитали, будем отправлять ответ
// 4 - ждем окончания отправки
// 5 - заново запускаем сокет
// 6 - отключиться от клиента
static unsigned char responseHtmlPage[] = "HTTP/1.1 200 OK\r\n\
Content-Type: text/html\r\n\
Connection: close\r\n\
\r\n\
<!DOCTYPE HTML>\r\n\
<html>\r\n\
<body>\r\n\
<h1>STM32F303CCT6 server</h1>\r\n\
</body>\r\n\
</html>\r\n";
void httpSrvCallbackSn_IR(unsigned char localSn_IR) {
writeLOGstr((unsigned char*) "~i5=0x");writeLOGhex(localSn_IR); writeLOGstr((unsigned char*) "\r\n");
if ((localSn_IR & Sn_IR_RECV) && (!modeHttpServer)) { // прилетели данные
modeHttpServer = 1; // будем читать данные
} else if ((localSn_IR & Sn_IR_TIMEOUT ) || (localSn_IR & Sn_IR_DISCON)) { // отключение
modeHttpServer = 5; // заново запускаем сокет
} else if ((localSn_IR & Sn_IR_SENDOK) && (modeHttpServer == 4)) { // все отправилось
modeHttpServer = 6; // разрываем соединение
}
}
void httpSrvCallbackRead(unsigned short currentPart, unsigned short countParts, unsigned short sizeReadData) {
if ((modeHttpServer == 2) && (countParts)) {
// обрабатываем пришедшие данные
if (currentPart < 10) { // первые части выведем в лог
unsigned char bt = bufHttpRead[sizeReadData-1]; bufHttpRead[sizeReadData-1] = 0;
writeLOGstr((unsigned char *)bufHttpRead); writeLOGbyte(bt);
}
if (currentPart == (countParts - 1)) { // если пришла последняя часть
modeHttpServer = 3; // будем отправлять ответ
}
}
}
void httpSrvCallbackError(unsigned char localError) {
writeLOGstr((unsigned char*) "~e5=0x");writeLOGhex(localError); writeLOGstr((unsigned char*) "\r\n");
}
void initBuiltLed(void) { // инициализация встроенного светодиода
RCC->AHBENR|=RCC_AHBENR_GPIOCEN; //Включаем тактирование порта GPIOC — 72Мгц
GPIOC->MODER &= ~GPIO_MODER_MODER13_1; // 01 out // режим выход
GPIOC->MODER |= GPIO_MODER_MODER13_0; // 01 out // режим выход
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_13; // 0 push-pull // двухтактный/обычный выход
GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13; // 00 - 2MHz // частота / быстродействие
}
void initSystem(void) { // init MCU
SystemInit(); // системная инициализация таймеров МК из библиотеки CMSIS. В идеале, когда нибудь потом, необходимо переписать самому
// запускаем DWT таймер чтоб отсчитывать микросекундные задержки
#define DWT_CYCCNT *(volatile uint32_t *)0xE0001004
#define DWT_CONTROL *(volatile uint32_t *)0xE0001000
#define SCB_DEMCR *(volatile uint32_t *)0xE000EDFC
SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // разрешение счётчика
DWT_CONTROL |= DWT_CTRL_CYCCNTENA_Msk; // запуск счётчика
SysTick_Config(SystemCoreClock/1000); // настройка прерывания SysTick_Handler на миллисекундный выход
}
int main(void) { // главная функция проекта
initSystem();
initUARTlog(); // инициализируем лог
writeLOGstr((const unsigned char *)"\r\nStart!\r\n"); // приветствие
initBuiltLed(); // инициализация встроенного светодиода
initSPI(); // инициализация SPI
initUnitW5500(); // инициализируемся Ethernet
while (w5500BUSY()); // ждем когда закончит
openListenSocket(5/*сокет*/, 80/*порт*/, httpSrvCallbackSn_IR/*обработчик прерываний*/, httpSrvCallbackError/*обработчик ошибок*/); // открываем порт http сервера
modeHttpServer = 0;
while (1){ // крутимся по кругу
if (!spiBUSY()) processW5500();
if (serialLOGavailable()) writeLOGbyte(readLOGbyte()); // echo log
if ((currentMillis & 0xFFUL) < 0x0F) GPIOC->ODR ^= GPIO_ODR_13; // моргаем встроенным светодиодом для понимания что не висим
if ((modeHttpServer) && (!w5500BUSY())) { // если надо по серверу что то сделать и W5500 свободен
switch (modeHttpServer) {
case 1: { // прилетели данные
readDataFromSocket(5/*сокет*/, bufHttpRead/*буфер чтения*/, sizeof(bufHttpRead)/*размер буфера*/, httpSrvCallbackRead/*обработчик чтения*/);
modeHttpServer = 2; // ждем прихода всех данных
break;
}
case 3: { // отправка данных в сокет
sendDataToSocket(5/*сокет*/, responseHtmlPage/*буфер отправки*/, sizeof(responseHtmlPage)/*размер буфера отправки, не более 2048 байт*/); // отправляем в обратку страничку
modeHttpServer = 4; // ждем окончания отправки
break;
}
case 5: { // заново запускаем сокет
openListenSocket(5/*сокет*/, 80/*порт*/, httpSrvCallbackSn_IR/*обработчик прерываний*/, httpSrvCallbackError/*обработчик ошибок*/); // открываем порт http сервера
modeHttpServer = 0; // ничего не делаем
break;
}
case 6: { // разрываем соединение
disconnectSocket(5/*сокет*/); // отключаемся
modeHttpServer = 0; // ничего не делаем
break;
}
default: {};
}
}
}
}
void SysTick_Handler(void) { // миллисекундное прерывание
++currentMillis; // увеличиваем наш глобальный таймер
}
- SPI
//f303spi2.c // модуль работы с SPI
//stm32f303cct6 SPI2 pins PB12 - PB15
#ifndef F303SPI2_H
#define F303SPI2_H
//#include "f303u2log.h" // работа с UART
void initSPI(void); // инициализация SPI
// в связи с особенностями обработки окончания отправки SPI через DMA + обработка NSS
// или у меня руки кривые
// данную процедуру обязательно запускать каждый проход цикла loop всей программы
_Bool spiBUSY(void); // занятость интерфейса // обязательно проверять при любом действии
void spiSendData(unsigned char * dataPointer, unsigned short sizeData); // отправить данные в шину
void setCallbackTX(void (*inCallbackTX)(unsigned short)); // установка внешнего обработчика при успешной передаче
void spiReadData(unsigned char * dataPointer, unsigned short sizeData); // получить данные из шины
void setCallbackRX(void (*inCallbackRX)(unsigned short)); // установка внешнего обработчика при успешном приеме
void spiSendAndReadData(unsigned char * dataOutPointer, unsigned short sizeOutData, unsigned char * dataInPointer, unsigned short sizeInData); // отправить данные в шину и сразу же получить не отсключая SS
void spiSendTwoData(unsigned char * dataOutPointer, unsigned short sizeOutData, unsigned char * dataTwoPointer, unsigned short sizeTwoData); // отправить две части подряд не отсключая SS
#endif /* F303SPI2 */
//f303spi2.c // модуль работы с SPI
//stm32f303cct6 SPI2 pins PB12 - PB15
#include "stm32f30x.h"
#include "f303spi2.h" // работа с SPI
volatile unsigned short countTXdata; // сохраняем количество передаваемых данных
volatile unsigned short countRXdata; // сохраняем количество принимаемых данных
volatile unsigned char flStartMode; // режим текущей передачи 0-отправка 1-прием 2-отправка и прием
volatile _Bool inWork; // флаг что работаем устанавливаем в самом начале работы / вызова процедуры
volatile _Bool flNextRead; // флаг для гибридного режима, устанавливается в начале отправки, сбрасывается после отправки и началом приема
unsigned char * localDataInPointer; // указатель куда принимаемые данные скидывать при гибридном режиме
void (*callbackTX)(unsigned short); // внешний обработчик окончания передачи
void setCallbackTX(void (*inCallbackTX)(unsigned short)) { // установка внешнего обработчика при успешной передаче
callbackTX = inCallbackTX;
}
void (*callbackRX)(unsigned short); // внешний обработчик окончания приема
void setCallbackRX(void (*inCallbackRX)(unsigned short)) { // установка внешнего обработчика при успешном приеме
callbackRX = inCallbackRX;
}
void nssStart(void) { // опустить ногу SS
// pin SS PB12 use manual
GPIOB->ODR &= ~GPIO_ODR_12;
GPIOB->MODER &= ~GPIO_MODER_MODER12; // обнуляем
//GPIOB->MODER &= ~GPIO_MODER_MODER12_1; // 01 out // режим выход
GPIOB->MODER |= GPIO_MODER_MODER12_0; // 01 out // режим выход
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_12; // 0 push-pull // двухтактный/обычный выход
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR12; // 11 — 50 MHz
inWork = 1; // флаг начала работы
}
void nssStop(void) { // поднять ногу SS
inWork = 0; // закончили посылку/фрейм
GPIOB->MODER &= ~GPIO_MODER_MODER12; // 00 in // режим входа // для классического режима подсчета импульсов
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR12_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR12_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
}
void initSPI(void) { // init SP2 SCK PB13 MISO PB14 MOSI PB15
callbackTX = 0; // нет обработчика передачи
callbackRX = 0; // нет обработчика приема
RCC->AHBENR|=RCC_AHBENR_GPIOBEN; //Включаем тактирование порта GPIOB — 72Мгц
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; // Включаем тактирование SPI2 — 36Мгц
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //Включаем тактирование DMA1
// pin SCK PB13 AF5 push-pull
GPIOB->MODER |= GPIO_MODER_MODER13_1; // 10 — режим альтернативной функции.
GPIOB->MODER &= ~GPIO_MODER_MODER13_0; // 10 — режим альтернативной функции.
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_13; // 0 - двухтактный выход или push-pull сокращено PP (после сброса)
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR13; // 11 — 50 MHz
GPIOB->AFR[1] |=(0x05 << 20); // Назначаем PB13 выводу альтернативную функцию AF5
// pin MISO PB14 input pull-up
GPIOB->MODER |= GPIO_MODER_MODER14_1; // 10 — режим альтернативной функции.
GPIOB->MODER &= ~GPIO_MODER_MODER14_0; // 10 — режим альтернативной функции.
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR14_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR14_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR14; // 11 — 50 MHz
GPIOB->AFR[1] |=(0x05 << 24); // Назначаем PB14 выводу альтернативную функцию AF5
// pin MOSI PB15 AF5 push-pull
GPIOB->MODER |= GPIO_MODER_MODER15_1; // 10 — режим альтернативной функции.
GPIOB->MODER &= ~GPIO_MODER_MODER15_0; // 10 — режим альтернативной функции.
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_15; // 0 - двухтактный выход или push-pull сокращено PP (после сброса)
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15; // 11 — 50 MHz
GPIOB->AFR[1] |=(0x05 << 28); // Назначаем PB12 выводу альтернативную функцию AF5
nssStop();
SPI2->CR1 = 0;
SPI2->CR2 = 0;
SPI2->CR1 &= ~SPI_CR1_LSBFIRST; // 0 - MSB передается первым
SPI2->CR1 |= SPI_CR1_SSM; // Программное управление SS
SPI2->CR1 |= SPI_CR1_SSI; // I/O value of the NSS pin is ignored
SPI2->CR1 |= SPI_CR1_BR; // BR=111 speed = 36 000 000 / 256 = 140 625 Hz
SPI2->CR1 |= SPI_CR1_MSTR; // master mode
SPI2->CR1 &= ~SPI_CR1_CPOL; // Clock polarity 0/1
SPI2->CR1 &= ~SPI_CR1_CPHA; // Clock phase // 0: The first clock transition is the first data capture edge // The second clock transition is the first data capture edge
SPI2->CR2 &= ~SPI_CR2_DS_3; // Data size 0111: 8-bit
SPI2->CR2 |= SPI_CR2_DS_2; // Data size 0111: 8-bit
SPI2->CR2 |= SPI_CR2_DS_1; // Data size 0111: 8-bit
SPI2->CR2 |= SPI_CR2_DS_0; // Data size 0111: 8-bit
SPI2->CR2 &= ~SPI_CR2_TXDMAEN; // запретить отправку через DMA
SPI2->CR2 &= ~SPI_CR2_RXDMAEN; // запретить прием через DMA
SPI2->CR2 |= SPI_CR2_FRXTH; // 1: RXNE event is generated if the FIFO level is greater than or equal to 1/4 (8-bit)
SPI2->CR1 |= SPI_CR1_SPE; // включить SPI
}
void spiLocalReadData(unsigned char * dataPointer, unsigned short sizeData) { // получить данные из шины
DMA1_Channel4->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет прием
DMA1_Channel5->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет передача
while ((SPI2->SR & SPI_SR_RXNE) || (SPI2->SR & SPI_SR_FRLVL)) SPI2->DR; // вычитываем хвосты оставшиеся в буфере
DMA1_Channel4->CPAR = (unsigned long)(&SPI2->DR); //заносим адрес регистра DR в CPAR
DMA1_Channel4->CMAR = (unsigned long)dataPointer; //заносим адрес данных в регистр CMAR
DMA1_Channel4->CNDTR = sizeData; //количество принимаемых данных
DMA1_Channel4->CCR &= ~DMA_CCR_MEM2MEM; //режим MEM2MEM отключен
DMA1_Channel4->CCR &= ~DMA_CCR_PL; //приоритет низкий
DMA1_Channel4->CCR &= ~DMA_CCR_MSIZE; //разрядность данных в памяти 8 бит
DMA1_Channel4->CCR &= ~DMA_CCR_PSIZE; //разрядность регистра данных 8 бит
DMA1_Channel4->CCR |= DMA_CCR_MINC; //Включить инкремент адреса памяти
DMA1_Channel4->CCR &= ~DMA_CCR_PINC; //Инкремент адреса периферии отключен
DMA1_Channel4->CCR &= ~DMA_CCR_CIRC; //кольцевой режим отключен
DMA1_Channel4->CCR &= ~DMA_CCR_DIR; //0 - из периферии в память
SPI2->CR2 |= SPI_CR2_RXDMAEN; // разрешить прием через DMA
DMA1_Channel4->CCR |= DMA_CCR_EN; // включаем прием данных
static unsigned char _filler = 0xFF; // значение, которое будем пихать в шину для получения данных
DMA1_Channel5->CPAR = (unsigned long)(&SPI2->DR); //заносим адрес регистра DR в CPAR
DMA1_Channel5->CMAR = (unsigned long)(&_filler); //заносим адрес данных в регистр CMAR
DMA1_Channel5->CNDTR = sizeData; //количество передаваемых данных
DMA1_Channel5->CCR &= ~DMA_CCR_MEM2MEM; //режим MEM2MEM отключен
DMA1_Channel5->CCR &= ~DMA_CCR_PL; //приоритет низкий
DMA1_Channel5->CCR &= ~DMA_CCR_MSIZE; //разрядность данных в памяти 8 бит
DMA1_Channel5->CCR &= ~DMA_CCR_PSIZE; //разрядность регистра данных 8 бит
DMA1_Channel5->CCR &= ~DMA_CCR_MINC; //Отключить инкремент адреса памяти
DMA1_Channel5->CCR &= ~DMA_CCR_PINC; //Инкремент адреса периферии отключен
DMA1_Channel5->CCR &= ~DMA_CCR_CIRC; //кольцевой режим отключен
DMA1_Channel5->CCR |= DMA_CCR_DIR; //1 - из памяти в периферию
SPI2->CR2 |= SPI_CR2_TXDMAEN; // разрешить отправку через DMA
DMA1_Channel5->CCR |= DMA_CCR_EN; // включаем передачу данных
}
void spiReadData(unsigned char * dataPointer, unsigned short sizeData) { // получить данные из шины
nssStart(); // говорим slave устройству что начинаем работать
flStartMode = 1; // помечаем что получаем данные
countRXdata = sizeData; // сохраним сколько собираемся принять
spiLocalReadData(dataPointer, sizeData); // запускаем прием
}
void spiLocalSendData(unsigned char * dataPointer, unsigned short sizeData) { // отправить данные в шину
DMA1_Channel5->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет передача
DMA1_Channel5->CPAR = (unsigned long)(&SPI2->DR); //заносим адрес регистра DR в CPAR
DMA1_Channel5->CMAR = (unsigned long)dataPointer; //заносим адрес данных в регистр CMAR
DMA1_Channel5->CNDTR = sizeData; //количество передаваемых данных
DMA1_Channel5->CCR &= ~DMA_CCR_MEM2MEM; //режим MEM2MEM отключен
DMA1_Channel5->CCR &= ~DMA_CCR_PL; //приоритет низкий
DMA1_Channel5->CCR &= ~DMA_CCR_MSIZE; //разрядность данных в памяти 8 бит
DMA1_Channel5->CCR &= ~DMA_CCR_PSIZE; //разрядность регистра данных 8 бит
DMA1_Channel5->CCR |= DMA_CCR_MINC; //Включить инкремент адреса памяти
DMA1_Channel5->CCR &= ~DMA_CCR_PINC; //Инкремент адреса периферии отключен
DMA1_Channel5->CCR &= ~DMA_CCR_CIRC; //кольцевой режим отключен
DMA1_Channel5->CCR |= DMA_CCR_DIR; //1 - из памяти в периферию
SPI2->CR2 |= SPI_CR2_TXDMAEN; // разрешить отправку через DMA
DMA1_Channel5->CCR |= DMA_CCR_EN; // включаем передачу данных
}
void spiSendData(unsigned char * dataPointer, unsigned short sizeData) { // отправить данные в шину
nssStart(); // говорим slave устройству что начинаем работать
flStartMode = 0; // помечаем что отправляем данные
countTXdata = sizeData; // сохраним сколько собираемся отправить
spiLocalSendData(dataPointer, sizeData); // начинаем отправку
}
void spiSendAndReadData(unsigned char * dataOutPointer, unsigned short sizeOutData, unsigned char * dataInPointer, unsigned short sizeInData) { // отправить данные в шину и сразу же получить не отсключая SS
nssStart(); // говорим slave устройству что начинаем работать
flStartMode = 2; // помечаем что отправляем данные потом сразу получаем
countTXdata = sizeOutData; // сохраним сколько собираемся отправить
countRXdata = sizeInData; // сохраним сколько собираемся принять
localDataInPointer = dataInPointer; // сохраним куда данные класть при приеме
flNextRead = 1; // после обработки передачи запустим прием
spiLocalSendData(dataOutPointer, sizeOutData); // начинаем отправку
}
void spiSendTwoData(unsigned char * dataOutPointer, unsigned short sizeOutData, unsigned char * dataTwoPointer, unsigned short sizeTwoData) { // отправить две части подряд не отсключая SS
nssStart(); // говорим slave устройству что начинаем работать
flStartMode = 3; // помечаем что отправляем данные потом сразу получаем
countTXdata = sizeOutData; // сохраним сколько собираемся отправить
countRXdata = sizeTwoData; // сохраним сколько собираемся принять
localDataInPointer = dataTwoPointer; // сохраним куда данные класть при приеме
flNextRead = 1; // после обработки передачи запустим прием
spiLocalSendData(dataOutPointer, sizeOutData); // начинаем отправку
}
_Bool spiBUSY(void) { // занятость интерфейса
if (inWork) { // в работе
switch (flStartMode) { // работаем в зависимости от режима
case 0: { // send
if ((!(SPI2->SR & SPI_SR_BSY)) && (!(SPI2->SR & SPI_SR_FTLVL))) {
DMA1_Channel5->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет передача
SPI2->CR2 &= ~SPI_CR2_TXDMAEN; // запретить отправку через DMA
DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT < 144) {} // задержка 2 мкс (2 * 72 )
nssStop(); // говорим устройству что закончили
DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT < 216) {} // задержка 3 мкс (3 * 72 )
if (callbackTX) callbackTX(countTXdata); // запускаем внешний обработчик если он есть
} else {
return 1;
}
break;
}
case 1: { // read
if ((!(SPI2->SR & SPI_SR_BSY)) && (!(SPI2->SR & SPI_SR_FRLVL))) {
DMA1_Channel4->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет прием
DMA1_Channel5->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет передача
SPI2->CR2 &= ~SPI_CR2_RXDMAEN; // запретить прием через DMA
SPI2->CR2 &= ~SPI_CR2_TXDMAEN; // запретить отправку через DMA
DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT < 144) {} // задержка 2 мкс (2 * 72 )
nssStop(); // говорим устройству что закончили
DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT < 216) {} // задержка 3 мкс (3 * 72 )
if (callbackRX) callbackRX(countRXdata); // запускаем внешний обработчик если он есть
} else {
return 1;
}
break;
}
case 2: { // send and read
if ((!(SPI2->SR & SPI_SR_BSY)) && (!(SPI2->SR & SPI_SR_FTLVL))) {
if (flNextRead) { // если надо получить теперь данные
flNextRead = 0; // при следующем входе - закончим все
DMA1_Channel5->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет передача
SPI2->CR2 &= ~SPI_CR2_TXDMAEN; // запретить отправку через DMA
spiLocalReadData(localDataInPointer, countRXdata); // запускаем прием
} else {
DMA1_Channel4->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет прием
DMA1_Channel5->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет передача
SPI2->CR2 &= ~SPI_CR2_RXDMAEN; // запретить прием через DMA
SPI2->CR2 &= ~SPI_CR2_TXDMAEN; // запретить отправку через DMA
DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT < 144) {} // задержка 2 мкс (2 * 72 )
nssStop(); // говорим устройству что закончили
DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT < 216) {} // задержка 3 мкс (3 * 72 )
if (callbackRX) callbackRX(countRXdata); // запускаем внешний обработчик если он есть
}
} else {
return 1;
}
break;
}
case 3: { // send and send
if ((!(SPI2->SR & SPI_SR_BSY)) && (!(SPI2->SR & SPI_SR_FTLVL))) {
DMA1_Channel5->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет передача
SPI2->CR2 &= ~SPI_CR2_TXDMAEN; // запретить отправку через DMA
if (flNextRead) { // если надо отправить вторую часть
flNextRead = 0; // при следующем входе - закончим все
spiLocalSendData(localDataInPointer, countRXdata); // запускаем вторую отправку
} else {
DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT < 144) {} // задержка 2 мкс (2 * 72 )
nssStop(); // говорим устройству что закончили
DWT->CYCCNT = 0; // сброс счётчика
while(DWT->CYCCNT < 216) {} // задержка 3 мкс (3 * 72 )
if (callbackTX) callbackTX(countTXdata); // запускаем внешний обработчик если он есть
}
} else {
return 1;
}
break;
}
default:{}
}
}
return 0;
}
- W5500
//w55v2.h // модуль работы с Ethernet модулем W5500
#ifndef W55V2_H
#define W55V2_H
#include "stm32f30x.h"
#include "f303spi2.h" // работа с SPI
//#include "f303u2log.h" // работа с UART
#define periodTimeOutSocketInit ((unsigned long)2UL) // ожидание инициализации сокета
#define periodTimeOutSocketOpen ((unsigned long)2UL) // ожидание открытия сокета
#define periodTimeOutSocketListen ((unsigned long)2UL) // ожидание прослушивания сокета
void initUnitW5500(void); // инициализация модуля
extern unsigned long currentMillis; // будем использовать внешний миллисекундный таймер для создания пауз
void processW5500(void); // работа модуля в бесконечном цикле
_Bool w5500BUSY(void); // занятость модуля какой то логической задачей, посылать следующую только при освобождении
void openListenSocket(unsigned char workSocket, unsigned short listenPort, void (*localCallbackSn_IR)(unsigned char), void (*localCallbackError)(unsigned char)); // открыть сокет для принятия внешних подключений
void readDataFromSocket(unsigned char workSocket, unsigned char * workReadBuf, unsigned short workSizeBuf, void (*localCallbackRead)(unsigned short, unsigned short, unsigned short)); // читаем данные из сокета
void sendDataToSocket(unsigned char workSocket, unsigned char * workSendBuf, unsigned short workSizeSend); // отправка данных в сокета
void disconnectSocket(unsigned char workSocket); // отключаемся
#define IP_ADDR {10,21,0,14}
#define IP_GATE {10,21,0,1}
#define IP_MASK {255,255,255,240}
#define MAC_ADDR {0x00,0x15,0x42,0xBF,0xF0,0x51}
//-------------------------------------------------- BSB[4:0] регистры стр 15 документации
#define BSB_COMMON 0x00
#define BSB_S0 0x01
#define BSB_S0_TX 0x02
#define BSB_S0_RX 0x03
#define BSB_S1 0x05
#define BSB_S1_TX 0x06
#define BSB_S1_RX 0x07
#define BSB_S2 0x09
#define BSB_S2_TX 0x0A
#define BSB_S2_RX 0x0B
#define BSB_S3 0x0D
#define BSB_S3_TX 0x0E
#define BSB_S3_RX 0x0F
#define BSB_S4 0x11
#define BSB_S4_TX 0x12
#define BSB_S4_RX 0x13
#define BSB_S5 0x15
#define BSB_S5_TX 0x16
#define BSB_S5_RX 0x17
#define BSB_S6 0x19
#define BSB_S6_TX 0x1A
#define BSB_S6_RX 0x1B
#define BSB_S7 0x1D
#define BSB_S7_TX 0x1E
#define BSB_S7_RX 0x1F
//-------------------------------------------------- режим записи/чтения
#define RWB_WRITE 1
#define RWB_READ 0
//-------------------------------------------------- режимы отправки фиксированной длины стр 24 доки
#define OM_FDM0 0x00//режим передачи данных переменной длины
#define OM_FDM1 0x01//режим передачи данных по одному байту
#define OM_FDM2 0x02//режим передачи данных по два байта
#define OM_FDM3 0x03//режим передачи данных по четыре байта
//-------------------------------------------------- адреса регистров
#define MR 0x0000 //Mode Register // регистр режима работы
//-------------------------------------------------- адреса регистров
#define SHAR0 0x0009 //Source Hardware Address Register MSB
#define SHAR1 0x000A
#define SHAR2 0x000B
#define SHAR3 0x000C
#define SHAR4 0x000D
#define SHAR5 0x000E // LSB
#define GWR0 0x0001 //Gateway IP Address Register MSB
#define GWR1 0x0002
#define GWR2 0x0003
#define GWR3 0x0004 // LSB
#define SUBR0 0x0005 //Subnet Mask Register MSB
#define SUBR1 0x0006
#define SUBR2 0x0007
#define SUBR3 0x0008 // LSB
#define SIPR0 0x000F //Source IP Address Register MSB
#define SIPR1 0x0010
#define SIPR2 0x0011
#define SIPR3 0x0012 // LSB
#define IR 0x0015 // регистр глобальных прерываний
#define BSBC_IMR 0x0016 // регистр маски глобальных прерываний
#define SIR 0x0017 // регистр прерываний сокетов
#define SIMR 0x0018 // регистр маски прерываний сокетов
//-------------------------------------------------- адреса регистров сокетов
#define Sn_MR 0x0000 // Socket Mode Register
#define Sn_CR 0x0001 // Socket Command Register
#define Sn_IR 0x0002 // Socket Interrupt
#define Sn_SR 0x0003 // регистр статуса сокета
#define Sn_PORT0 0x0004 // Socket 0 Source Port Register MSB
#define Sn_PORT1 0x0005 // Socket 0 Source Port Register LSB
#define Sn_MSSR0 0x0012 // максимальный размер сегмента
#define Sn_MSSR1 0x0013 // максимальный размер сегмента
#define Sn_TX_FSR0 0x0020 // Socket TX Free Size
#define Sn_TX_FSR1 0x0021 // Socket TX Free Size
#define Sn_TX_RD0 0x0022 // Socket TX Read Pointer
#define Sn_TX_RD1 0x0023 // Socket TX Read Pointer
#define Sn_TX_WR0 0x0024 // Socket TX Write Pointer
#define Sn_TX_WR1 0x0025 // Socket TX Write Pointer
#define Sn_RX_RSR0 0x0026 // Socket RX Read Size
#define Sn_RX_RSR1 0x0027 // Socket RX Read Size
#define Sn_RX_RD0 0x0028 // Socket RX Read Pointer
#define Sn_RX_RD1 0x0029 // Socket RX Read Pointer
#define Sn_RX_WR0 0x002A // Socket RX WR Pointer
#define Sn_RX_WR1 0x002B // Socket RX WR Pointer
#define Sn_IMR 0x002C // регистр маски прерываний отдельного сокета
//-------------------------------------------------- Socket mode
#define Mode_CLOSED 0x00
#define Mode_TCP 0x01
#define Mode_UDP 0x02
#define Mode_MACRAV 0x04
//-------------------------------------------------- Socket states
#define SOCK_CLOSED 0x00
#define SOCK_INIT 0x13
#define SOCK_LISTEN 0x14
#define SOCK_ESTABLISHED 0x17
#define SOCK_CLOSE_WAIT 0x1C
#define manualSOCK_TIMEOUT_INIT 0xFF
#define manualSOCK_OPEN_TIMEOUT 0xFE
#define manualSOCK_LISTEN_TIMEOUT 0xFD
//-------------------------------------------------- Socket commands
#define Command_OPEN 0x01
#define Command_LISTEN 0x02
#define Command_CONNECT 0x04
#define Command_DISCON 0x08
#define Command_CLOSE 0x10
#define Command_SEND 0x20
#define Command_SEND_MAC 0x21
#define Command_SEND_KEEP 0x22
#define Command_RECV 0x40
//-------------------------------------------------- Sn_IR bits причина прихода прерывания по сокету
#define Sn_IR_SENDOK 0x10
#define Sn_IR_TIMEOUT 0x08
#define Sn_IR_RECV 0x04
#define Sn_IR_DISCON 0x02
#define Sn_IR_CON 0x01
//--------------------------------------------------
#endif /* W55V2_H */
//w55v2.c // модуль работы с Ethernet модулем W5500
#include "w55v2.h" // работа с W5500
#include "stm32f30x.h"
#include "f303spi2.h" // работа с SPI
static unsigned char bufSendRegWrite[7]; // буфер для записи данных в регистры
static unsigned char bufSendRegRead[4]; // буфер для чтения данных из регистров
volatile static unsigned char dataSIMR; // регистр маски прерываний сокетов, хранится в скетче чтоб не читать лишний раз из модуля
volatile static unsigned char waitSocketData; // ждем получения данных
volatile static unsigned char waitSendData; // ждем отправки данных
volatile static unsigned char currentSocket; // текущий рабочий сокет
volatile static unsigned char jobCurrentStep; // текущий шаг текущей задачи
volatile static unsigned char internalBusyW5500; // внутренний флаг что выполняется некая команда
volatile static unsigned char timerSocket; // таймер по которому мы будем ждать операции по сокету
volatile static unsigned char interruptSocket; // флаг что прилетело прерывание сокета
volatile static unsigned char currentSIR; // прочитанное значение SIR - список сокетов по которым есть прерывание
enum typeBusyInternalW5500 {internalW5500none, internalW5500init/*начальная инициализация модуля*/,
internalOpenListenSocket/*открываем сокет на прослушку порта*/,
internalInterruptSocket/*обрабатываем прерывания*/,
internalReadDataSocket/*читаем данные из сокета*/,
internalSendDataSocket/*отправляем данные в сокет*/,
internalDisconnectSocket/*отключаемся*/} currentInternalBusyW5500; // типы текущих действий модуля
struct typeStructSocket { // структура сокета
unsigned short socketListenPort; // прослушиваемый порт
void (*socketCallbackSn_IR)(unsigned char); // внешний обработчик прерывания
void (*socketCallbackError)(unsigned char); // внешний обработчик прерывания
unsigned short socketSn_RX_RSR; // размер полученных данных
unsigned short socketSn_RX_RD; // адрес начала полученных данных
unsigned short socketSn_RX_WR; // адрес конца полученных данных
unsigned short socketSn_TX_FSR; // размер свободного места
unsigned short socketSn_TX_RD; // адрес начала отправляемых данных
unsigned short socketSn_TX_WR; // адрес конца отправляемых данных
unsigned short socketCurrentPart; // текущая партия которую щас получаем
unsigned short socketCountParts; // всего частей
unsigned short socketSizeLastPart; // размер последней части
unsigned char * socketReadBuf; // куда сливать данные
unsigned short socketSizeReadBuf; // размер буфера для приема данных
unsigned char * socketSendBuf; // откуда брать данные
unsigned short socketSizeSendBuf; // размер буфера отправляемых данных
void (*socketCallbackRead)(unsigned short, unsigned short, unsigned short); // внешний обработчик для принятых данных
};
struct typeStructSocket static massSockets[8]; // массив данных сокетов
unsigned char massBSB_Comm[] = {BSB_S0, BSB_S1, BSB_S2, BSB_S3, BSB_S4, BSB_S5, BSB_S6, BSB_S7}; // массив общих регистров сокетов
unsigned char massBSB_TX[] = {BSB_S0_TX, BSB_S1_TX, BSB_S2_TX, BSB_S3_TX, BSB_S4_TX, BSB_S5_TX, BSB_S6_TX, BSB_S7_TX}; // массив TX регистров сокетов
unsigned char massBSB_RX[] = {BSB_S0_RX, BSB_S1_RX, BSB_S2_RX, BSB_S3_RX, BSB_S4_RX, BSB_S5_RX, BSB_S6_RX, BSB_S7_RX}; // массив RX регистров сокетов
unsigned char ipaddr[4]=IP_ADDR;
unsigned char ipgate[4]=IP_GATE;
unsigned char ipmask[4]=IP_MASK;
unsigned char macaddr[6]=MAC_ADDR;
void delayMillis(unsigned long countMillis) { // задержка на Х миллисекунд - использовать в крайних случаях!!! только при инициализации
unsigned long cms = currentMillis; while ((currentMillis-cms) < countMillis);
}
void initW5500resetPin(void) { // инициализация пина хардварного сброса модуля
// init PA8 for reset W5500
GPIOA->ODR |= GPIO_ODR_8; // HIGH level pin
RCC->AHBENR|=RCC_AHBENR_GPIOAEN; //Включаем тактирование порта GPIOA — 72Мгц
GPIOA->MODER &= ~GPIO_MODER_MODER8_1; // 01 out // режим выход
GPIOA->MODER |= GPIO_MODER_MODER8_0; // 01 out // режим выход
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_8; // 0 push-pull // двухтактный/обычный выход
GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR8; // 00 - 2MHz // частота / быстродействие
}
void w5500hardReset(void) { // аппаратный сброс модуля
GPIOA->ODR &= ~GPIO_ODR_8; // LOW level pin
delayMillis(70UL);
GPIOA->ODR |= GPIO_ODR_8; // HIGH level pin
delayMillis(100UL);
}
void w5500callbackSendSPI(unsigned short goodTXdata) { // обработчик окончания отправки данных
if (waitSendData) { // если ждем окончания отправки
switch (currentInternalBusyW5500) { // смотрим в зависимости от операции
case internalW5500init: { // проинициализировались ОК
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
break;
}
case internalInterruptSocket: { // обработка прерываний
switch (jobCurrentStep) { // обработка в зависимости от шага
case 6: { // отправили очистку прерывания
//writeLOGstr((unsigned char *) "$10$");
waitSendData = 0; // не ждем окончания отправки
jobCurrentStep = 2; // возвращаемся на анализ локального SIR
break;
}
default: {}
}
break;
}
case internalReadDataSocket: { // обработка приема
switch (jobCurrentStep) { // обработка в зависимости от шага
case 5: { // прекратили прием данных
//writeLOGstr((unsigned char *) "#9#");
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
break;
}
default: {}
}
break;
}
case internalSendDataSocket: { // обработка отправки
switch (jobCurrentStep) { // обработка в зависимости от шага
case 5: { // отправилось
waitSendData = 0; // не ждем окончания отправки
++jobCurrentStep; // отправим SEND команду
break;
}
case 7: { // отправилось
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
break;
}
default: {}
}
break;
}
case internalDisconnectSocket: { // послали команду отключения
switch (jobCurrentStep) { // обработка в зависимости от шага
case 1: { // отправилось
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
break;
}
default: {}
}
break;
}
default: {}
}
}
}
void internalProcessInterruptSocketReadSPI(void) { // внутрення процедура - крутится внутри прерывания чтения SPI
switch (jobCurrentStep) { // работаем по шагам
case 1: { // прилетело значение SIR
//writeLOGstr((unsigned char *) "$4=0x"); writeLOGhex(bufSendRegRead[0]);writeLOGbyte('$');
currentSIR = bufSendRegRead[0]; // сохраняем значение
if (currentSIR) { // если есть прерывания
waitSocketData = 0; // не ждем ответа
++jobCurrentStep; // обрабатываем список
} else { // нет прерываний
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
}
break;
}
case 4: { // прилетело значение Sn_IR
//writeLOGstr((unsigned char *) "$8=0x"); writeLOGhex(bufSendRegRead[0]);writeLOGbyte('$');
massSockets[currentSocket].socketCallbackSn_IR(bufSendRegRead[0]); // запускаем обработчик
waitSocketData = 0; // не ждем ответа
++jobCurrentStep; // переходим на шаг очистки Sn_IR
break;
}
default: {}
}
}
void internalProcessListenSocketReadSPI(void) { // внутрення процедура - крутится внутри прерывания чтения SPI
switch (jobCurrentStep) { // работаем по шагам
case 1: { // ждем статус CLOSED
//writeLOGstr((unsigned char *) "^4=0x"); writeLOGhex(bufSendRegRead[0]);writeLOGbyte('^');
if ((bufSendRegRead[0] == SOCK_CLOSED) || (bufSendRegRead[0] == SOCK_CLOSE_WAIT)) { // если сокет свободен
waitSocketData = 0; // не ждем ответа
++jobCurrentStep; // шлем команду открытия и прописываем порты и прерывания
} else if ((currentMillis - timerSocket) >= periodTimeOutSocketInit) { // если слишком долго инициализируемся
//writeLOGstr((unsigned char *) "^4e=0x"); writeLOGhex(bufSendRegRead[0]);writeLOGbyte('^');
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
massSockets[currentSocket].socketCallbackError(manualSOCK_TIMEOUT_INIT); // вызываем внешний обработчик ошибок сокета
} else { // повторно запрашиваем статус
waitSocketData = 0; // не ждем ответа
jobCurrentStep = 0; // опять запросим статус
}
break;
}
case 4: { // ждем статус INIT
//writeLOGstr((unsigned char *) "^7=0x"); writeLOGhex(bufSendRegRead[0]);writeLOGbyte('^');
if (bufSendRegRead[0] == SOCK_INIT) { // если сокет проинициализировался
waitSocketData = 0; // не ждем ответа
++jobCurrentStep; // шлем команду прослушивания
} else if ((currentMillis - timerSocket) >= periodTimeOutSocketOpen) { // если слишком долго инициализируемся
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
massSockets[currentSocket].socketCallbackError(manualSOCK_OPEN_TIMEOUT); // вызываем внешний обработчик ошибок сокета
} else { // повторно запрашиваем статус
waitSocketData = 0; // не ждем ответа
jobCurrentStep = 3; // опять запросим статус
}
break;
}
case 7: { // ждем статус LISTEN
//writeLOGstr((unsigned char *) "^10=0x"); writeLOGhex(bufSendRegRead[0]);writeLOGbyte('^');
if (bufSendRegRead[0] == SOCK_LISTEN) { // если сокет прослушивает
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
// далее работаем с прерываниями
} else if ((currentMillis - timerSocket) >= periodTimeOutSocketListen) { // если слишком долго инициализируемся
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
massSockets[currentSocket].socketCallbackError(manualSOCK_LISTEN_TIMEOUT); // вызываем внешний обработчик ошибок сокета
} else { // повторно запрашиваем статус
waitSocketData = 0; // не ждем ответа
jobCurrentStep = 6; // опять запросим статус
}
break;
}
default: {}
} //
}
void internalProcessReadDataSocketReadSPI(void) { // внутрення процедура - крутится внутри прерывания чтения SPI
switch (jobCurrentStep) { // работаем по шагам
case 1: { // получили данные о размере принятых данных и адресе
massSockets[currentSocket].socketSn_RX_RSR = (bufSendRegRead[0] << 8) & 0xFF00;
massSockets[currentSocket].socketSn_RX_RSR |= bufSendRegRead[1] & 0x00FF;
massSockets[currentSocket].socketSn_RX_RD = (bufSendRegRead[2] << 8) & 0xFF00;
massSockets[currentSocket].socketSn_RX_RD |= bufSendRegRead[3] & 0x00FF;
//writeLOGstr((unsigned char *) "#s4="); writeLOGint(massSockets[currentSocket].socketSn_RX_RSR);writeLOGbyte('#');
//writeLOGstr((unsigned char *) "#p5=0x"); writeLOGhex(massSockets[currentSocket].socketSn_RX_RD);writeLOGbyte('#');
if (massSockets[currentSocket].socketSn_RX_RSR) { // если размер больше нуля - считаем количество блоков
unsigned short localCalcParts = massSockets[currentSocket].socketSn_RX_RSR / massSockets[currentSocket].socketSizeReadBuf;
if (localCalcParts) { // если данных больше чем размер буфера
massSockets[currentSocket].socketCountParts = localCalcParts;
localCalcParts = massSockets[currentSocket].socketSn_RX_RSR % massSockets[currentSocket].socketSizeReadBuf;
if (localCalcParts) { // последний блок не целиковый
++massSockets[currentSocket].socketCountParts;
massSockets[currentSocket].socketSizeLastPart = localCalcParts;
} else { // последний блок целиковы
massSockets[currentSocket].socketSizeLastPart = massSockets[currentSocket].socketSizeReadBuf;
}
} else { // если данных совсем мало
massSockets[currentSocket].socketCountParts = 1;
massSockets[currentSocket].socketSizeLastPart = massSockets[currentSocket].socketSn_RX_RSR;
}
massSockets[currentSocket].socketCurrentPart = 0; // читаем первый блок
waitSocketData = 0; // не ждем ответа
jobCurrentStep = 6; // переходим на чтение регистра Sn_RX_WR
} else { // ничего не пришло
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
massSockets[currentSocket].socketCallbackRead(0, 0, 0); // запускаем внешнее прерывание с нулевыми данными
}
break;
}
case 7: { // получили RX WR
massSockets[currentSocket].socketSn_RX_WR = (bufSendRegRead[0] << 8) & 0xFF00;
massSockets[currentSocket].socketSn_RX_WR |= bufSendRegRead[1] & 0x00FF;
//writeLOGstr((unsigned char *) "#w5=0x"); writeLOGhex(massSockets[currentSocket].socketSn_RX_WR);writeLOGbyte('#');
waitSocketData = 0; // не ждем ответа
jobCurrentStep = 2; // читаем часть данных
break;
}
case 3: { // получили данные из сокета
//writeLOGstr((unsigned char *) "#7#");
if (massSockets[currentSocket].socketCurrentPart == (massSockets[currentSocket].socketCountParts-1)) { // последняя партия
massSockets[currentSocket].socketCallbackRead(massSockets[currentSocket].socketCurrentPart, massSockets[currentSocket].socketCountParts, massSockets[currentSocket].socketSizeLastPart); // запускаем внешнее прерывание
waitSocketData = 0; // не ждем ответа
++jobCurrentStep; // переходим на часть отправки сокету команды подтверждения прочтения всех данных
} else { // не последняя часть
massSockets[currentSocket].socketCallbackRead(massSockets[currentSocket].socketCurrentPart, massSockets[currentSocket].socketCountParts, massSockets[currentSocket].socketSizeReadBuf); // запускаем внешнее прерывание
++massSockets[currentSocket].socketCurrentPart; // будем читать следующую часть
waitSocketData = 0; // не ждем ответа
jobCurrentStep = 2; // возвращаемся на шаг чтения части данных
}
break;
}
default: {}
}
}
void internalProcessSendDataSocketReadSPI(void) { // внутрення процедура - крутится внутри прерывания чтения SPI
switch (jobCurrentStep) { // работаем по шагам
case 1: { // получили данные о размере принятых данных и адресе
massSockets[currentSocket].socketSn_TX_FSR = (bufSendRegRead[0] << 8) & 0xFF00;
massSockets[currentSocket].socketSn_TX_FSR |= bufSendRegRead[1] & 0x00FF;
massSockets[currentSocket].socketSn_TX_RD = (bufSendRegRead[2] << 8) & 0xFF00;
massSockets[currentSocket].socketSn_TX_RD |= bufSendRegRead[3] & 0x00FF;
//writeLOGstr((unsigned char *) "-s3="); writeLOGint(massSockets[currentSocket].socketSn_TX_FSR);writeLOGbyte('-');
//writeLOGstr((unsigned char *) "-p4=0x"); writeLOGhex(massSockets[currentSocket].socketSn_TX_RD);writeLOGbyte('-');
waitSocketData = 0; // не ждем ответа
++jobCurrentStep; // запросим регистр TX WR
break;
}
case 3: { // получили данные о размере принятых данных и адресе
massSockets[currentSocket].socketSn_TX_WR = (bufSendRegRead[0] << 8) & 0xFF00;
massSockets[currentSocket].socketSn_TX_WR |= bufSendRegRead[1] & 0x00FF;
//writeLOGstr((unsigned char *) "-w5=0x"); writeLOGhex(massSockets[currentSocket].socketSn_TX_WR);writeLOGbyte('-');
if (massSockets[currentSocket].socketSizeSendBuf > massSockets[currentSocket].socketSn_TX_FSR) { // если размер отправляемых данных больше чем свободное место
massSockets[currentSocket].socketSizeLastPart = massSockets[currentSocket].socketSn_TX_FSR; // запишем размер отправки равной свободному месту
} else { // если отправляемые данные умещаются
massSockets[currentSocket].socketSizeLastPart = massSockets[currentSocket].socketSizeSendBuf; // запишем размер отправки равной свободному месту
}
//writeLOGstr((unsigned char *) "-b5=0x"); writeLOGhex(massSockets[currentSocket].socketSizeSendBuf);writeLOGbyte('-');
waitSocketData = 0; // не ждем ответа
++jobCurrentStep; // отправляем данные
break;
}
default: {}
}
}
void w5500callbackReadSPI(unsigned short goodRXdata) { // обработчик успешного принятия данных
if (waitSocketData) { // если ждем прихода данных
switch (currentInternalBusyW5500) { // смотрим в зависимости от операции
case internalOpenListenSocket: { // открываем сокет на прослушку
//writeLOGstr((unsigned char *) "^3^");
internalProcessListenSocketReadSPI(); // работаем с шагами
break;
}
case internalInterruptSocket: { // прерывания
//writeLOGstr((unsigned char *) "$3$");
internalProcessInterruptSocketReadSPI(); // работаем с шагами
break;
}
case internalReadDataSocket: { // читаем принятые данные
//writeLOGstr((unsigned char *) "#3#");
internalProcessReadDataSocketReadSPI(); // работаем с шагами
break;
}
case internalSendDataSocket: { // отправка данных
internalProcessSendDataSocketReadSPI(); // работаем с шагами
break;
}
default: {}
}
}
}
void w5500_write1byteReg(unsigned char op, unsigned short addres, unsigned char data) {
while (spiBUSY());
bufSendRegWrite[0] = (unsigned char)(addres >> 8);
bufSendRegWrite[1] = (unsigned char)(addres);
bufSendRegWrite[2] = (unsigned char)(op|(RWB_WRITE<<2));
bufSendRegWrite[3] = (unsigned char)(data);
spiSendData(bufSendRegWrite, 4);
}
void w5500_write2byteReg(unsigned char op, unsigned short addres, unsigned short data) {
while (spiBUSY());
bufSendRegWrite[0] = (unsigned char)(addres >> 8);
bufSendRegWrite[1] = (unsigned char)(addres & 0x00FF);
bufSendRegWrite[2] = (unsigned char)(op|(RWB_WRITE<<2));
bufSendRegWrite[3] = (unsigned char)(data >> 8);
bufSendRegWrite[4] = (unsigned char)(data & 0x00FF);
spiSendData(bufSendRegWrite, 5);
}
void w5500_read1byteReg(unsigned char op, unsigned short addres) {
while (spiBUSY());
bufSendRegWrite[0] = (unsigned char)(addres >> 8);
bufSendRegWrite[1] = (unsigned char)(addres);
bufSendRegWrite[2] = (unsigned char)(op|(RWB_READ<<2));
spiSendAndReadData(bufSendRegWrite, 3, bufSendRegRead, 1);
}
void w5500_read2byteReg(unsigned char op, unsigned short addres) {
while (spiBUSY());
bufSendRegWrite[0] = (unsigned char)(addres >> 8);
bufSendRegWrite[1] = (unsigned char)(addres);
bufSendRegWrite[2] = (unsigned char)(op|(RWB_READ<<2));
spiSendAndReadData(bufSendRegWrite, 3, bufSendRegRead, 2);
}
void w5500_read4byteReg(unsigned char op, unsigned short addres) {
while (spiBUSY());
bufSendRegWrite[0] = (unsigned char)(addres >> 8);
bufSendRegWrite[1] = (unsigned char)(addres);
bufSendRegWrite[2] = (unsigned char)(op|(RWB_READ<<2));
spiSendAndReadData(bufSendRegWrite, 3, bufSendRegRead, 4);
}
void w5500_readLongData(unsigned char op, unsigned short addres, unsigned char * dataPointer, unsigned short sizeReadData) {
while (spiBUSY());
bufSendRegWrite[0] = (unsigned char)(addres >> 8);
bufSendRegWrite[1] = (unsigned char)(addres);
bufSendRegWrite[2] = (unsigned char)(op|(RWB_READ<<2));
spiSendAndReadData(bufSendRegWrite, 3, dataPointer, sizeReadData);
}
void w5500_writeLongData(unsigned char op, unsigned short addres, unsigned char * dataPointer, unsigned short sizeSendData) {
while (spiBUSY());
bufSendRegWrite[0] = (unsigned char)(addres >> 8);
bufSendRegWrite[1] = (unsigned char)(addres);
bufSendRegWrite[2] = (unsigned char)(op|(RWB_WRITE<<2));
spiSendTwoData(bufSendRegWrite, 3, dataPointer, sizeSendData);
}
void initW5500interruptPin(void) { // инициализация пина прерывания от Ethernet модуля
// init PB0 pin as IN // инициализация пина - вход
RCC->AHBENR |= RCC_AHBENR_GPIOBEN; //Включаем тактирование порта GPIOB — 72Мгц
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // включаем тактирование sys cfg
SYSCFG->EXTICR[0] &= ~SYSCFG_EXTICR1_EXTI0; // прерывание 0 на PB порт
SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI0_PB; // прерывание 0 на PB порт
GPIOB->MODER &= ~GPIO_MODER_MODER0; // 00 in // режим входа // для классического режима подсчета импульсов
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR0_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR0_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
EXTI->RTSR &= ~EXTI_RTSR_TR0; // to low key enable // регистр включения нарастающего края импульса
EXTI->FTSR |= EXTI_FTSR_TR0; // to low key enable // регистр включения спадающего края импульса
EXTI->IMR |= EXTI_IMR_MR0; // включаем 0ое внешнеее прерывание
NVIC_EnableIRQ (EXTI0_IRQn); // прописываем прерывание
NVIC_SetPriority(EXTI0_IRQn, 0); // прописываем приоритет
}
void initUnitW5500(void) { // инициализация модуля
internalBusyW5500 = 1; // занимаем модуль
waitSocketData = 0; // не ждем прихода данных
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500init; // прописали чем заняли модуль
initW5500resetPin(); // инициализация пина сброса
w5500hardReset(); // сброс модуля
setCallbackTX(w5500callbackSendSPI); // установим внешний обработчик при окончании передачи
setCallbackRX(w5500callbackReadSPI); // установим внешний обработчик при окончании приема
//Configute Net
unsigned char opcode = (BSB_COMMON<<3)|OM_FDM1; // в главный регистр по 1 байту пишем адрес mac и т д...
w5500_write1byteReg(opcode, SHAR0,macaddr[0]); w5500_write1byteReg(opcode, SHAR1,macaddr[1]); w5500_write1byteReg(opcode, SHAR2,macaddr[2]); w5500_write1byteReg(opcode, SHAR3,macaddr[3]); w5500_write1byteReg(opcode, SHAR4,macaddr[4]); w5500_write1byteReg(opcode, SHAR5,macaddr[5]);
w5500_write1byteReg(opcode, GWR0,ipgate[0]); w5500_write1byteReg(opcode, GWR1,ipgate[1]); w5500_write1byteReg(opcode, GWR2,ipgate[2]); w5500_write1byteReg(opcode, GWR3,ipgate[3]);
w5500_write1byteReg(opcode, SUBR0,ipmask[0]); w5500_write1byteReg(opcode, SUBR1,ipmask[1]); w5500_write1byteReg(opcode, SUBR2,ipmask[2]); w5500_write1byteReg(opcode, SUBR3,ipmask[3]);
w5500_write1byteReg(opcode, SIPR0,ipaddr[0]); w5500_write1byteReg(opcode, SIPR1,ipaddr[1]); w5500_write1byteReg(opcode, SIPR2,ipaddr[2]); w5500_write1byteReg(opcode, SIPR3,ipaddr[3]);
dataSIMR = 0; // не будем реагировать на прерывания сокетов
w5500_write1byteReg(opcode, SIMR, dataSIMR); // прописываем маску прерываний сокетов
w5500_write1byteReg(opcode, BSBC_IMR, 0x00); // не будем реагировать на глобальные прерывания
initW5500interruptPin(); // прописываем прерывание
interruptSocket = 0; // нет прерываний
waitSendData = 1; // ждем окончания отправки
}
void EXTI0_IRQHandler(void) { // прерывание 0
EXTI->PR |= EXTI_PR_PR0; // сбрасываем флаг обработки прерывания
interruptSocket = 1; // прописываем что прилетело прерывание
}
void internalProcessListenSocketProcessW5500(void) { // внутрення процедура - крутится внутри общего процесса
switch (jobCurrentStep) { // работаем по шагам
case 0: { // запросим статус сокета
//writeLOGstr((unsigned char *) "^2^");
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1;
w5500_read1byteReg(opcode, Sn_SR);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
timerSocket = currentMillis; // сбросим таймер
break;
}
case 2: { // настройка сокета порта и команда на открытие
//writeLOGstr((unsigned char *) "^5^");
dataSIMR |= (1 << currentSocket); // глобальной маске прерываний прописываем что данный сокет их ждет
unsigned char opcode = (BSB_COMMON<<3)|OM_FDM1; // в главный регистр сначала запишем
w5500_write1byteReg(opcode, SIMR, dataSIMR); // прописываем маску прерываний сокетов
opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1; // дальше будем опять писать в регистр сокета
w5500_write1byteReg(opcode, Sn_PORT0, (unsigned char) massSockets[currentSocket].socketListenPort >> 8); // прописываем порт
w5500_write1byteReg(opcode, Sn_PORT1, (unsigned char) massSockets[currentSocket].socketListenPort & 0xFF); // прописываем порт
w5500_write1byteReg(opcode, Sn_MR, Mode_TCP); // прописываем режим
w5500_write1byteReg(opcode, Sn_IMR, 0xFF); // будем реагировать на все прерывания сокета
w5500_write1byteReg(opcode, Sn_CR, Command_OPEN); // даем команду на открытие
++jobCurrentStep; // запрос статуса сокета
break;
}
case 3: { // запросим статус сокета
//writeLOGstr((unsigned char *) "^6^");
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1;
w5500_read1byteReg(opcode, Sn_SR);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
timerSocket = currentMillis; // сбросим таймер
break;
}
case 5: { // настройка сокета порта и команда на открытие
//writeLOGstr((unsigned char *) "^8^");
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1;
w5500_write1byteReg(opcode, Sn_CR, Command_LISTEN); // даем команду на прослушку
++jobCurrentStep; // запрос статуса сокета
break;
}
case 6: { // запросим статус сокета
//writeLOGstr((unsigned char *) "^9^");
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1;
w5500_read1byteReg(opcode, Sn_SR);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
timerSocket = currentMillis; // сбросим таймер
break;
}
default: {}
} //
}
void internalProcessInterruptSocket(void) { // обработчик прерываяний в цикле processW5500
switch (jobCurrentStep) { // работаем по шагам
case 0: { // запросить регистр списка по которым прошло прерывание
//writeLOGstr((unsigned char *) "$2$");
unsigned char opcode = (BSB_COMMON<<3)|OM_FDM1; // в главный регистр
w5500_read1byteReg(opcode, SIR);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
break;
}
case 2: { // обрабатываем локальный SIR
//writeLOGstr((unsigned char *) "$5$");
if (currentSIR) { // если есть прерывания
unsigned char i; // счетчик
for(i = 0; i < 8; ++i) { // цикл по всем сокетам
if ((1 << i) & currentSIR) break; // выбрался нужный сокет
}
currentSocket = i; // прописали рабочий сокет
currentSIR &= ~(1 << i); // обнулим выбранный бит
//writeLOGstr((unsigned char *) "$6="); writeLOGint(currentSocket); writeLOGbyte(','); writeLOGint(currentSIR); writeLOGbyte('$');
if (massSockets[currentSocket].socketCallbackSn_IR) { // если у выбранного сокета есть обработчик
++jobCurrentStep; // запрашиваем Sn_IR выбранного сокета
} else { // если нет обработчика
jobCurrentStep = 5; // идем на шаг стирания Sn_IR конкретного сокета для обнуления бита в SIR
}
} else { // нет прерываний
waitSocketData = 0; // не ждем ответа
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalW5500none; // ничего не делаем
internalBusyW5500 = 0; // освобождаем модуль
}
break;
}
case 3: { // запрашиваем Sn_IR выбранного сокета
//writeLOGstr((unsigned char *) "$7$");
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1;
w5500_read1byteReg(opcode, Sn_IR);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
break;
}
case 5: { // очистка Sn_IR
//writeLOGstr((unsigned char *) "$9$");
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1; // дальше будем опять писать в регистр сокета
w5500_write1byteReg(opcode, Sn_IR, 0xFF); // во все разряды пишем 1 для обнуления Sn_IR и SIR разряда сокета
waitSendData = 1; // ждем окончания отправки
++jobCurrentStep; // ждем окончания очистки
break;
}
default: {}
}
}
void internalProcessReadDataSocket(void) { // обработчик чтения в цикле processW5500
switch (jobCurrentStep) { // работаем по шагам
case 0: { // запросить регистры объем данных и местонахождение
//writeLOGstr((unsigned char *) "#2#");
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM3; // читаем 4 байта из регистра сокета
w5500_read4byteReg(opcode, Sn_RX_RSR0);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
break;
}
case 2: { // читаем блок данных
//writeLOGstr((unsigned char *) "#6#");
unsigned char opcode = (massBSB_RX[currentSocket]<<3)|OM_FDM0; // читаем произвольное количество байт из буфера сокета
unsigned short beginRXRDaddress = massSockets[currentSocket].socketSn_RX_RD + (massSockets[currentSocket].socketCurrentPart * massSockets[currentSocket].socketSizeReadBuf);
if (massSockets[currentSocket].socketCurrentPart == (massSockets[currentSocket].socketCountParts-1)) { // последняя партия
w5500_readLongData(opcode, beginRXRDaddress, massSockets[currentSocket].socketReadBuf, massSockets[currentSocket].socketSizeLastPart);
} else { // не последняя часть
w5500_readLongData(opcode, beginRXRDaddress, massSockets[currentSocket].socketReadBuf, massSockets[currentSocket].socketSizeReadBuf);
}
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
break;
}
case 4: { // закончили чтение данных
//writeLOGstr((unsigned char *) "#8#");
massSockets[currentSocket].socketSn_RX_RD = massSockets[currentSocket].socketSn_RX_RD + massSockets[currentSocket].socketSn_RX_RSR; // новое значение регистра начала записи при чтении
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM2; // дальше будем опять писать в регистр сокета 2 байта
w5500_write2byteReg(opcode, Sn_RX_RD0, massSockets[currentSocket].socketSn_RX_RD); // обновим адрес в регистре
opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1; // дальше будем опять писать в регистр сокета 1 байт
w5500_write1byteReg(opcode, Sn_CR, Command_RECV); // даем команду что все приняли
waitSendData = 1; // ждем окончания отправки
++jobCurrentStep; // ждем окончания отправки
break;
}
case 6: { // запросить регистр RX_WR
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM2; // читаем 2 байта из регистра сокета
w5500_read2byteReg(opcode, Sn_RX_WR0);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
break;
}
default: {}
}
}
void internalProcessSendDataSocket(void) { // обработчик отправки в цикле processW5500
switch (jobCurrentStep) { // работаем по шагам
case 0: { // запросить регистры объем данных и местонахождение
//writeLOGstr((unsigned char *) "-2-");
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM3; // читаем 4 байта из регистра сокета
w5500_read4byteReg(opcode, Sn_TX_FSR0);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
break;
}
case 2: { // запросить регистр TX_WR
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM2; // читаем 2 байта из регистра сокета
w5500_read2byteReg(opcode, Sn_TX_WR0);
waitSocketData = 1; // ждем ответа
++jobCurrentStep; // ждем ответа
break;
}
case 4: { // отправка
unsigned char opcode = (massBSB_TX[currentSocket]<<3)|OM_FDM0; // отправляем произвольное количество байт из буфера сокета
w5500_writeLongData(opcode, massSockets[currentSocket].socketSn_TX_RD, massSockets[currentSocket].socketSendBuf, massSockets[currentSocket].socketSizeLastPart); // отправляем
waitSendData = 1; // ждем окончания отправки
++jobCurrentStep; // ждем окончания отправки
break;
}
case 6: { // запускаем отправку командой SEND
massSockets[currentSocket].socketSn_TX_WR = massSockets[currentSocket].socketSn_TX_WR + massSockets[currentSocket].socketSizeLastPart; // обновили новое значение регистра записи
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM2; // дальше будем опять писать в регистр сокета 2 байта
w5500_write2byteReg(opcode, Sn_TX_WR0, massSockets[currentSocket].socketSn_TX_WR); // обновим адрес в регистре
opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1; // дальше будем опять писать в регистр сокета 1 байт
w5500_write1byteReg(opcode, Sn_CR, Command_SEND); // даем команду отправки
waitSendData = 1; // ждем окончания отправки
++jobCurrentStep; // ждем окончания отправки
break;
}
default: {}
}
}
void processW5500(void) { // работа модуля в бесконечном цикле
if (internalBusyW5500) { // что то делаем
switch (currentInternalBusyW5500) { // в зависимости от задачи
case internalOpenListenSocket: { // открыть сокет на прослушку порта
internalProcessListenSocketProcessW5500(); // работаем по шагам
break;
}
case internalInterruptSocket: { // работаем с прерываниями
internalProcessInterruptSocket(); // работаем по шагам
break;
}
case internalReadDataSocket: { // читаем данные
internalProcessReadDataSocket(); // работаем по шагам
break;
}
case internalSendDataSocket: { // читаем данные
internalProcessSendDataSocket(); // работаем по шагам
break;
}
case internalDisconnectSocket: { // отключаемся
if (!jobCurrentStep) {
unsigned char opcode = (massBSB_Comm[currentSocket]<<3)|OM_FDM1; // дальше будем опять писать в регистр сокета 1 байт
w5500_write1byteReg(opcode, Sn_CR, Command_DISCON); // даем команду отключения
waitSendData = 1; // ждем окончания отправки
++jobCurrentStep; // ждем окончания отправки
}
break;
}
default: {}
}
} else { // если нет текущей выполняемой задачи
if (interruptSocket) { // если прилетело прерывание
//writeLOGstr((unsigned char *) "$1$");
interruptSocket = 0; // сбрасываем флаг
internalBusyW5500 = 1; // занимаем модуль
waitSocketData = 0; // не ждем прихода данных
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalInterruptSocket; // прописали чем заняли модуль
jobCurrentStep = 0; // начнем с начала
}
}
}
_Bool w5500BUSY(void) { // занятость модуля какой то логической задачей, посылать следующую только при освобождении
if (!spiBUSY()) { // смотрим сначала сам SPI не занят ли
return internalBusyW5500;
}
return 1;
}
void openListenSocket(unsigned char workSocket, unsigned short listenPort, void (*localCallbackSn_IR)(unsigned char), void (*localCallbackError)(unsigned char)) { // открыть сокет для принятия внешних подключений
//writeLOGstr((unsigned char *) "^1^");
internalBusyW5500 = 1; // занимаем модуль
waitSocketData = 0; // не ждем прихода данных
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalOpenListenSocket; // прописали чем заняли модуль
jobCurrentStep = 0; // начнем с начала
currentSocket = workSocket; // пропишем с каким сокетом работаем
massSockets[workSocket].socketListenPort = listenPort; // сохраним порт
massSockets[workSocket].socketCallbackSn_IR = localCallbackSn_IR; // сохраним обработчик прерываний сокета
massSockets[workSocket].socketCallbackError = localCallbackError; // сохраним обработчик прерываний ошибок
}
void readDataFromSocket(unsigned char workSocket, unsigned char * workReadBuf, unsigned short workSizeBuf, void (*localCallbackRead)(unsigned short, unsigned short, unsigned short)) { // читаем данные из сокета
//writeLOGstr((unsigned char *) "#1#");
internalBusyW5500 = 1; // занимаем модуль
waitSocketData = 0; // не ждем прихода данных
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalReadDataSocket; // прописали чем заняли модуль
jobCurrentStep = 0; // начнем с начала
currentSocket = workSocket; // пропишем с каким сокетом работаем
massSockets[workSocket].socketReadBuf = workReadBuf;
massSockets[workSocket].socketSizeReadBuf = workSizeBuf;
massSockets[workSocket].socketCallbackRead = localCallbackRead;
}
void sendDataToSocket(unsigned char workSocket, unsigned char * workSendBuf, unsigned short workSizeSend) { // отправка данных в сокета
//writeLOGstr((unsigned char *) "-1-");
internalBusyW5500 = 1; // занимаем модуль
waitSocketData = 0; // не ждем прихода данных
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalSendDataSocket; // прописали чем заняли модуль
jobCurrentStep = 0; // начнем с начала
currentSocket = workSocket; // пропишем с каким сокетом работаем
massSockets[workSocket].socketSendBuf = workSendBuf;
massSockets[workSocket].socketSizeSendBuf = workSizeSend;
}
void disconnectSocket(unsigned char workSocket) { // отключаемся
internalBusyW5500 = 1; // занимаем модуль
waitSocketData = 0; // не ждем прихода данных
waitSendData = 0; // не ждем окончания отправки
currentInternalBusyW5500 = internalDisconnectSocket; // прописали чем заняли модуль
jobCurrentStep = 0; // начнем с начала
currentSocket = workSocket; // пропишем с каким сокетом работаем
}
- UART для вывода лога
//f303u2log.h // работа с UART
//stm32f303cct6 UART2 pins PA2 PA3 speed9600 for LOG use
#ifndef F303U2LOG_H
#define F303U2LOG_H
void initUARTlog(void); // инициализация UART лога
void writeLOGbyte(const unsigned char inByte); // кладет байт в отправляемый кольцевой буфер
void writeLOGstr(const unsigned char * inStr); // кладет строку в отправляемый кольцевой буфер
void writeLOGint(const unsigned long inInt); // преобразует число в строку и кладет на отправку
void writeLOGhex(const unsigned long inHex); // аналогично в шестнадцатиричном виде
unsigned char serialLOGavailable(void); // возвращает количество байт которые висят не прочитанные в приемном кольцевом буфере
unsigned char readLOGbyte(void); // читаем входящий байт из кольцевого буфера. Запускать строго при уверенности что есть что считать!
#endif /* F303U2LOG */
//f303u2log.c // работа с UART
//stm32f303cct6 UART2 pins PA2 PA3 speed9600 for LOG use
#include "stm32f30x.h"
#include "f303u2log.h" // работа с UART
#define max_size_fifo_log_buf ((unsigned char)224) // размер кольцевого лог буфера
volatile unsigned char fifoBufTXlog[max_size_fifo_log_buf]; // буфер отправки
volatile unsigned char fifoBufRXlog[max_size_fifo_log_buf]; // буфер приема
volatile unsigned char fifoPosTXlog; // текущая абсолютная позиция отправки
volatile unsigned char fifoPosRXlog; // аналогично для приема
volatile unsigned char fifoCountTXlog; // количество байт на отправку
volatile unsigned char fifoCountRXlog; // количество не прочитанных байт
void localLOGbyte(const unsigned char inByte){ // кладет байт в отправляемый кольцевой буфер
if (fifoCountTXlog < max_size_fifo_log_buf) { // если в кольцевом буфере есть место
fifoBufTXlog[fifoPosTXlog] = inByte; // кладем байт в буфер
++fifoCountTXlog; ++fifoPosTXlog; /*увеличиваем счетчики*/ if (fifoPosTXlog >= max_size_fifo_log_buf) fifoPosTXlog = 0; // если позиция перепрыгрула за границу - обнуляем
}
}
void writeLOGbyte(const unsigned char inByte){ // кладет байт в отправляемый кольцевой буфер // запуск извне
USART2->CR1 &= ~USART_CR1_TXEIE; // запрещаем прерывание на отправку
localLOGbyte(inByte);
USART2->CR1 |= USART_CR1_TXEIE; // разрешаем прерывание на отправку
}
void writeLOGstr(const unsigned char * inStr){ // кладет строку в отправляемый кольцевой буфер
USART2->CR1 &= ~USART_CR1_TXEIE; // запрещаем прерывание на отправку
unsigned char i = 0; // переменная счетчик
while (inStr[i]) { // пока в строке есть не нулевой байт
localLOGbyte(inStr[i]); ++i; // кидаем его в буфер и увеличиваем счетчик
}
USART2->CR1 |= USART_CR1_TXEIE; // разрешаем прерывание на отправку
}
void writeLOGint(const unsigned long inInt){ // преобразует число в строку и кладет на отправку
USART2->CR1 &= ~USART_CR1_TXEIE; // запрещаем прерывание на отправку
unsigned long tmInt = inInt; // сохраняем число во временную переменную
char intBuf[20]; // строковый буфер где мы будем сохранять текст
unsigned char i = 0; // позиция строкового буфера
do {intBuf[i++] = tmInt % 10 + '0';} // кладем остаток от деления на 10 временного числа в строковый буфер - идет с маладшего разряда
while ((tmInt /= 10) > 0); // до тех пор, пока деленное на 10 временная переменная больше нуля
intBuf[i] = '\0'; // заканчиваем строку нулем
for(unsigned char j=i; j>0; --j) { // бежим циклом по строке в обратном порядке
localLOGbyte(intBuf[j-1]); // и кидаем байты в отправляемый буфер
}
USART2->CR1 |= USART_CR1_TXEIE; // разрешаем прерывание на отправку
}
void writeLOGhex(const unsigned long inHex){ // преобразует число в шестнадцатеричную строку и кладет на отправку
USART2->CR1 &= ~USART_CR1_TXEIE; // запрещаем прерывание на отправку
unsigned long tmHex = inHex; // сохраняем число во временную переменную
char hexBuf[20]; // строковый буфер где мы будем сохранять текст
unsigned char i = 0; // позиция строкового буфера
do {
unsigned char k = tmHex % 16; // остаток от деления на 16 временного числа
if (k > 9) hexBuf[i++] = k + '7'; else hexBuf[i++] = k + '0'; // кладем в строковый буфер цифру или букву, если число больше 9
}
while ((tmHex /= 16) > 0); // до тех пор, пока деленное на 16 временная переменная больше нуля
if (i % 2) hexBuf[i++] = '0'; // если коичество символов не четное - добавлем нолик слева (на самом деле справа буфера)
hexBuf[i] = '\0'; // заканчиваем строку нулем
for(unsigned char j=i; j>0; --j) { // бежим циклом по строке в обратном порядке
localLOGbyte(hexBuf[j-1]); // и кидаем байты в отправляемый буфер
}
USART2->CR1 |= USART_CR1_TXEIE; // разрешаем прерывание на отправку
}
void initUARTlog(void) { // init USART2 PA3-RxD PA2-TxD // инициализация UART лога
RCC->AHBENR|=RCC_AHBENR_GPIOAEN; //Включаем тактирование порта GPIOA — 72Мгц
fifoPosTXlog = 0; fifoPosRXlog = 0; fifoCountTXlog = 0; fifoCountRXlog = 0; // сбрасываем все переменные
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; //Подаем тактирование на уарт — 36Мгц
//Настраиваем порт Tx
GPIOA->MODER |= GPIO_MODER_MODER2_1; // 10 — режим альтернативной функции.
GPIOA->MODER &= ~GPIO_MODER_MODER2_0; // 10 — режим альтернативной функции.
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_2; // 0 - двухтактный выход или push-pull сокращено PP (после сброса)
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR2_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR2_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR2_1; // 01 — 10 MHz
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2_0; // 01 — 10 MHz
GPIOA->AFR[0] |=(0x07 << 8); // Назначаем PA2 выводу альтернативную функцию AF7
// Настаиваем Rx
GPIOA->MODER |= GPIO_MODER_MODER3_1; // 10 — режим альтернативной функции.
GPIOA->MODER &= ~GPIO_MODER_MODER3_0; // 10 — режим альтернативной функции.
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR3_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR3_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR3_1; // 01 — 10 MHz
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3_0; // 01 — 10 MHz
GPIOA->AFR[0] |=(0x07 << 12); // Назначаем PA3 выводу альтернативную функцию AF7
// Настраиваем USART2
USART2->BRR = 0xEA6; // 9600 -> 234.375 - 324->0xEA 0.375*16=6=0x6
USART2->CR1 |= USART_CR1_UE|USART_CR1_TE|USART_CR1_RE; // Вкл. uart, приема и передачи
USART2->CR1 |= USART_CR1_RXNEIE; // Разрешаем генерировать прерывание по приему
USART2->CR1 &= ~USART_CR1_M; // длина байта 9 бит
NVIC_EnableIRQ (USART2_IRQn); // Включаем прерывание, указываем вектор
}
void USART2_IRQHandler (void) { // прерывание на прием данных с uart log
if (USART2->ISR & USART_ISR_RXNE){//Следим за состоянием данного флага 1 - данные пришли, 0- пусто
if ((USART2->ISR & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE | USART_ISR_PE)) == 0) { // если нет ошибок приема
unsigned char t = USART2->RDR; // считываем байт из регистра МК
if (fifoCountRXlog < max_size_fifo_log_buf) { // если есть место в приемном буфере
fifoBufRXlog[fifoPosRXlog] = t; // вносим его туда
++fifoCountRXlog; ++fifoPosRXlog; /*увеличиваем счетчики*/ if (fifoPosRXlog >= max_size_fifo_log_buf) fifoPosRXlog = 0; // если позиция превысила размер - обнуляем
}
}
}
while (!(USART2->ISR & USART_ISR_TXE)); // ждем пока отправится байт
if (fifoCountTXlog) { // если есть что отправлять
unsigned char currOut; // выходной байт
if (fifoCountTXlog <= fifoPosTXlog) { // если позиция отправки больше количества отправляемых байт
currOut = fifoBufTXlog[fifoPosTXlog-fifoCountTXlog]; // берем первый вошедший байт
--fifoCountTXlog; // уменьшаем счетчик не отправленных байт
} else { // если хвост кольцевого буфера выходит за границы размеров
currOut = fifoBufTXlog[max_size_fifo_log_buf-(fifoCountTXlog-fifoPosTXlog)]; // берем байт по другой логике
--fifoCountTXlog; // уменьшаем счетчик не отправленных байт
}
USART2->TDR = currOut; // кладем байт в регистр МК для непосредственно отправки
} else { // нечего отправлять
USART2->CR1 &= ~USART_CR1_TXEIE; // запрещаем прерывание на отправку
}
}
unsigned char serialLOGavailable(void) { // возвращает количество байт в приемном буфере лога
return fifoCountRXlog;
}
unsigned char readLOGbyte(void){ // читаем входящий байт из кольцевого буфера
unsigned char currOut; // Переменная куда читать
if (fifoCountRXlog <= fifoPosRXlog) { // если позиция чтения больше остатка
currOut = fifoBufRXlog[fifoPosRXlog-fifoCountRXlog]; // читаем
--fifoCountRXlog; // уменьшаем счетчик количества
} else { // если хвост чтения за границей буфера
currOut = fifoBufRXlog[max_size_fifo_log_buf-(fifoCountRXlog-fifoPosRXlog)]; // читаем по другой формуле
--fifoCountRXlog; // уменьшаем счетчик количества
}
return currOut; // возвращаем прочитанный байт
}
Привет. Dimax подскажи можно ли добавить еще канал для измерения частоты? Решил модернизировать свой частотомер, усилитель неплохой сделал, но тут возникла проблема: сам частотомер измеряет до 20МГц стабильно, с делителем SAB6456 от 5МГц стабильно до 1ГГц и выше должно по идее, тестировал до 200 МГЦ.
Но так как канал 1, то нужно или добавлять вч ключи/переключатель/логика И, или добавить еще канал измерения и подключить его на выход усилителя, а кнопкой или автоматом переключаться между каналами.
В коде ты указал или PA0 или PA5, может можно одновременно использовать
Конечно PA0 сейчас занят, но сделаю новую плату если что
jeka_tm, Входа два, но 32-битный таймер один. Поэтому одновременно может работать только какой-то один вход. Их можно переключать программно регистром AFR, но всё равно ж в итоге будет какая-то физическая кнопка, иначе придётся городить портянку кода ради автодетекта на какой вход из двух пришёл сигнал. Учитывая что один вход уже занят чем-то, то наверно уже нет смысла переделывать. Аппаратный переключатель входа решает все проблемы просто и надёжно.
Сделаю хардварный переключатель, действительно будет проще. На PA0 дисплей висит. Лишь бы возбуда не появилось А вообще заказал SN74LVC1G3157 на подобный случай, или например с есп использовать, если будет нужен больше 1 канала АЦП.
Дело было вечером....делать было нечего....
Решил молодость вспомнить и голову потренировать заодно.
Классическая игра Columns, собирать кубики одного цвета от 3х шт.
STM32F303CCT6 + TFT 320*240
Частота SPI 16 МГц, заполнение всего экрана за 130 миллисекунд.
Для ускорения обмена с шиной используется промежуточный буфер 14400 байт.
Т.к. лень было лепить кнопки, управление игрой через консольную UART программу с ПК, четыре стрелки + пробел - уронить фигуру.
-
-
-
/*
* columns.h
*
* Created on: 18 нояб. 2021 г.
* Author: andycat2013@yandex.ru
*/
#ifndef INC_COLUMNS_H_
#define INC_COLUMNS_H_
void startColumns(unsigned short w_size, unsigned short h_size);
void processColumns(void);
#endif /* INC_COLUMNS_H_ */
/************************ (C) COPYRIGHT andycat2013@yandex.ru *****END OF FILE****/
/*
* columns.c
*
* Created on: 18 нояб. 2021 г.
* Author: andycat2013@yandex.ru
*/
#include "spitft.h"
#include "columns.h"
#include "logusart.h"
#include "stdlib.h"
unsigned short tftWIDTH;
unsigned short tftHEIGHT;
unsigned char whRect;
unsigned char countRectRow = 8; // количество квадратов в строке
unsigned char countRectCol; // количество квадратов в столбце
unsigned char hSpeed;
unsigned long timerBoxMove;
unsigned char boxColSt; // 0-null 1-move 2-game over
unsigned short boxColors[3];
unsigned short boxRealCoord[2]; // X Y
unsigned short boxScope;
#define col_max_count_colors ((unsigned char)7)
unsigned short colColorsRectMass[] = {TFT9341_BLUE, TFT9341_RED, TFT9341_GREEN, TFT9341_CYAN,
TFT9341_MAGENTA, TFT9341_YELLOW, TFT9341_WHITE}; // 0xFFFF = BLACK -> NULL place
#define col_max_hspeed ((unsigned char)20)
#define col_max_col_box ((unsigned char)10)
#define col_max_row_box ((unsigned char)12)
unsigned short col_game_place[col_max_col_box][col_max_row_box]; // x y // игровое поле
#define hStep ((unsigned char)5) // на сколько пикселей падать за шаг СТРОГО! высота кубика должна быть кратна! этому числу
unsigned char good_box_place[col_max_col_box][col_max_row_box]; // x y // поле поиска удаляемых кубиков
void colPrintTFToneRect(unsigned char plX, unsigned char plY, unsigned short rtColor) { // рисуем квадратик по координатам массива
tftFillRect(plX*whRect, tftHEIGHT-(plY+1)*whRect, plX*whRect+whRect-1, tftHEIGHT-(plY+1)*whRect+whRect-1, rtColor);
}
void colPrintBoxByCoord(void) {
tftFillRect(boxRealCoord[0], boxRealCoord[1], boxRealCoord[0]+whRect-1, boxRealCoord[1]+whRect-1, boxColors[0]);
tftFillRect(boxRealCoord[0], boxRealCoord[1]+whRect, boxRealCoord[0]+whRect-1, boxRealCoord[1]+whRect+whRect-1, boxColors[1]);
tftFillRect(boxRealCoord[0], boxRealCoord[1]+whRect+whRect, boxRealCoord[0]+whRect-1, boxRealCoord[1]+whRect+whRect+whRect-1, boxColors[2]);
}
void boxCreateColorsAndCoordAndShow(void) {
boxColors[0] = colColorsRectMass[rand() % col_max_count_colors];
boxColors[1] = colColorsRectMass[rand() % col_max_count_colors];
boxColors[2] = colColorsRectMass[rand() % col_max_count_colors];
// считаем количество свободных столбцов наверху
unsigned char countFreeCol = 0;
for (unsigned char i=0; i< countRectRow; i++) {
if (col_game_place[i][countRectCol-3] == TFT9341_BLACK) ++countFreeCol;
}
if (!countFreeCol) { // некуда ставить новую колонну
boxColSt = 2;
logPrintString((unsigned char *)"Game over! Scope = ");
logPrintLnInt(boxScope);
return;
} else if (countFreeCol == countRectRow) { // все свободные
boxRealCoord[0] = (rand() % countRectRow)*whRect; // ставим в случайное место
} else { // ставим в случайное свободное
unsigned char iX;
do {
iX = rand() % countRectRow;
} while (col_game_place[iX][countRectCol-3] != TFT9341_BLACK);
boxRealCoord[0] = iX*whRect;
}
boxRealCoord[1] = 0;
colPrintBoxByCoord();
}
void startColumns(unsigned short w_size, unsigned short h_size) {
tftWIDTH = w_size;
tftHEIGHT = h_size;
whRect = tftWIDTH / countRectRow;
countRectCol = tftHEIGHT / whRect;
hSpeed = 0;
unsigned long periodFillTFT = HAL_GetTick();
colPrintTFToneRect(1, 2, colColorsRectMass[5]);
periodFillTFT = HAL_GetTick() - periodFillTFT;
logPrintString((unsigned char *)"Fill one box time = ");
logPrintLnInt(periodFillTFT);
tftFillScreen(TFT9341_BLACK);
for(unsigned char x = 0; x < col_max_col_box; x++) {
for(unsigned char y = 0; y < col_max_row_box; y++) {
col_game_place[x][y] = TFT9341_BLACK;
}
}
boxColSt = 0;
boxScope = 0;
}
unsigned char getColBoxByCoord(void) {
return (boxRealCoord[0] / whRect);
}
unsigned char getRowBoxByCoord(void) {
return ((tftHEIGHT - boxRealCoord[1] - 1) / whRect);
}
unsigned char boxMove(void) {
unsigned short lastRealY = boxRealCoord[1] + whRect * 3 - 1; // нижняя часть колонны
unsigned short lastPixels = tftHEIGHT - 1 - lastRealY; // расстояние от колонны до дна
if (lastRealY >= (tftHEIGHT - 1)) return 0; // достигнуто дно стакана
// проверяем что столбец упал на расстояние кратное ячейки, что бы проверить не занята ли она для дальнейшего движения
unsigned char tmY = getRowBoxByCoord();
tmY -= 3; // строка массива где будем смотреть помеху
unsigned char tmX = getColBoxByCoord(); // столбец массива где будем смотреть помеху
if (!(((tftHEIGHT - 1) - lastRealY) % whRect)) { // стоблик на границах массива
if ((col_game_place[tmX][tmY]) != TFT9341_BLACK) { // под столбиком место занято
return 0;
}
}
// сдвигаем столбик вниз
if (lastPixels < hStep) { // если до дна стакана осталось расстояние меньше одного шага
tftFillRect(boxRealCoord[0], boxRealCoord[1], boxRealCoord[0]+whRect-1, boxRealCoord[1]+lastPixels-1, TFT9341_BLACK); // рисуем сверху колонны черную полоску высоту разницы
boxRealCoord[1] += lastPixels;
} else {
tftFillRect(boxRealCoord[0], boxRealCoord[1], boxRealCoord[0]+whRect-1, boxRealCoord[1]+hStep-1, TFT9341_BLACK); // рисуем сверху колонны черную полоску высоту шага
boxRealCoord[1] += hStep;
}
colPrintBoxByCoord();
return 1;
}
void saveBoxToPlace(void) {
unsigned char xBox = getColBoxByCoord();
unsigned char yBox = getRowBoxByCoord();
col_game_place[xBox][yBox] = boxColors[0];
col_game_place[xBox][yBox-1] = boxColors[1];
col_game_place[xBox][yBox-2] = boxColors[2];
}
void boxUp(void) {
unsigned short ibc= boxColors[0];
boxColors[0] = boxColors[1];
boxColors[1] = boxColors[2];
boxColors[2] = ibc;
colPrintBoxByCoord();
}
void boxDown(void) {
unsigned short ibc= boxColors[2];
boxColors[2] = boxColors[1];
boxColors[1] = boxColors[0];
boxColors[0] = ibc;
colPrintBoxByCoord();
}
void boxFall(void) {
unsigned char lastY = (tftHEIGHT - (boxRealCoord[1] + whRect*3 - 1) - 1) / whRect; // последняя линия столбика в массиве
unsigned short readyLastCoordY; // нижняя граница до которой будем опускать
unsigned char lastX = getColBoxByCoord(); // столбец по которому будем опускать
if (!lastY) { // если он уже внизу - опускаем на самую нижнюю ячейку
readyLastCoordY = tftHEIGHT-1;
} else { // будем искать незанятые ячейки под столбиком
unsigned char freePosY = 0;
for (unsigned char iy = 0; iy < (countRectCol-3); ++iy) {
if ((col_game_place[lastX][iy]) == TFT9341_BLACK) break;
++freePosY;
}
readyLastCoordY = tftHEIGHT - 1 - freePosY*whRect;
}
lastY = readyLastCoordY - whRect*3 + 1;
// чистим старое место
tftFillRect(boxRealCoord[0], boxRealCoord[1], boxRealCoord[0]+whRect-1, boxRealCoord[1]+whRect*3-1, TFT9341_BLACK);
// рисуем на новом
boxRealCoord[1] = lastY;
colPrintBoxByCoord();
}
void boxLeft(void) {
unsigned char fX = getColBoxByCoord();
if (!fX) return;
// находим место в массиве слева от нижнего кубика
unsigned char fY = (tftHEIGHT - 1 - (boxRealCoord[1] + whRect * 3 - 1)) / whRect;
--fX;
if ((col_game_place[fX][fY]) == TFT9341_BLACK) {
// чистим старое место
tftFillRect(boxRealCoord[0], boxRealCoord[1], boxRealCoord[0]+whRect-1, boxRealCoord[1]+whRect*3-1, TFT9341_BLACK);
// рисуем на новом
boxRealCoord[0] -= whRect;
colPrintBoxByCoord();
}
}
void boxRight(void) {
unsigned char fX = getColBoxByCoord();
if (fX >= (countRectRow - 1)) return;
// находим место в массиве справа от нижнего кубика
unsigned char fY = (tftHEIGHT - 1 - (boxRealCoord[1] + whRect * 3 - 1)) / whRect;
++fX;
if ((col_game_place[fX][fY]) == TFT9341_BLACK) {
// чистим старое место
tftFillRect(boxRealCoord[0], boxRealCoord[1], boxRealCoord[0]+whRect-1, boxRealCoord[1]+whRect*3-1, TFT9341_BLACK);
// рисуем на новом
boxRealCoord[0] += whRect;
colPrintBoxByCoord();
}
}
unsigned char findAndProcessHline(unsigned char lnX, unsigned char lnY) { // поиск цветов в горизонтальной линии
if ((col_game_place[lnX][lnY] != TFT9341_BLACK) && (col_game_place[lnX][lnY] == col_game_place[lnX+1][lnY]) &&
(col_game_place[lnX][lnY] == col_game_place[lnX+2][lnY])) {
good_box_place[lnX][lnY] = 1; good_box_place[lnX+1][lnY] = 1; good_box_place[lnX+2][lnY] = 1;
return 3;
} else {
return 0;
}
}
unsigned char findAndProcessVline(unsigned char lnX, unsigned char lnY) { // поиск цветов в vert линии
if ((col_game_place[lnX][lnY] != TFT9341_BLACK) && (col_game_place[lnX][lnY] == col_game_place[lnX][lnY+1]) &&
(col_game_place[lnX][lnY] == col_game_place[lnX][lnY+2])) {
good_box_place[lnX][lnY] = 1; good_box_place[lnX][lnY+1] = 1; good_box_place[lnX][lnY+2] = 1;
return 3;
} else {
return 0;
}
}
unsigned char findAndProcessColors(unsigned char inX, unsigned char inY) { // поиск цветов в квадрате 3 на 3
unsigned char outColors = findAndProcessHline(inX, inY);
outColors += findAndProcessHline(inX, inY+1);
outColors += findAndProcessHline(inX, inY+2);
outColors += findAndProcessVline(inX, inY);
outColors += findAndProcessVline(inX+1, inY);
outColors += findAndProcessVline(inX+2, inY);
if ((col_game_place[inX][inY] != TFT9341_BLACK) && (col_game_place[inX][inY] == col_game_place[inX+1][inY+1])
&& (col_game_place[inX][inY] == col_game_place[inX+2][inY+2])) {
good_box_place[inX][inY] = 1; good_box_place[inX+1][inY+1] = 1; good_box_place[inX+2][inY+2] = 1;
outColors += 3;
}
if ((col_game_place[inX][inY+2] != TFT9341_BLACK) && (col_game_place[inX][inY+2] == col_game_place[inX+1][inY+1])
&& (col_game_place[inX][inY+2] == col_game_place[inX+2][inY])) {
good_box_place[inX][inY+2] = 1; good_box_place[inX+1][inY+1] = 1; good_box_place[inX+2][inY] = 1;
outColors += 3;
}
return outColors;
}
void moveColorBoxToDown(void) { // очищаем исчезающие кубики и роняем столбики вниз
for (unsigned char iy = countRectCol; iy > 0; --iy) { // цикл по строкам сверху вниз
for (unsigned char ix = 0; ix < countRectRow; ++ix) { // цикл по столбцам
if (good_box_place[ix][iy-1]) { // кубик надо чистить
good_box_place[ix][iy-1] = 0; // очистили в массиве поиска
col_game_place[ix][iy-1] = TFT9341_BLACK; // очистили в главном массиве
colPrintTFToneRect(ix, iy-1, TFT9341_BLACK); // осистили на экране
// ищем кубик вверху столбика
if ((iy-1) < (countRectCol - 1)) { // кубик ниже верхней границы
if (col_game_place[ix][iy] != TFT9341_BLACK) { // сверху кубика что то есть
// ищем верхнюю границу опускаемого столбика
unsigned short busyBoxY;
for (busyBoxY = (countRectCol - 1); busyBoxY >= iy; --busyBoxY) { // сверху вниз
if (col_game_place[ix][busyBoxY] != TFT9341_BLACK) { // если ячейка занята
break; // прерываем цикл - получили верхний кубик для сдвига
}
}
// сдвигаем все кубики столбика вниз
for (unsigned char bbY = iy-1; bbY <= busyBoxY; ++bbY) { // цикл по сдвигаемому столбику снизу
col_game_place[ix][bbY] = col_game_place[ix][bbY+1]; // сдвигаем цвет на 1 вниз
colPrintTFToneRect(ix, bbY, col_game_place[ix][bbY]); // рисуем его
good_box_place[ix][bbY] = 0; // очистили в массиве поиска, что бы повторно не попал в отбор
}
// чистим самый верхний сдвинутый кубик
good_box_place[ix][busyBoxY] = 0; // очистили в массиве поиска
col_game_place[ix][busyBoxY] = TFT9341_BLACK; // очистили в главном массиве
colPrintTFToneRect(ix, busyBoxY, TFT9341_BLACK); // очистили на экране
}
} // --
}
}
}
}
unsigned char processGameScope(void) { // обрабатываем массив поиск одинаковых строк кубиков
unsigned char outBox = 0; // сколько нашли кубиков за один запуск функции
for (unsigned char iy = 0; iy < countRectCol; ++iy) { // цикл по строкам
for (unsigned char ix = 0; ix < countRectRow; ++ix) { // цикл по столбцам
good_box_place[ix][iy] = 0; // чистим массив результатов
}
}
for (unsigned char iy = 0; iy <= (countRectCol - 3); ++iy) { // цикл по строкам
for (unsigned char ix = 0; ix <= (countRectRow - 3); ++ix) { // цикл по столбцам
outBox += findAndProcessColors(ix, iy); // обрабатываем кубиками 3 на 3
}
}
if (outBox) { // если есть что удалять
moveColorBoxToDown(); // чистим и роняем столбики
boxScope += outBox;
}
return outBox;
}
void processColumns(void) {
if (!boxColSt) {
boxColSt = 1;
boxCreateColorsAndCoordAndShow();
timerBoxMove = HAL_GetTick();
} else if (boxColSt == 1) {
if ((HAL_GetTick() - timerBoxMove) >= (1000 / (hSpeed+1))) { // двигаемся вниз
if (!boxMove()) { // если падать некуда - записываем в массив
saveBoxToPlace();
boxColSt = 0; // next box
while (processGameScope()); // считаем ищем одинаковые кубики
hSpeed = boxScope / 100;
} else {
timerBoxMove = HAL_GetTick();
}
}
// управление влево/вправо/падение
if (logAvailable()) {
unsigned char inKey = logReadByte();
//logPrintLnInt(inKey);
switch (inKey) {
case 65: {
boxUp();
break;
}
case 68: {
boxLeft();
break;
}
case 67: {
boxRight();
break;
}
case 66: {
boxDown();
break;
}
case 32: {
boxFall();
break;
}
default:{}
}
}
// конец управления
}
}
/************************ (C) COPYRIGHT andycat2013@yandex.ru *****END OF FILE****/
Код написан в STM32CubeIDE + HAL, если вдруг кому понадобиться полный архив проекта - выложу.
Update: Добавил рисование текста, масштабирование идет от базового шрифта 8*8 в промежуточном буфере + примитивное сглаживание/скругление/срезание углов, входная строка в кодировке UTF-8 или Win1251.
-
/*
* spitft.h
*
* Created on: Nov 18, 2021
* Author: https://narodstream.ru/stm-urok-179-displej-tft-240x320-spi-chast-1/
*/
#ifndef INC_SPITFT_H_
#define INC_SPITFT_H_
#include "stm32f3xx_hal.h"
#define tft_max_size_tx_buf_fill_rect ((unsigned long)14400)
enum TFT9341_ROTATE_t {ROTATE_0, ROTATE_90, ROTATE_180, ROTATE_270};
void tftInit(unsigned short w_size, unsigned short h_size, enum TFT9341_ROTATE_t locTFT9341_ROTATE);
void tftFillRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
void tftFillScreen(uint16_t color);
void tftSetFontSize(unsigned char locFontSize);
void tftSetFontColor(unsigned short locFontColor);
void tftSetBackgroundFont(unsigned short locColorBackground);
void tftPrintString(unsigned short textX, unsigned short textY, unsigned char * inText, unsigned char flConvertFromUTF);
void setSmCorner(unsigned char locFlSm);
//-------------------------------------------------------------------
#define RESET_ACTIVE() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET)
#define RESET_IDLE() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET)
#define CS_ACTIVE() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET)
#define CS_IDLE() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET)
#define DC_COMMAND() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET)
#define DC_DATA() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET)
//-------------------------------------------------------------------
#define TFT9341_MADCTL_MY 0x80
#define TFT9341_MADCTL_MX 0x40
#define TFT9341_MADCTL_MV 0x20
#define TFT9341_MADCTL_ML 0x10
#define TFT9341_MADCTL_RGB 0x00
#define TFT9341_MADCTL_BGR 0x08
#define TFT9341_MADCTL_MH 0x04
#define TFT9341_ROTATION (TFT9341_MADCTL_MX | TFT9341_MADCTL_BGR)
#define TFT9341_BLACK 0x0000
#define TFT9341_BLUE 0x001F
#define TFT9341_RED 0xF800
#define TFT9341_GREEN 0x07E0
#define TFT9341_CYAN 0x07FF
#define TFT9341_MAGENTA 0xF81F
#define TFT9341_YELLOW 0xFFE0
#define TFT9341_WHITE 0xFFFF
//-------------------------------------------------------------------
#define swap(a,b) {int16_t t=a;a=b;b=t;}
//-------------------------------------------------------------------
#endif /* INC_SPITFT_H_ */
/************************ (C) COPYRIGHT andycat2013@yandex.ru *****END OF FILE****/
/*
* spitft.c
*
* Created on: Nov 18, 2021
* Author: https://narodstream.ru/stm-urok-179-displej-tft-240x320-spi-chast-1/
*/
#include "spitft.h"
#include "logusart.h"
#include "FR08x08.h"
#include <string.h>
#include <stdio.h>
extern SPI_HandleTypeDef hspi1;
#define bitRead(x, bitPosition) (((x) >> bitPosition) & 1)
unsigned short tftWIDTH;
unsigned short tftHEIGHT;
unsigned char _txBuf[tft_max_size_tx_buf_fill_rect];
unsigned char _tftCurrentSizeFont = 0;
unsigned short _tftCurrentColorFont = TFT9341_WHITE;
unsigned short _tftCurrentBackgroundColor = TFT9341_BLACK;
unsigned short _tftWfont = 8; // ширина шрифта
unsigned short _tftHfont = 8; // высота шрифта
unsigned char _tftSmCornFont = 0; // Сглаживание шрифтов
void setSmCorner(unsigned char locFlSm) {
_tftSmCornFont = locFlSm;
}
void tftSetFontSize(unsigned char locFontSize) {
_tftCurrentSizeFont = locFontSize;
if (!locFontSize) {
_tftWfont = 8; // ширина шрифта
_tftHfont = 8; // высота шрифта
return;
}
// вычисляем размер шрифта
_tftHfont = 8 + _tftCurrentSizeFont * 8; // высоту всегду увеличиваем кратно 8 пикселям
if (_tftHfont > tftHEIGHT) { // если высота шрифта превышает высоту экрана
_tftCurrentSizeFont = (tftHEIGHT / 8) - 1; // подгоняем размер шрифта под высоту
_tftHfont = 8 + _tftCurrentSizeFont * 8; // высоту всегду увеличиваем кратно 8 пикселям
}
_tftWfont = 6 + _tftCurrentSizeFont * 6; // ширину всегду увеличиваем кратно 6 пикселям
}
void tftSetFontColor(unsigned short locFontColor) {
_tftCurrentColorFont = locFontColor;
}
void tftSetBackgroundFont(unsigned short locColorBackground) {
_tftCurrentBackgroundColor = locColorBackground;
}
void tftConvertStringToWIN1251(unsigned char * utfText) {
unsigned char locPosOriginalStr = 0; // позиция обрабатываемого байта в исходной строке
unsigned char locPosOutStr = 0; // позиция куда класть исходящий байт
while (utfText[locPosOriginalStr]) { // цикл по всей входящей строке
if (utfText[locPosOriginalStr] == 0xD0) { // UTF символ
if (utfText[locPosOriginalStr+1] == 0x81) { // буква Ё
utfText[locPosOutStr] = 0xA8; // кладем символ
++locPosOutStr; // прибавляем выходной счетчик
++locPosOriginalStr; // пропускаем один байт исходной строки
} else if ((utfText[locPosOriginalStr+1] >= 0x90) && (utfText[locPosOriginalStr+1] <= 0xBF)) { // первая часть русской UTF
utfText[locPosOutStr] = 0xC0 + (utfText[locPosOriginalStr+1] - 0x90); // кладем символ
++locPosOutStr; // прибавляем выходной счетчик
++locPosOriginalStr; // пропускаем один байт исходной строки
} else { // или конец строки или байт не относиться к русской UTF
utfText[locPosOutStr] = utfText[locPosOriginalStr]; // кладем байт как есть
++locPosOutStr; // прибавляем выходной счетчик
}
} else if (utfText[locPosOriginalStr] == 0xD1) { // UTF символ
if (utfText[locPosOriginalStr+1] == 0x91) { // буква ё
utfText[locPosOutStr] = 0xB8; // кладем символ
++locPosOutStr; // прибавляем выходной счетчик
++locPosOriginalStr; // пропускаем один байт исходной строки
} else if ((utfText[locPosOriginalStr+1] >= 0x80) && (utfText[locPosOriginalStr+1] <= 0x8F)) { // вторая часть русской UTF
utfText[locPosOutStr] = 0xF0 + (utfText[locPosOriginalStr+1] - 0x80); // кладем символ
++locPosOutStr; // прибавляем выходной счетчик
++locPosOriginalStr; // пропускаем один байт исходной строки
} else { // или конец строки или байт не относиться к русской UTF
utfText[locPosOutStr] = utfText[locPosOriginalStr]; // кладем байт как есть
++locPosOutStr; // прибавляем выходной счетчик
}
} else {
utfText[locPosOutStr] = utfText[locPosOriginalStr]; // кладем байт как есть
++locPosOutStr; // прибавляем выходной счетчик
}
++locPosOriginalStr; // переходим на след символ оригинальной строки
}
utfText[locPosOutStr] = 0; // заканчиваем строку нулем
}
void tftWriteData(unsigned char * buff, size_t buff_size) {
DC_DATA();
while(buff_size > 0) {
unsigned short chunk_size = buff_size > 32768 ? 32768 : buff_size;
HAL_SPI_Transmit(&hspi1, buff, chunk_size, HAL_MAX_DELAY);
buff += chunk_size;
buff_size -= chunk_size;
}
}
void tftSendCommand(unsigned char cmd) {
DC_COMMAND();
HAL_SPI_Transmit (&hspi1, &cmd, 1, 5000);
}
void tftSetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
// column address set
tftSendCommand(0x2A); // CASET
{
uint8_t data[] = { (x0 >> 8) & 0xFF, x0 & 0xFF, (x1 >> 8) & 0xFF, x1 & 0xFF };
tftWriteData(data, sizeof(data));
}
// row address set
tftSendCommand(0x2B); // RASET
{
uint8_t data[] = { (y0 >> 8) & 0xFF, y0 & 0xFF, (y1 >> 8) & 0xFF, y1 & 0xFF };
tftWriteData(data, sizeof(data));
}
// write to RAM
tftSendCommand(0x2C); // RAMWR
}
typedef struct { // структура точки вокруг которой будем сглаживать
unsigned long smPosBuf; // абсолютная позиция в буфере
unsigned char smInnCorn; // 1 внутренний угол 0 наружный угол
unsigned char smDidCorn; // направление заполнения. 0 лево вверх 1 право вверх 2 право вниз 3 лево вниз
} smpoint_t;
#define max_sm_point_buf 50 // максимальное количсетво углов скругления
void tftSmPicOne(smpoint_t * _locOnePoint, unsigned char locCountSmLevel) { // рисуем закругление вокруг точки
unsigned char highC, lowC; // цвет заполнения
unsigned long locCurrentPointBuf = _locOnePoint->smPosBuf; // угловая точка от которой строиться заполнение
if (_locOnePoint->smInnCorn) { // если внутренний угол
switch (_locOnePoint->smDidCorn) {
case 0: {
locCurrentPointBuf -= _tftWfont*2+2;
break;
}
case 1: {
locCurrentPointBuf -= _tftWfont*2-2;
break;
}
case 2: {
locCurrentPointBuf += _tftWfont*2+2;
break;
}
case 3: {
locCurrentPointBuf += _tftWfont*2-2;
break;
}
}
highC = _tftCurrentColorFont >> 8; lowC = _tftCurrentColorFont & 0xFF;
//highC = TFT9341_RED >> 8; lowC = TFT9341_RED & 0xFF;
} else { // наружный угол
highC = _tftCurrentBackgroundColor >> 8; lowC = _tftCurrentBackgroundColor & 0xFF;
//highC = TFT9341_BLUE >> 8; lowC = TFT9341_BLUE & 0xFF;
}
_txBuf[locCurrentPointBuf] = highC; _txBuf[locCurrentPointBuf+1] = lowC;
for (unsigned char smYl = 0; smYl < locCountSmLevel; ++smYl) {
for (unsigned char smXl = 0; smXl < (locCountSmLevel - smYl); ++smXl) {
switch (_locOnePoint->smDidCorn) {
case 0: {
_txBuf[locCurrentPointBuf - _tftWfont*2*smYl - 2*smXl] = highC;
_txBuf[locCurrentPointBuf - _tftWfont*2*smYl - 2*smXl + 1] = lowC;
break;
}
case 1: {
_txBuf[locCurrentPointBuf - _tftWfont*2*smYl + 2*smXl] = highC;
_txBuf[locCurrentPointBuf - _tftWfont*2*smYl + 2*smXl + 1] = lowC;
break;
}
case 2: {
_txBuf[locCurrentPointBuf + _tftWfont*2*smYl + 2*smXl] = highC;
_txBuf[locCurrentPointBuf + _tftWfont*2*smYl + 2*smXl + 1] = lowC;
break;
}
case 3: {
_txBuf[locCurrentPointBuf + _tftWfont*2*smYl - 2*smXl] = highC;
_txBuf[locCurrentPointBuf + _tftWfont*2*smYl - 2*smXl + 1] = lowC;
break;
}
}
}
} // --
}
void tftSmFontBuf(unsigned char locCountSmLevel) { // сглаживание углов шрифта в буфере
if ((!locCountSmLevel) || (locCountSmLevel > 10)) return;
unsigned long locCurrentBufPos = 0; // текущая позиция в буфере, он линейный
smpoint_t locSmPointsBuf[max_sm_point_buf]; // буфер углов для скругления
unsigned char locCurrentSmPoint = 0; // текущая позиция/количество точек для скругления
for (unsigned short bY = 0; bY < _tftHfont; ++bY) { // цикл по строкам символа
for (unsigned short bX = 0; bX < _tftWfont; ++bX) { // цикл по столбцам символа
unsigned char highColor = _txBuf[locCurrentBufPos]; unsigned char lowColor = _txBuf[locCurrentBufPos+1]; // разобьем цвет текущей точки побайтно для уменьшения вычислений по ходу цикла
unsigned short locPointColor = (highColor << 8) + lowColor; // цвет текущей точки
if (locPointColor == _tftCurrentColorFont) { // если цвет совпадает с цветом шрифта - обрабатываем
// --- смотрим что вокруг точки - диагональные точки
unsigned char pUpRt,pLtUp,pDnRt,pLtDn; // если 1 то цвет шрифта 0-пустота или цвет фона
if ((!bX) || (!bY)) { // слева ничего нет
pLtUp = 0; // если слева ничего нет - подразумеваем что там цвет фона
} else { // слева что то есть
if ((highColor == _txBuf[locCurrentBufPos-_tftWfont*2-2]) && (lowColor == _txBuf[locCurrentBufPos-_tftWfont*2-1])) pLtUp = 1; // цвет слева совпадает с цветом шрифта
else pLtUp = 0; // слева другой цвет
}
if ((!bY) || (bX >= (_tftWfont-1))) { // сверху ничего нет
pUpRt = 0; // если сверху ничего нет - подразумеваем что там цвет фона
} else { // сверху что то есть
if ((highColor == _txBuf[locCurrentBufPos-_tftWfont*2+2]) && (lowColor == _txBuf[locCurrentBufPos+3-_tftWfont*2])) pUpRt = 1; // цвет серху совпадает с цветом шрифта
else pUpRt = 0; // сверху другой цвет
}
if ((bX >= (_tftWfont-1)) || (bY >= (_tftHfont-1))) { // справа ничего нет
pDnRt = 0; // если справа ничего нет - подразумеваем что там цвет фона
} else { // справа что то есть
if ((highColor == _txBuf[locCurrentBufPos+_tftWfont*2+2]) && (lowColor == _txBuf[locCurrentBufPos+3+_tftWfont*2])) pDnRt = 1; // цвет справо совпадает с цветом шрифта
else pDnRt = 0; // справо другой цвет
}
if ((bY >= (_tftHfont-1)) || (!bX)) { // снизу ничего нет
pLtDn = 0; // если снизу ничего нет - подразумеваем что там цвет фона
} else { // снизу что то есть
if ((highColor == _txBuf[locCurrentBufPos+_tftWfont*2-2]) && (lowColor == _txBuf[locCurrentBufPos+_tftWfont*2-1])) pLtDn = 1; // цвет снизу совпадает с цветом шрифта
else pLtDn = 0; // снизу другой цвет
}
// --- смотрим верхние нижние левые правые
unsigned char pUpUp,pLtLt,pRtRt,pDnDn; // если 1 то цвет шрифта 0-пустота или цвет фона
if (!bX) { // слева ничего нет
pLtLt = 0; // если слева ничего нет - подразумеваем что там цвет фона
} else { // слева что то есть
if ((highColor == _txBuf[locCurrentBufPos-2]) && (lowColor == _txBuf[locCurrentBufPos-1])) pLtLt = 1; // цвет слева совпадает с цветом шрифта
else pLtLt = 0; // слева другой цвет
}
if (!bY) { // сверху ничего нет
pUpUp = 0; // если сверху ничего нет - подразумеваем что там цвет фона
} else { // сверху что то есть
if ((highColor == _txBuf[locCurrentBufPos-_tftWfont*2]) && (lowColor == _txBuf[locCurrentBufPos-_tftWfont*2+1])) pUpUp = 1; // цвет серху совпадает с цветом шрифта
else pUpUp = 0; // сверху другой цвет
}
if (bX >= (_tftWfont-1)) { // справа ничего нет
pRtRt = 0; // если справа ничего нет - подразумеваем что там цвет фона
} else { // справа что то есть
if ((highColor == _txBuf[locCurrentBufPos+2]) && (lowColor == _txBuf[locCurrentBufPos+3])) pRtRt = 1; // цвет справо совпадает с цветом шрифта
else pRtRt = 0; // справо другой цвет
}
if (bY >= (_tftHfont-1)) { // снизу ничего нет
pDnDn = 0; // если снизу ничего нет - подразумеваем что там цвет фона
} else { // снизу что то есть
if ((highColor == _txBuf[locCurrentBufPos+_tftWfont*2]) && (lowColor == _txBuf[locCurrentBufPos+_tftWfont*2+1])) pDnDn = 1; // цвет снизу совпадает с цветом шрифта
else pDnDn = 0; // снизу другой цвет
}
// --- логика осмотра соседних точек
if (locCurrentSmPoint < max_sm_point_buf) { // есть куда точки сохранять
if ((!pLtUp) && (!pUpRt) && (!pDnRt) && (pLtDn) && (!pUpUp) && (!pRtRt)) { // левый нижний наружный угол // 1
locSmPointsBuf[locCurrentSmPoint].smDidCorn = 3;
locSmPointsBuf[locCurrentSmPoint].smPosBuf = locCurrentBufPos;
locSmPointsBuf[locCurrentSmPoint].smInnCorn = 0;
++locCurrentSmPoint; // увеличиваем счетчик углов
} else if ((pLtUp) && (pUpRt) && (pDnRt) && (!pLtDn) && (pLtLt) && (pDnDn)) { // правый верхний внутренний угол // 2
locSmPointsBuf[locCurrentSmPoint].smDidCorn = 3;
locSmPointsBuf[locCurrentSmPoint].smPosBuf = locCurrentBufPos;
locSmPointsBuf[locCurrentSmPoint].smInnCorn = 1;
++locCurrentSmPoint; // увеличиваем счетчик углов
} else if ((pLtUp) && (!pUpRt) && (!pDnRt) && (!pLtDn) && (!pDnDn) && (!pRtRt)) { // левый верхний наружный угол // 3
locSmPointsBuf[locCurrentSmPoint].smDidCorn = 0;
locSmPointsBuf[locCurrentSmPoint].smPosBuf = locCurrentBufPos;
locSmPointsBuf[locCurrentSmPoint].smInnCorn = 0;
++locCurrentSmPoint; // увеличиваем счетчик углов
} else if ((!pLtUp) && (pUpRt) && (pDnRt) && (pLtDn) && (pLtLt) && (pUpUp)) { // правый нижний внутренний угол // 4
locSmPointsBuf[locCurrentSmPoint].smDidCorn = 0; //--0
locSmPointsBuf[locCurrentSmPoint].smPosBuf = locCurrentBufPos;
locSmPointsBuf[locCurrentSmPoint].smInnCorn = 1;
++locCurrentSmPoint; // увеличиваем счетчик углов
} else if ((!pLtUp) && (pUpRt) && (!pDnRt) && (!pLtDn) && (!pLtLt) && (!pDnDn)) { // правый верхний наружный угол // 5
locSmPointsBuf[locCurrentSmPoint].smDidCorn = 1;
locSmPointsBuf[locCurrentSmPoint].smPosBuf = locCurrentBufPos;
locSmPointsBuf[locCurrentSmPoint].smInnCorn = 0;
++locCurrentSmPoint; // увеличиваем счетчик углов
} else if ((pLtUp) && (!pUpRt) && (pDnRt) && (pLtDn) && (pRtRt) && (pUpUp)) { // левый нижний внутренний угол // 6
locSmPointsBuf[locCurrentSmPoint].smDidCorn = 1; //--1
locSmPointsBuf[locCurrentSmPoint].smPosBuf = locCurrentBufPos;
locSmPointsBuf[locCurrentSmPoint].smInnCorn = 1;
++locCurrentSmPoint; // увеличиваем счетчик углов
} else if ((!pLtUp) && (!pUpRt) && (pDnRt) && (!pLtDn) && (!pUpUp) && (!pLtLt)) { // правый нижний наружный угол // 7
locSmPointsBuf[locCurrentSmPoint].smDidCorn = 2;
locSmPointsBuf[locCurrentSmPoint].smPosBuf = locCurrentBufPos;
locSmPointsBuf[locCurrentSmPoint].smInnCorn = 0;
++locCurrentSmPoint; // увеличиваем счетчик углов
} else if ((pLtUp) && (pUpRt) && (!pDnRt) && (pLtDn) && (pRtRt) && (pDnDn)) { // левый верхний внутренний угол // 8
locSmPointsBuf[locCurrentSmPoint].smDidCorn = 2;
locSmPointsBuf[locCurrentSmPoint].smPosBuf = locCurrentBufPos;
locSmPointsBuf[locCurrentSmPoint].smInnCorn = 1;
++locCurrentSmPoint; // увеличиваем счетчик углов
}
}
// --- end
}
locCurrentBufPos += 2; // переходим на след точку в буфере
}
}
//---
for(unsigned char posSmBuf = 0; posSmBuf < locCurrentSmPoint; ++posSmBuf) { // цикл по всем угловым точкам
tftSmPicOne(&locSmPointsBuf[posSmBuf], locCountSmLevel); // рисуем загругление
}
// -- end
}
void tftPrintChar(unsigned short charX, unsigned short charY, unsigned char inChar) { // вывод одного символа
unsigned long locPosInBuf = 0; // абсолютная позиция в буфере где мы формируем картинку
unsigned char highColor, lowColor;
if (!_tftCurrentSizeFont) { // самый маленький шрифт 8 на 8
for(unsigned char lineY = 0; lineY < _tftHfont; ++lineY) { // цикл по строкам шрифта
for(unsigned char bitY = _tftWfont; bitY > 0; --bitY) { // обратный цикл по горизонтальным битам строки шрифта
if (bitRead(frus08x08[inChar][lineY], (bitY-1))) {
highColor = _tftCurrentColorFont >> 8; lowColor = _tftCurrentColorFont & 0xFF; // разбили цвет побайтно
} else {
highColor = _tftCurrentBackgroundColor >> 8; lowColor = _tftCurrentBackgroundColor & 0xFF; // разбили цвет побайтно
}
_txBuf[locPosInBuf] = highColor; _txBuf[locPosInBuf+1] = lowColor;
locPosInBuf += 2; // сдвигаем позицию записи в выходном буфере
}
}
tftSetAddrWindow(charX, charY, charX + _tftWfont - 1, charY + _tftHfont - 1);
DC_DATA();
HAL_SPI_Transmit(&hspi1, _txBuf, locPosInBuf, HAL_MAX_DELAY); // рисуем символ
} else { // необходимо масштабирование шрифта
unsigned long locUseByteOneChar = _tftWfont * _tftHfont * 2; // количество байт, занимаемого в буфере одним символом
unsigned char locWholePartsW8font = _tftWfont / 8; // количество целыых частей восьмерок в ширине шрифта, фактически во сволько раз увеличивать ширину
unsigned char locLastBitsW8font = _tftWfont % 8; // еще на сколько бит/точек увеличить ширину шрифта
if (locUseByteOneChar <= tft_max_size_tx_buf_fill_rect) { // умещаемся в буфер
for(unsigned char lineY = 0; lineY < 8; ++lineY) { // цикл по строкам шрифта
for(unsigned char repRow = 0; repRow <= _tftCurrentSizeFont; ++repRow) { // повторяем проход по строкам необходимое количество раз = размеру шрифта
for(unsigned char bitY = 8; bitY > 0; --bitY) { // обратный цикл по горизонтальным битам строки шрифта
if (bitRead(frus08x08[inChar][lineY], (bitY-1))) {
highColor = _tftCurrentColorFont >> 8; lowColor = _tftCurrentColorFont & 0xFF; // разбили цвет побайтно
} else {
highColor = _tftCurrentBackgroundColor >> 8; lowColor = _tftCurrentBackgroundColor & 0xFF; // разбили цвет побайтно
}
for(unsigned char repPoint = 0; repPoint < locWholePartsW8font; ++repPoint) { // повторяем по горизонтали нужное количество каждой точки шрифта
_txBuf[locPosInBuf] = highColor; _txBuf[locPosInBuf+1] = lowColor;
locPosInBuf += 2; // сдвигаем позицию записи в выходном буфере
}
switch (locLastBitsW8font) { // в зависимости от остатка добавляемых бит по горизонтали, добавляем определенные позиции
case 2: {
if (((bitY-1) == 0) || ((bitY-1) == 4)) {
_txBuf[locPosInBuf] = highColor; _txBuf[locPosInBuf+1] = lowColor;
locPosInBuf += 2; // сдвигаем позицию записи в выходном буфере
}
break;
}
case 4: {
if (((bitY-1) == 0) || ((bitY-1) == 2) || ((bitY-1) == 4) || ((bitY-1) == 6)) {
_txBuf[locPosInBuf] = highColor; _txBuf[locPosInBuf+1] = lowColor;
locPosInBuf += 2; // сдвигаем позицию записи в выходном буфере
}
break;
}
case 6: {
if (((bitY-1) == 0) || ((bitY-1) == 1) || ((bitY-1) == 2) || ((bitY-1) == 4) || ((bitY-1) == 5) || ((bitY-1) == 6)) {
_txBuf[locPosInBuf] = highColor; _txBuf[locPosInBuf+1] = lowColor;
locPosInBuf += 2; // сдвигаем позицию записи в выходном буфере
}
break;
}
}
}
}
}
// ----- скругляем углы готовой картинки
if ((_tftCurrentSizeFont > 2) && (_tftSmCornFont)) { // угловатый некрасивый размер начинается с третьего
tftSmFontBuf((_tftCurrentSizeFont - 2)/4 + 1);
}
// -----
tftSetAddrWindow(charX, charY, charX + _tftWfont - 1, charY + _tftHfont - 1);
DC_DATA();
HAL_SPI_Transmit(&hspi1, _txBuf, locPosInBuf, HAL_MAX_DELAY); // рисуем символ
} else { // места не хватает, будем разбивать один символ на несколько частей
// уже слишком большие буквы, в реальности больше 4х симовлов на экран не влазит
// смысл реализации под сомнением
}
}
}
void tftPrintString(unsigned short textX, unsigned short textY, unsigned char * inText, unsigned char flConvertFromUTF) {
if ((textY+_tftHfont) > tftHEIGHT) return; // если по высоте символы не умещаются в экран - выходим
unsigned char locPosOriginalStr = 0; // позиция обрабатываемого байта в исходной строке
if (flConvertFromUTF) {
unsigned char inUTF[64]; // для преобразования выделяем буфер
strcpy((char *)inUTF, (char *)inText); // копируем исходный текст в буфер
tftConvertStringToWIN1251(inUTF); // переводим строку в Win1251
while (inUTF[locPosOriginalStr]) { // цикл по всей входящей строке
unsigned short locOutX = textX + locPosOriginalStr * _tftWfont; // координата X куда выводить символ
if ((locOutX + _tftWfont) > tftWIDTH) return; // если символ не умещается по ширине экрана - выходим
tftPrintChar(locOutX, textY, inUTF[locPosOriginalStr]); // выводим символ
++locPosOriginalStr; // переходим на след символ
}
} else while (inText[locPosOriginalStr]) { // цикл по всей входящей строке
unsigned short locOutX = textX + locPosOriginalStr * _tftWfont; // координата X куда выводить символ
if ((locOutX + _tftWfont) > tftWIDTH) return; // если символ не умещается по ширине экрана - выходим
tftPrintChar(locOutX, textY, inText[locPosOriginalStr]); // выводим символ
++locPosOriginalStr; // переходим на след символ
}
}
void tftSendData(unsigned char dt) {
DC_DATA();
HAL_SPI_Transmit (&hspi1, &dt, 1, 5000);
}
void tftReset(void) {
RESET_ACTIVE();
HAL_Delay(5);
RESET_IDLE();
}
void tftInit(unsigned short w_size, unsigned short h_size, enum TFT9341_ROTATE_t locTFT9341_ROTATE) {
unsigned char _locDataSend[16];
CS_ACTIVE();
tftReset();
tftSendCommand(0x01); //Software Reset
HAL_Delay(1000);
//Power Control A
_locDataSend[0] = 0x39;
_locDataSend[1] = 0x2C;
_locDataSend[2] = 0x00;
_locDataSend[3] = 0x34;
_locDataSend[4] = 0x02;
tftSendCommand(0xCB);
tftWriteData(_locDataSend, 5);
//Power Control B
_locDataSend[0] = 0x00;
_locDataSend[1] = 0xC1;
_locDataSend[2] = 0x30;
tftSendCommand(0xCF);
tftWriteData(_locDataSend, 3);
//Driver timing control A
_locDataSend[0] = 0x85;
_locDataSend[1] = 0x00;
_locDataSend[2] = 0x78;
tftSendCommand(0xE8);
tftWriteData(_locDataSend, 3);
//Driver timing control B
_locDataSend[0] = 0x00;
_locDataSend[1] = 0x00;
tftSendCommand(0xEA);
tftWriteData(_locDataSend, 2);
//Power on Sequence control
_locDataSend[0] = 0x64;
_locDataSend[1] = 0x03;
_locDataSend[2] = 0x12;
_locDataSend[3] = 0x81;
tftSendCommand(0xED);
tftWriteData(_locDataSend, 4);
//Pump ratio control
_locDataSend[0] = 0x20;
tftSendCommand(0xF7);
tftWriteData(_locDataSend, 1);
//Power Control,VRH[5:0]
_locDataSend[0] = 0x10;
tftSendCommand(0xC0);
tftWriteData(_locDataSend, 1);
//Power Control,SAP[2:0];BT[3:0]
_locDataSend[0] = 0x10;
tftSendCommand(0xC1);
tftWriteData(_locDataSend, 1);
//VCOM Control 1
_locDataSend[0] = 0x3E;
_locDataSend[1] = 0x28;
tftSendCommand(0xC5);
tftWriteData(_locDataSend, 2);
//VCOM Control 2
_locDataSend[0] = 0x86;
tftSendCommand(0xC7);
tftWriteData(_locDataSend, 1);
//Memory Acsess Control
switch (locTFT9341_ROTATE) {
case ROTATE_0:{
_locDataSend[0] = 0x58;
break;
}
case ROTATE_90:{
_locDataSend[0] = 0x28;
break;
}
case ROTATE_180:{
_locDataSend[0] = 0x88;
break;
}
case ROTATE_270:{
_locDataSend[0] = 0xE8;
break;
}
}
//_locDataSend[0] = 0x48;
tftSendCommand(0x36);
tftWriteData(_locDataSend, 1);
//Pixel Format Set
_locDataSend[0] = 0x55;//16bit
tftSendCommand(0x3A);
tftWriteData(_locDataSend, 1);
//Frame Rratio Control, Standard RGB Color
_locDataSend[0] = 0x00;
_locDataSend[1] = 0x18;
tftSendCommand(0xB1);
tftWriteData(_locDataSend, 2);
//Display Function Control
_locDataSend[0] = 0x08;
_locDataSend[1] = 0x82;
_locDataSend[2] = 0x27;//320 строк
tftSendCommand(0xB6);
tftWriteData(_locDataSend, 3);
//Enable 3G (пока не знаю что это за режим)
_locDataSend[0] = 0x00;//не включаем
tftSendCommand(0xF2);
tftWriteData(_locDataSend, 1);
//Gamma set
_locDataSend[0] = 0x01;//Gamma Curve (G2.2) (Кривая цветовой гаммы)
tftSendCommand(0x26);
tftWriteData(_locDataSend, 1);
//Positive Gamma Correction
_locDataSend[0] = 0x0F;
_locDataSend[1] = 0x31;
_locDataSend[2] = 0x2B;
_locDataSend[3] = 0x0C;
_locDataSend[4] = 0x0E;
_locDataSend[5] = 0x08;
_locDataSend[6] = 0x4E;
_locDataSend[7] = 0xF1;
_locDataSend[8] = 0x37;
_locDataSend[9] = 0x07;
_locDataSend[10] = 0x10;
_locDataSend[11] = 0x03;
_locDataSend[12] = 0x0E;
_locDataSend[13] = 0x09;
_locDataSend[14] = 0x00;
tftSendCommand(0xE0);
tftWriteData(_locDataSend, 15);
//Negative Gamma Correction
_locDataSend[0] = 0x00;
_locDataSend[1] = 0x0E;
_locDataSend[2] = 0x14;
_locDataSend[3] = 0x03;
_locDataSend[4] = 0x11;
_locDataSend[5] = 0x07;
_locDataSend[6] = 0x31;
_locDataSend[7] = 0xC1;
_locDataSend[8] = 0x48;
_locDataSend[9] = 0x08;
_locDataSend[10] = 0x0F;
_locDataSend[11] = 0x0C;
_locDataSend[12] = 0x31;
_locDataSend[13] = 0x36;
_locDataSend[14] = 0x0F;
tftSendCommand(0xE1);
tftWriteData(_locDataSend, 15);
tftSendCommand(0x11);//Выйдем из спящего режима
HAL_Delay(120);
//Display ON
_locDataSend[0] = TFT9341_ROTATION;
tftSendCommand(0x29);
tftWriteData(_locDataSend, 1);
switch (locTFT9341_ROTATE) {
case ROTATE_0:{
tftWIDTH = w_size;
tftHEIGHT = h_size;
break;
}
case ROTATE_90:{
tftWIDTH = h_size;
tftHEIGHT = w_size;
break;
}
case ROTATE_180:{
tftWIDTH = w_size;
tftHEIGHT = h_size;
break;
}
case ROTATE_270:{
tftWIDTH = h_size;
tftHEIGHT = w_size;
break;
}
}
// test show rect
tftFillScreen(TFT9341_BLUE);
tftFillRect(tftWIDTH-50, tftHEIGHT-50, tftWIDTH-1, tftHEIGHT-1, TFT9341_GREEN);
tftFillRect(0, 0, 99, 99, TFT9341_RED);
// text test
tftPrintString(0, 0, (unsigned char *)"0 шрифт. TFT ili9341 320x240.", 1);
tftSetFontSize(1);
tftSetFontColor(TFT9341_YELLOW);
tftSetBackgroundFont(TFT9341_BLACK);
tftPrintString(0, 8, (unsigned char *)"1 font size", 0);
/* tftSetFontSize(6);
tftSetFontColor(TFT9341_WHITE);
tftSetBackgroundFont(TFT9341_BLACK);
tftPrintString(0, 30, (unsigned char *)"6 шрифт", 1);
setSmCorner(1);
tftPrintString(0, 90, (unsigned char *)"6 сглаженный", 1);
setSmCorner(0);
tftSetFontSize(3);
tftSetFontColor(TFT9341_BLACK);
tftSetBackgroundFont(TFT9341_WHITE);
tftPrintString(10, 150, (unsigned char *)"30", 0);*/
setSmCorner(1);
tftPrintString(20, 190, (unsigned char *)"03", 0);
tftSetFontSize(2); tftPrintString(0, 24, (unsigned char *)"2 font", 0);
tftSetFontSize(3); tftPrintString(0, 48, (unsigned char *)"3 font", 0);
tftSetFontSize(4); tftPrintString(0, 80, (unsigned char *)"4 font", 0);
tftSetFontSize(5); tftPrintString(0, 120, (unsigned char *)"5 font", 0);
tftSetFontSize(6); tftPrintString(0, 168, (unsigned char *)"6 font", 0);
tftSetFontColor(TFT9341_WHITE);
tftSetFontSize(7); tftPrintString(70, 8, (unsigned char *)"7 font", 0);
tftSetFontSize(8); tftPrintString(70, 72, (unsigned char *)"8 font", 0);
tftSetFontSize(9); tftPrintString(70, 144, (unsigned char *)"9 font", 0);
tftSetFontColor(TFT9341_MAGENTA);
tftSetFontSize(10); tftPrintString(176, 8, (unsigned char *)"10", 0);
tftSetFontSize(11); tftPrintString(176, 96, (unsigned char *)"11", 0);
// end test
}
/* ...
https://www.cyberforum.ru/digital-signal-processing/thread2094401.html
...
send_sommomd(0x36);
switch(rotate) {
default:
case ROTATE_0: send_data(0x58); briok;
case ROTATE_90: send_data(0x28); briok;
case ROTATE_180: send_data(0x88); briok;
case ROTATE_270: send_data(0xE8); briok;
}
...
т.е. сначала отправляем команду 0х36, и следом соответствующую выбранной ориентации дату...
ЗЫ. Ну и соответственно, при изменении ориентации с книжной на альбомную (или наоборот) - не забудьте махнуть местами значения ширины и высоты дисплея =)
*/
void tftFillRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
if((x1 >= tftWIDTH) || (y1 >= tftHEIGHT) || (x2 >= tftWIDTH) || (y2 >= tftHEIGHT)) return;
if((x1 >= tftWIDTH) || (y1 >= tftHEIGHT) || (x2 >= tftWIDTH) || (y2 >= tftHEIGHT)) return;
if(x1>x2) swap(x1,x2);
if(y1>y2) swap(y1,y2);
tftSetAddrWindow(x1, y1, x2, y2);
unsigned char highColor = color >> 8; unsigned char lowColor = color & 0xFF; // разбили цвет побайтно
unsigned long allPointByte = (x2-x1+1)*(y2-y1+1)*2; // количество сколько всего байт отправлять
unsigned long allWholePartsOfMass = allPointByte / tft_max_size_tx_buf_fill_rect; // сколько целых частей заполнять
unsigned long lastPartBytesMass = allPointByte % tft_max_size_tx_buf_fill_rect; // сколько байт остается в хвосте заполнения
unsigned long countByteToFillBuf = allWholePartsOfMass ? tft_max_size_tx_buf_fill_rect : lastPartBytesMass; // сколько байт будем заполнять в буфере
for (unsigned short i = 0; i < countByteToFillBuf; i += 2) { // заполняем буфер байтами по очереди
_txBuf[i] = highColor; _txBuf[i+1] = lowColor;
}
DC_DATA();
while (allWholePartsOfMass) {
HAL_SPI_Transmit(&hspi1, _txBuf, tft_max_size_tx_buf_fill_rect, HAL_MAX_DELAY);
--allWholePartsOfMass;
}
if (lastPartBytesMass) {
HAL_SPI_Transmit(&hspi1, _txBuf, lastPartBytesMass, HAL_MAX_DELAY);
}
}
void tftFillScreen(uint16_t color) {
tftFillRect(0, 0, tftWIDTH-1, tftHEIGHT-1, color);
}
/************************ (C) COPYRIGHT andycat2013@yandex.ru *****END OF FILE****/
От именно такой экран можно раскочегарить очень сильно. Команды подавать на 16МГц, а поток данных в экранный буфер до 100МГц хавает. А на 60-70МГц даже стабильно работает. 30 ФПС выходит
конкретно в моем случае МК особо не разгонишь, а так да, если постараться то и кино можно крутить
Up to three SPIs are able to communicate up to 18 Mbits/s in slave and master modes in full-duplex and half-duplex communication modes. The 3-bit prescaler gives 8 master mode frequencies and the frame size is configurable from 4 bits to 16 bits.
В SPL куда как нагляднее
I2C_STATUS CI2C::Write(u16 addr, u8 data)
{
I2C_GenerateSTART(I2C1, ENABLE);
if(!WaitForEvent(I2C_EVENT_MASTER_MODE_SELECT)) return I2C_ERR;
I2C_Send7bitAddress(I2C1, I2C_HW_ADDR, I2C_Direction_Transmitter);
if(!WaitForEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) return I2C_ERR;
I2C_SendData(I2C1, addr>>8);
if(!WaitForEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) return I2C_ERR;
I2C_SendData(I2C1, addr&0xFF);
if(!WaitForEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) return I2C_ERR;
I2C_SendData(I2C1, data);
if(!WaitForEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) return I2C_ERR;
I2C_GenerateSTOP(I2C1, ENABLE);
if(!WaitForFlag(I2C_FLAG_BUSY)) return I2C_ERR;
return I2C_OK;
}
Но ты ведь не ищешь легкий путей. К тому же отступать от рукожопия здесь не принято. Вперед на грабли.
сообщения с "утырками" и всякими другими крысоухими змеями буду удалять
В SPL куда как нагляднее
Но ты ведь не ищешь легкий путей. К тому же отступать от рукожопия здесь не принято. Вперед на грабли.
спасибо, я приму к сведению :)
граблей от библиотек я нахватался вдоволь.....
сообщения с "утырками" и всякими другими крысоухими змеями буду удалять
Рукожопия не забудь, а то двойными стандартами запахло...
Ну и засрали вы тему, однако, господа...
По существу - кто какими средами разработки пользуется?
Я тут пересел на рабочем ноуте на Linux, пришлось перелезать с Keil. Сначала писал в саблиме, собирал make, потом чуть попарился и прикрутил Qt Creator и Qbs для сборки. Код arm-none-eabi-gcc собирается чуть побольше, чем Keilом, либо линкую коряво, но в целом прикольно. По сравнению с креатором в плане интерфейса, кеил - блокнот прошлого века, но проект создавать не очень удобно, ручками всё-таки приходится прописывать мелочи в qbs-ном файле. Если кто хочет попробовать, могу свой qbs шаблон скинуть.
Рукожопия не забудь, а то двойными стандартами запахло...
Рукожопие - нормальное анатомическое строение 80% здешних обитателей. Я сам лауреат множества подобных премий
А кто хочет не пробовать а плодотворно работать под линухом, ставьте SES...
Может быть это на какие мысли наведёт? https://narodstream.ru/urok-172-cmsis-stm32f1-i2c-podklyuchaem-vneshnij-eeprom/
Может быть это на какие мысли наведёт? https://narodstream.ru/urok-172-cmsis-stm32f1-i2c-podklyuchaem-vneshnij-eeprom/
Читал, спасибо. Мысли одинаковые во всех статьях и код в разных вариантах уже вылизан по даташиту, но не работает. Хз, надо смотреть что на пинах, экспериментировать надоело.
Резюки то есть на пинах к VCC?
Программная подтяжка к плюсу, на F3 она есть.
40К? ставь 4,7К
40К? ставь 4,7К
Попробую, спасибо.
С процессором понятно, а какую конкретно плату мучаете? У меня были nucleo на 303k8 у них для совместимости с наной i2c был закорочен на выходы A5 A4 и когда а перевёл их в режим DAC i2c перестал работать. Отпаял на плате перемычки - всё заработало. И DAC и i2c/
Да не, плата от robotdyn, всегда от них беру, и рядом контакты всегда проверяю на кз
#Aliexpress 345,44 руб. | STM32F303CCT6 256KB STM32, загрузчик совместим с Arduino IDE или STM прошивкой, ARM Cortex-M4 мини системная плата
https://a.aliexpress.ru/_eKhvwu
nik182, там во первых F1 , а во вторых народстрим это кухарка в кепке, сам не знает а учит.
andycat, мой код рабочий , но как и ранее говорил нужен ла.
Тайминги отличаются , подтяжка нужна .внешняя.
мой код рабочий , но как и ранее говорил нужен ла.
Тайминги отличаются , подтяжка нужна .внешняя.
дождусь логического анализатора, что то никак не выходит. Резисторы подтяжки и сменить на другой чип eeprom (вдруг дохлый) смогу только после четверга.
Убирая программную подтяжку pull-up - МК намертво зависает, что логично, т к не отрабатывают флаги TXE TXIS. Пробовал прописывал timeout на while циклах, зависания нет, но и данные не пишутся.
Update: установка резисторов на SCL SDA ни к чему не привела :(
Если убрали программную подтяжку PULLUP и МК намертво зависло - такое возможно только в случае, если внешние подтягивающие резисторы не работают - подпаяны не правильно или вообще контакта нет. Без них, только внутренней подтяжке, i2c не работает правильно.
Последние пришедшие блюпилы оказались с резистором 1.5к на USB. Полез перепаивать и не пришлось. Маленькая, но радость.
P.S. Влад дал софтверный i2c. http://arduino.ru/forum/programmirovanie/attiny13a-101-primenenie?page=19#comment-225475 . Он работал без проблем на разных чипах. Может быть попробовать на тех же ногах и тех же соединениях как и для аппаратного, что бы убедиться, что со стороны железа нет огрехов?
nik182, какие нафиг контакты, естественно я все проверил, выше почитайте - конечно он виснет без поддержки и это тоже описано. Какой блин блюпил, чип то другой и плата другая (. Не стоит смешивать опят разных систем.
Жду лог анализатор, дальнейшие эксперименты думаю бесполезны.
Блюпилл это моя радость по поводу USB резистора, к Вам отношения не имеет. У меня никогда не было проблем с i2c на stm. Я пользовал F1,F3,F4 на SPL, HAL и LL. Про подтяжку прочитал после отправки предыдущего поста. Меня удивляет Ваш способ поиска проблемы. У меня в похожих условиях уходит пару часов на локализацию и решения проблемы. Обычный путь - деление проблемы на составляющие. Сначала подключаю к точно работающую железку. Делаю тестовый проект в котором есть только i2c на HAL. С ним у i2c у меня проблем никогда не было. Если всё заработало, меняю железку на проблемную. Если не заработало, меняю процессор и проверяю шины. В отладчике, до старта программы на шинах i2c должно быть напряжение питания и просаживаться не до нуля при замыкании шины резистором 1к. Обычно этого хватает что бы устранить причину траблов. В одной из библиотек встретился модуль i2c скана на HAL, выдает результат в uart, поэтому его тоже приходится поднимать. Чаще под отладчиком комментирую вывод и результат смотрю в переменной i2cnum. Я помню про Вашу не любовь к обёрткам, но их можно использовать просто как инструмент отладки.
nik182, как это без проблем? В F1 I2C полный ужас. Вы хоть еррата почитайте...
В F4 немного лучше но то же ужас - http://mcu.goodboard.ru/viewtopic.php?id=14
В F3 - F0 же новые модули и намного лучше...
Ужас, это когда включил и не работает как надо. А если включил повесив LCD BME820 и флэш и всё работает с первого раза и даже без подстроек какойже это ужас? А еррату я всегда читаю, и даже в этой теме ссылки приводил. У Вас были проблемы с i2c которые ерратой решать пришлось? Версия драйверов куба для 103 1.8.0 c 18 года сильно подросла. Может в этом дело?
nik182, хватит уже хвалиться и воздух сотрясать, толку от того что вы правильно проверяете железо с помощью библиотек (hal, spl и т д) - ноль!
PS не в тему конечно, но ни разу не было проблем в железе, и почти всегда собирал пайкой устройство и работало, а если у вас железо сразу не работает --может проблема в руках или в производителе/поставщике.
Проблемы были только с битым железом, которое после замены работало исправно. Как найти причину не работы я уже писал. И почему толку ноль? Вы пробовали? Тоже не работает? Не верю. И про хвалюсь это Вы загнули. Я предлагаю пути поиска проблемы исключительно на собственном положительном опыте. Хотите пройти по собственным граблям - пожалуйста.
nik182, помощью ЛА или хотя бы осцилографа можно решить любую проблему с шинами i2c и spi, даже те что вносят ваши г.либы а вы их не замечаете...
andycat - наблюдаю дискуссию со стороны, мне кажется у вас ступор случился. Вы только и твердите. что все сделали правильно и отвергаете любые советы. Но практика - она такова. что у вас не работает.
Вот вам дали рабочий код в SPL. Несмотря на всю вашу ненависть к библиотекам - может все таки его попробовать? Исключительно ради проверки? - ведь если оно заработает, вы будете точно знать что косяк в вашем коде. А если нет - вероятно проблемы с железом или сборкой
Это не практик это потребитель, в самом плохом смысле...
jeka_tm, специально для тебя :)
Ну вот и сделал. В целом доволен. Правда тот делитель случайно сжег, в итоге собрал на SAB6456, но суть та же, те же глюки: частота на выходе делителя зависит от уровня сигнала на входе, при низком ерунду показывает. Усилители разные по входу ставил, усиливает же все подряд, лучше не становится. От батарейного питания отказался, делитель жрет как мой кот)) Суммарное потребление под 100мА.
Заработал код с I2C с EEPROM 24LC64, достаточно было добавить паузу согласно даташита Write Cycle 5 ms, позже этот костыль буду убирать, смотреть что там выдает или не выдает eeprom
всем огромное спасибо.
P.S. Работает и с программной подтяжкой выводов к плюсу.
Внешние резюки оставь, ты же не можешь гарантировать, что емкость монтажа не будет превышать 50 пФ или внутренние подтяжки никогда не будут больше 40К. Во все циклы добавь таймаут. Работа МК не должна зависеть от внешних факторов.
По поводу задержек, остальной код должен быть неблокирующим. Иначе 1 задержка погоды не сделает.
Друзья, что то застрял на таймерах с внешним тактированием, просьба подсказать.
МК STM32F303CCT6, к пину PA8 подключена пока кнопка с подтяжкой, с которой считаются импульсы.
- пин входной настроен как альтернативная функция AF10 для счета TIM4_ETR
- сами таймеры, TIM3 считает каждую секунду и тупо выводит TIM4->CNT
- но выводит нули :(
Само железо однозначно рабочее, т к этот же функционал, написанный по внешним прерываниям EXTI отлично работает, т е первое нажатие запускает таймер отсчета времени, все остальные импульсы с пина просто увеличивают счетчик. Но хочеться написать без IRQ т е чтоб само все считалось.
Update: Самое непонятное - если я счетчику пропишу некоторое начальное значение TIM4->CNT = 100; то потом при чтении его получаю ноль, т е оно где то обнуляется :(
Update 2: Все заработало, ошибся в написании тактирования таймера :(
Доброго времени суток!
Есть домофон, он шлет импульсы на PA8, их необходимо подсчитать и вывести номер вызывной квартиры. Таймер TIM4 master считает импульсы, если импульсов больше трех, по сравнению TIM4_CNT=TIM4_CCR4 OC4REF -> TRGO запускает slave таймер TIM3 отсчет времени, по окончании отсчета времени срабатывает прерывание, счетчик TIM4_CNT заноситься в переменную, обнуляются счетчики, заново запускается прерывание master TIM4 и все идет по кругу.
Проблема в том, что slave TIM3 таймер запускается только один раз :(
-
апноуты прочитал, разные режимы попробовал, есть подозрение что TIM4 master в принципе не может в таком режиме работать, т к нашел режим в TIM1 который наверное больше бы мне подошел, но на пине PA8 нет TIM1_ETR :(
балуюсь с Ethernet модулем, получился простенький сервер
STM32F303CCT6 + W5500 обмен SPI через DMA
- пробник
- SPI
- W5500
- UART для вывода лога
-
jeka_tm, специально для тебя :)
Привет. Dimax подскажи можно ли добавить еще канал для измерения частоты? Решил модернизировать свой частотомер, усилитель неплохой сделал, но тут возникла проблема: сам частотомер измеряет до 20МГц стабильно, с делителем SAB6456 от 5МГц стабильно до 1ГГц и выше должно по идее, тестировал до 200 МГЦ.
Но так как канал 1, то нужно или добавлять вч ключи/переключатель/логика И, или добавить еще канал измерения и подключить его на выход усилителя, а кнопкой или автоматом переключаться между каналами.
В коде ты указал или PA0 или PA5, может можно одновременно использовать
Конечно PA0 сейчас занят, но сделаю новую плату если что
jeka_tm, Входа два, но 32-битный таймер один. Поэтому одновременно может работать только какой-то один вход. Их можно переключать программно регистром AFR, но всё равно ж в итоге будет какая-то физическая кнопка, иначе придётся городить портянку кода ради автодетекта на какой вход из двух пришёл сигнал. Учитывая что один вход уже занят чем-то, то наверно уже нет смысла переделывать. Аппаратный переключатель входа решает все проблемы просто и надёжно.
можно ли добавить еще канал для измерения частоты?
Да хоть 10... лишь бы таймеров хватило... и вязались между собой...
На СТМ8 три канала прямого счёта... от нефиг делать... На СТМ32 и подавно...
И 32бит таймера не обязательно юзать... можно два по 16бит склеить... а можно и в прерывании переполнения обычного 16бит таймера считать...
Сделаю хардварный переключатель, действительно будет проще. На PA0 дисплей висит. Лишь бы возбуда не появилось А вообще заказал SN74LVC1G3157 на подобный случай, или например с есп использовать, если будет нужен больше 1 канала АЦП.
Дело было вечером....делать было нечего....
Решил молодость вспомнить и голову потренировать заодно.
Классическая игра Columns, собирать кубики одного цвета от 3х шт.
STM32F303CCT6 + TFT 320*240
Частота SPI 16 МГц, заполнение всего экрана за 130 миллисекунд.
Для ускорения обмена с шиной используется промежуточный буфер 14400 байт.
Т.к. лень было лепить кнопки, управление игрой через консольную UART программу с ПК, четыре стрелки + пробел - уронить фигуру.
-
-
-
Код написан в STM32CubeIDE + HAL, если вдруг кому понадобиться полный архив проекта - выложу.
Update: Добавил рисование текста, масштабирование идет от базового шрифта 8*8 в промежуточном буфере + примитивное сглаживание/скругление/срезание углов, входная строка в кодировке UTF-8 или Win1251.
-
От именно такой экран можно раскочегарить очень сильно. Команды подавать на 16МГц, а поток данных в экранный буфер до 100МГц хавает. А на 60-70МГц даже стабильно работает. 30 ФПС выходит
конкретно в моем случае МК особо не разгонишь, а так да, если постараться то и кино можно крутить
Да. Я этим его крутил https://ru.m.wikipedia.org/wiki/Omega_2_(%D0%BC%D0%B8%D0%BA%D1%80%D0%BE%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80) Про проблемы spi по той ссылке написана правда, но не вся правда.