RTC: установка даты и времени через HAL

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Аватара пользователя
afz
Опытный кот
Сообщения: 744
Зарегистрирован: Сб дек 22, 2012 08:17:42
Откуда: Караганда, Казахстан

RTC: установка даты и времени через HAL

Сообщение afz »

Коллеги, подскажите, плз... Никак не въеду в установку даты-времени через HAL. Ситуация элементарная: оставил я плату со своим STM32F407VET6 на полгода в покое, RTC работает от батарейки. Включаю, а часы отстали или убежали на несколько часов.

Через регистры, вроде-бы, все понятно, а вот через HAL что-то никак не въеду. Подкиньте, плз, последовательность вызовов HAL для установки даты и времени?
Кто мешает тебе выдумать порох непромокаемый? (К. Прутков, мысль № 133)
Реклама
Аватара пользователя
Eddy_Em
Собутыльник Кота
Сообщения: 2516
Зарегистрирован: Пт июл 12, 2019 22:52:01
Контактная информация:

Re: RTC: установка даты и времени через HAL

Сообщение Eddy_Em »

А что, надо именно через кал?
Тернист путь…
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда.
Я на гитхабе, в ЖЖ
Реклама
Аватара пользователя
afz
Опытный кот
Сообщения: 744
Зарегистрирован: Сб дек 22, 2012 08:17:42
Откуда: Караганда, Казахстан

Re: RTC: установка даты и времени через HAL

Сообщение afz »

Да я уже тоже склоняюсь к тому, чтобы забить на эту гадость и сделать всё через регистры. Просто у меня большой проект собран из кубиков, там задействована FATFS от Чана, основное взаимодействие с периферией у меня и так через регистры, через кубики идёт только FATFS. А RTC так, между делом, есть оборудование на МК, почему бы его и не использовать. Только посмотрел я на эти кубические драйвера HAL и решил, что эту дрянь я себе в голову загружать не буду. В слабой надежде спросил, может кто это делал, а сам сегодня поразглядывал работу с RTC через регистры. В общем-то, ничего военного, просто я надеялся, что у кого-нибудь найдется готовое решение, которое можно будет скопипастить, и мне можно будет не вникать в подробности этого самого RTC. Получается, нет! Ладно, вникну...
Кто мешает тебе выдумать порох непромокаемый? (К. Прутков, мысль № 133)
Аватара пользователя
Just_Fluffy
Вымогатель припоя
Сообщения: 534
Зарегистрирован: Ср июн 29, 2022 16:25:45

Re: RTC: установка даты и времени через HAL

Сообщение Just_Fluffy »

afz, так вроде FATFS требует от железа только процедуру чтения/отправки байта через SPI... И ее вполне на регистрах можно...
А, в какой то версии еще FATFS хотел тик от таймера 100 раз в сек. Но там сервисные функции - их можно дергать и не строго 100 раз в сек.
Белая и Пушистая
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
Eddy_Em
Собутыльник Кота
Сообщения: 2516
Зарегистрирован: Пт июл 12, 2019 22:52:01
Контактная информация:

Re: RTC: установка даты и времени через HAL

Сообщение Eddy_Em »

Есть же компактные ФС для МК - специально, чтобы не было всякого излишества (скажем, зачем там права пользователя как в ext2?). Можно и вообще свое что-нибудь примитивное реализовать. Допустим, если файлы не нужно удалять, можно сделать последовательную запись, чем-то напоминающую структуру архива tar. Потом его просто при помощи dd к себе забрать и распаковать стандартными средствами.
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда.
Я на гитхабе, в ЖЖ
Реклама
Аватара пользователя
afz
Опытный кот
Сообщения: 744
Зарегистрирован: Сб дек 22, 2012 08:17:42
Откуда: Караганда, Казахстан

Re: RTC: установка даты и времени через HAL

Сообщение afz »

Нет, коллеги, мне была нужна настоящая FAT32, чтобы можно было выдернуть uSD-шку из моего контроллера, сунуть ее в кардридер и работать с ней на PC, потом выдернуть из кардридера, сунуть в мой контроллер и продолжить работу на нем. Если нужны подробности, приведу ссылки, но думаю, что вас это не заинтересует.

А на тему моего вопроса, так я уже, практически, вник в работу с RTC через регистры, да и нашел, где подкрутить, чтобы кубики под ногами не путались, когда мне это не нужно. Не хотелось вникать, надеялся скопипастить, но, увы, пришлось таки вникнуть...

Тогда еще один вопрос.
Хочу задействовать USART1, пусть кубики его проинитят, но потом хочу перехватить у них управление и отработать опять же, через регистры. Напомните, плз, где там подключить свою программу прерываний - вроде-бы там было что-то, объявленное, как weak и туда можно подсунуть свою программулю. Кажется, у них это называется коллбэком...

Добавлено after 2 hours 42 minutes 19 seconds:
Что-то я ничего не нашел внятного про кубические коллбэки. То есть, конечно, никто мне не мешает вписать в программу STM32F4xx_it.c между
/* USER CODE BEGIN USART1_IRQn 0 */ и /* USER CODE END USART1_IRQn 0 */ вызов своей программы обработки прерываний, только вот это еще одно место правки сгенерированного кубического кода, что мне активно не нравится. То есть, конечно, именно этот исходник должен сохранить мои изменения при повторной генерации из кубиков, но перегенеришь что-то по-другому, так не забыть и здесь подправить...
Кто мешает тебе выдумать порох непромокаемый? (К. Прутков, мысль № 133)
Реклама
Аватара пользователя
AlanDrakes
Прорезались зубы
Сообщения: 236
Зарегистрирован: Пн июл 04, 2016 16:51:22
Откуда: Россия, Омск

Re: RTC: установка даты и времени через HAL

Сообщение AlanDrakes »

Писать программу между USER CODE BEGIN USART1_IRQn 0 USER CODE END USART1_IRQn 0 должен мешать здравый смысл, т.к. в этот момент ядро контроллера будет находиться в обработчике прерывания, а он должен быть максимально коротким и минимально взаимодействовать с общей памятью (ну, кроме чтения - читать можно, но можно получить случайные данные).

Может вам дать код на регистрах? Для себя писал на кольцевом буфере (на чтение и запись), работа с DMA для приёма и передачи. Дополнительных сложностей не добавлял, типа snprintf(что-нибудь) - просто console_put(нуль-терминированная-строка) и дополнительные сущности типа _hex_word, _hex_byte, _int...

В общем, если будет интересно, код ниже.
Спойлер

Код: Выделить всё

volatile uint8_t DMA_STATE;
#define DMA_tty0_TX_ACTIVE	0x01

uint8_t tty0_TX_BUF[1024];
volatile uint16_t tty0_TX_POS;
volatile uint16_t tty0_WR_POS;

void DMA2_Stream7_IRQHandler(void) {
	// tty0 (отладочная консоль)
	if (DMA2->HISR & DMA_HISR_TCIF7) {
		DMA2->HIFCR = DMA_HIFCR_CTCIF7;			// сброс флага события TCIF
		DMA_STATE &= ~DMA_tty0_TX_ACTIVE;
		tty0_ActivateDMA();
	} else if (DMA2->HISR & DMA_HISR_HTIF7) {
		DMA2->HIFCR = DMA_HIFCR_CHTIF7;			// сброс флага события HTIF
	} else if (DMA2->HISR & DMA_HISR_FEIF7) {
		DMA2->HIFCR = DMA_HIFCR_CFEIF7;			// сброс флага события FEIF
	} else if (DMA2->HISR & DMA_HISR_DMEIF7) {
		DMA2->HIFCR = DMA_HIFCR_CDMEIF7;		// сброс флага события DMEIF
	} else if (DMA2->HISR & DMA_HISR_TEIF7) {
		DMA2->HIFCR = DMA_HIFCR_CTEIF7;			// сброс флага события TEIF
	};
};
void USART1_IRQHandler(void) {
	uint8_t tmp;
	while (USART1->SR & USART_SR_RXNE) {
		tmp = USART1->DR;
		if (DMA_STATE & DMA_tty0_thread_ready) {
#ifdef USE_FREERTOS
			xQueueSendFromISR(Q_tty0, &tmp, 0);
#endif
		} else {
			microrl_insert_char(prl, tmp);
		};
	};
};

void tty0_ActivateDMA(void) {
	uint16_t CurrWrPos = tty0_WR_POS;
	uint16_t DataToSend;
	DataToSend = 0;
	if(!(DMA_STATE & DMA_tty0_TX_ACTIVE)) {
		DMA2_Stream7->CR &= ~DMA_SxCR_EN;	// Отключаем поток DMA
		if (tty0_TX_POS != CurrWrPos) {
			// Если не совпадает - значит, данные есть. Или малый шанс на переполнение буфера.
			DMA2_Stream7->M0AR = (uint32_t)&(tty0_TX_BUF[tty0_TX_POS]);
			if (tty0_TX_POS < CurrWrPos) {
				// Нет перехода через конец буфера
				DataToSend = (CurrWrPos - tty0_TX_POS);
				tty0_TX_POS = CurrWrPos;
			} else {
				// Нужно сделать кольцо.
				DataToSend = (sizeof(tty0_TX_BUF) - tty0_TX_POS);
				tty0_TX_POS = 0;
			};
			DMA2_Stream7->NDTR = DataToSend;
			DMA2_Stream7->FCR = 0;
			USART1->SR = ~(USART_SR_TC);
			// И только ПОСЛЕ этого включаем его. Да, странность. Но иначе он уходит в ошибку.
			DMA2_Stream7->CR |= DMA_SxCR_EN;
			DMA_STATE |= DMA_tty0_TX_ACTIVE;
		};
	};	// Если активен - сработает при вызове события завершения обмена.
};


// ------------------------------------- Аппаратно-зависимые функции - инициализпция -------------
void UpdateUSART_BR(void) {
	static uint8_t PrescalerTable[8] = {1,1,1,1,2,4,8,16};
	static uint16_t AHB_Presc[16] = {1,1,1,1,1,1,1,1,2,4,8,16,64,128,256,512};
	uint8_t presc;
	uint16_t APB_Presc;
	SystemCoreClockUpdate();
	APB_Presc = AHB_Presc[(RCC->CFGR >> 4) & 0x0F];
	presc = PrescalerTable[(RCC->CFGR >> 13) & 0x07];
	USART1->CR1 &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_UE);
	// Системная консоль / tty0 - 115200
	USART1->BRR = (SystemCoreClock / 115200 / presc / APB_Presc);	// Делим системную частоту на скорость порта, на делители APB и AHB, чтобы получить правильный делитель.
	USART1->CR1 |= (USART_CR1_TE | USART_CR1_RE | USART_CR1_UE);
};
void InitUSART(void) {
	GPIOB->MODER &= 0xFFFF0FFF;	// GPIOB[6-7].MODER = 00 - Input
	GPIOB->MODER |= 0x0000A000;	// GPIOB[6-7].MODER = 10 - Alternate
	GPIOB->AFR[0] &= 0xFFFFFFFF;	// GPIOB[6-7].Alternate = 0 (Sys)
	GPIOB->AFR[0] |= 0x77000000;	// GPIOB[6-7].Alternate = 7 (USART1)
	// Enable USART1 (tty0)
	RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
	// Reset USART1
	RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
	// UnReset USART1
	RCC->APB2RSTR &= ~RCC_APB2RSTR_USART1RST;
	USART1->CR2 = 0;
	USART1->CR3 = 0;
	// Сброс DMA
	RCC->AHB1RSTR |= RCC_AHB1RSTR_DMA2RST | RCC_AHB1RSTR_DMA1RST;
	RCC->AHB1RSTR &= ~(RCC_AHB1RSTR_DMA2RST | RCC_AHB1RSTR_DMA1RST);
	// Enable DMA1 & DMA2.
	RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN | RCC_AHB1ENR_DMA1EN;
	// Config USART1 to DMA Operations and clear flags.
	USART1->CR3 |= /*USART_CR3_DMAR |*/ USART_CR3_DMAT;
	USART1->SR &= ~(USART_SR_TC/* | USART_SR_RXNE*/);
	UpdateUSART_BR();
	// Указываем периферию
	DMA2_Stream7->PAR = (uint32_t)&(USART1->DR);
	// Указываем память
	DMA2_Stream7->M0AR = (uint32_t)&tty0_TX_BUF;
	// DMA1:Stream1:Channel4 (HW Console RX): MemInc + P->M + Transfer Complete INT.
// 	DMA1_Stream1->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_TCIE;
	// DMA1:Stream3:Channel4 (HW Console TX): MemInc + M->P + Transfer Complete INT.
	DMA2_Stream7->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE;
	NVIC_EnableIRQ(USART1_IRQn);
	NVIC_SetPriority(USART1_IRQn, 0x04);
	NVIC_SetPriority(DMA2_Stream7_IRQn, 0x03);
	NVIC_EnableIRQ(DMA2_Stream7_IRQn);
//	microrl_init (prl, console_put);			// Инициализпция консоли Microtl
//	microrl_set_complete_callback(prl, console_complete);	// Выставляем callback "Дополнить!"
//	microrl_set_execute_callback(prl, console_execute);	// Добавляем callback на выполнение.
	USART1->CR1 = (USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE | USART_CR1_UE);
};
// ------------------------------------------- "Высокоуровневые" функции.

char char_to_hex(char value) {
	if (value > 9)
		return '7' + value;
	return '0' + value;
};
uint8_t hchar_to_int(char value) {
	if ((value >= '0') && (value <= '9')) {
		return (value - '0');
	};
	if ((value >= 'A') && (value <= 'F')) {
		return (value - 'A' + 0x0A);
	};
	if ((value >= 'a') && (value <= 'f')) {
		return (value - 'a' + 0x0A);
	};
	return 0;
};
void console_put(const char *text) {
	while(*text) {
		// Пока не нуль-терминатор
		tty0_TX_BUF[tty0_WR_POS] = *text;		// Копируем данные в буфер
		text++;						// Сдвигаем указатель текста.
		tty0_WR_POS++;					// Сдвигаем указатель на 1 байт дальше.
		if (tty0_WR_POS >= sizeof(tty0_TX_BUF)) tty0_WR_POS = 0;	// Указатель должен быть в пределах допустимых значений.
	};
	// Здесь всё хорошо, записываем от старой позиции до новой.
	tty0_ActivateDMA();
};
void console_put_hex_dword(const uint32_t Data) {
	char txt[9];
	txt[0] = char_to_hex((Data >> 28) & 0x0F);
	txt[1] = char_to_hex((Data >> 24) & 0x0F);
	txt[2] = char_to_hex((Data >> 20) & 0x0F);
	txt[3] = char_to_hex((Data >> 16) & 0x0F);
	txt[4] = char_to_hex((Data >> 12) & 0x0F);
	txt[5] = char_to_hex((Data >> 8) & 0x0F);
	txt[6] = char_to_hex((Data >> 4) & 0x0F);
	txt[7] = char_to_hex((Data) & 0x0F);
	txt[8] = 0;
	console_put(txt);
};
void console_put_hex_word(const uint16_t Data) {
	char txt[5];
	txt[0] = char_to_hex((Data >> 12) & 0x0F);
	txt[1] = char_to_hex((Data >> 8) & 0x0F);
	txt[2] = char_to_hex((Data >> 4) & 0x0F);
	txt[3] = char_to_hex((Data) & 0x0F);
	txt[4] = 0;
	console_put(txt);
};
void console_put_hex_byte(const uint8_t Data) {
	char txt[3];
	txt[0] = char_to_hex((Data >> 4) & 0x0F);
	txt[1] = char_to_hex((Data) & 0x0F);
	txt[2] = 0;
	console_put(txt);
};
void console_put_int(const uint32_t value) {
	uint8_t txt[13];
	txt[12] = 0;
	console_put(utoa_builtin_div(value, (char *)txt));
};
Собственно, любой вывод текста отправляется в console_put(), который занимается рутиной, копируя строку из входа в кольцевой буфер. После этого пинается DMA контроллер, который перебрасывает данные в USART1. Если данные закончились - производится новая попытка запустить DMA, перепроверив указатели на буфер. Если совпадают - все данные были отправлены (или буфер был переполнен идеально полностью) и запускать не нужно. Если нужно - указатели соответствующим образом обновляются (указатель, который принадлежит DMA, а не функции!).

Получение байтов - посимвольное. Где-то в другом проекте принимал по DMA, но переписывать, чтобы заработало, немного лень.
Аватара пользователя
afz
Опытный кот
Сообщения: 744
Зарегистрирован: Сб дек 22, 2012 08:17:42
Откуда: Караганда, Казахстан

Re: RTC: установка даты и времени через HAL

Сообщение afz »

AlanDrakes, вообще-то, не так всё плохо. Точнее, многое зависит от кучи всяких факторов, но, если нет каких-то параллельных скоростных процессов, то, грамотно используя прерывания, все проблемы легко обходятся. Что действительно нельзя делать - это ждать чего-то в этом обработчике, а так - времени от одного до другого прерывания по TXE - 14.5 тыс тактов при работе на168 мГц и скорости USART'а 115200. И я с трудом представляю, чего туда такого можно насочинять, что бы потребовало больше 1000 тактов.

Естественно, все программы, работающие в прерываниях, должны быть быстрыми. То есть взяли что-то из регистров, поместили что-то в регистры, где-то отметились, и на выход. А ожидания будут в основном цикле, который while(1).

А без DMA не обойтись только тогда, когда нужно что-то скоростное и/или с минимально возможным джиттером.
Кто мешает тебе выдумать порох непромокаемый? (К. Прутков, мысль № 133)
Аватара пользователя
AlanDrakes
Прорезались зубы
Сообщения: 236
Зарегистрирован: Пн июл 04, 2016 16:51:22
Откуда: Россия, Омск

Re: RTC: установка даты и времени через HAL

Сообщение AlanDrakes »

[uquote="afz",url="/forum/viewtopic.php?p=4263858#p4263858"]А без DMA не обойтись только тогда, когда нужно что-то скоростное и/или с минимально возможным джиттером.[/uquote]

Либо наоборот, очень медленное, что может работать пока ядро спит =)
Ответить

Вернуться в «ARM»