Назвался груздем - полезай в кузов.Продолжение.
Одной из самых полезных функций, включенных в состав микроконтроллеров STM32 стали часы реального времени (RTC). Данный модуль обладает всеми классическими возможностями подобных часов и может иметь отдельное автономное питание от батареи. Такой подход позволяет использовать область памяти часов для резервного хранения данных, а также реализовывать различные схемы энергосбережения.
Основу модуля часов реального времени составляет двоично-десятичный таймер/счетчик. Результат счета отображается в двух 32-разрядных регистрах. Первый из них RTC_TR содержит информацию о времени, второй – RTC_DR представляет собой календарь, включающий год, месяц, неделю и день. Все данные представлены в BCD формате, что позволяет их сразу же использовать для отображения на различных индикаторах. Модуль календаря поддерживает автоматическое определение високосных лет, а также количества дней в текущем месяце. Кроме этого доступны функции перевода на зимнее и летнее время, вызываемые программно.
Среди функций модуля RTC следует отметить два отдельных регистра событий (ALARM_x), с помощью которых можно реализовать будильники, таймеры и т.п. Формат регистров событий аналогичен счетным регистрам, что несколько упрощает их программирование. Еще одним событием, формируемым RTC, может стать сигнал выхода из спящего режима (WakeUP). Данный сигнал формируется периодически, что позволяет легко реализовывать различные системы, критичные к энергопотреблению. Счетчик выхода из спящего режима работает независимо от часов/календаря. Максимальное значение периода сна составляет примерно 48 суток.
Модуль часов расположен в отдельной области памяти, имеющей возможность внешнего питания от батареи. Регистры RTC оснащены дополнительной защитой от записи, что обеспечивает невозможность случайного повреждения информации в них. Кроме часовых регистров в модуле выполнены 12 регистров резервирования пользовательских данных с разрядностью 32. Эти регистры не обнуляются по сигналу «Сброс» при наличии внешнего источника напряжения, что позволяет хранить в них важную информацию.
Так как мы делаем часы - грех не воспользоваться еще одной замечательной возможностью микроконтроллеров STM32. Читаем документацию и AN3371 Application note Using the hardware real-time clock (RTC) in STM32 F0, F2, F3, F4 and L1 series of MCUs, смотрим примеры, любезно предоставленные производителем нашего микроконтроллера. Пробуем применить:
Спойлер
Код: Выделить всё
#include "stm32f0xx.h"
#define SegA (uint8_t)1<<0
#define SegB (uint8_t)1<<1
#define SegC (uint8_t)1<<2
#define SegD (uint8_t)1<<3
#define SegE (uint8_t)1<<4
#define SegF (uint8_t)1<<5
#define SegG (uint8_t)1<<6
#define SegH (uint8_t)1<<7
#define digit_0 (uint8_t)(SegA | SegB | SegC | SegD | SegE | SegF)
#define digit_1 (uint8_t)(SegB | SegC)
#define digit_2 (uint8_t)(SegA | SegB | SegG | SegE | SegD)
#define digit_3 (uint8_t)(SegA | SegB | SegG | SegC | SegD)
#define digit_4 (uint8_t)(SegF | SegG | SegB | SegC)
#define digit_5 (uint8_t)(SegA | SegF | SegG | SegC | SegD)
#define digit_6 (uint8_t)(SegA | SegF | SegE | SegD | SegC | SegG)
#define digit_7 (uint8_t)(SegA | SegB | SegC)
#define digit_8 (uint8_t)(SegA | SegB | SegC | SegD | SegE | SegF | SegG)
#define digit_9 (uint8_t)(SegA | SegB | SegC | SegD | SegF | SegG)
const uint8_t seg7[10] =
{
digit_0,
digit_1,
digit_2,
digit_3,
digit_4,
digit_5,
digit_6,
digit_7,
digit_8,
digit_9
};
#define SEG_GPIO_ODR GPIOB->ODR
#define SEGA GPIO_ODR_0
#define SEGB GPIO_ODR_1
#define SEGC GPIO_ODR_2
#define SEGD GPIO_ODR_3
#define SEGE GPIO_ODR_4
#define SEGF GPIO_ODR_5
#define SEGG GPIO_ODR_6
#define AN_GPIO_ODR GPIOB->ODR
#define AN1 GPIO_ODR_7
#define AN2 GPIO_ODR_8
#define AN3 GPIO_ODR_9
#define AN4 GPIO_ODR_10
#define DOT_GPIO_ODR GPIOB->ODR
#define CDOT GPIO_ODR_11
#define ADOT GPIO_ODR_12
__IO uint8_t SegmentIndex = 0x01;
__IO uint8_t DigitIndex = 0;
__IO uint32_t Tick = 0;
uint16_t Counter = 0;
uint8_t DigitBuffer[4];
uint16_t DotMask = 0x1800;
uint16_t DataBuffer[4*7]=
{
0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF,
0x017F, 0x017F, 0x017F, 0x017F, 0x017F, 0x017F, 0x017F,
0x027F, 0x027F, 0x027F, 0x027F, 0x027F, 0x027F, 0x027F,
0x047F, 0x047F, 0x047F, 0x047F, 0x047F, 0x047F, 0x047F
};
void GPIOInit(void)
{
/* GPIOB Periph clock enable */
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
GPIOB->MODER |= (GPIO_MODER_MODER0_0 | GPIO_MODER_MODER1_0 |
GPIO_MODER_MODER2_0 | GPIO_MODER_MODER3_0 |
GPIO_MODER_MODER4_0 | GPIO_MODER_MODER5_0 |
GPIO_MODER_MODER6_0 | GPIO_MODER_MODER7_0 |
GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0 | GPIO_MODER_MODER10_0 |
GPIO_MODER_MODER11_0 | GPIO_MODER_MODER12_0) ; /* Configure PB0-PB12 in output mode */
GPIOB->OTYPER &= ~( GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_1 |
GPIO_OTYPER_OT_2 | GPIO_OTYPER_OT_3 |
GPIO_OTYPER_OT_4 | GPIO_OTYPER_OT_5 |
GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7 |
GPIO_OTYPER_OT_8 | GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_10 |
GPIO_OTYPER_OT_11 | GPIO_OTYPER_OT_12) ; // Ensure push pull mode selected--default
GPIOB->OSPEEDR |= ( GPIO_OSPEEDER_OSPEEDR0 | GPIO_OSPEEDER_OSPEEDR1 |
GPIO_OSPEEDER_OSPEEDR2 | GPIO_OSPEEDER_OSPEEDR3 |
GPIO_OSPEEDER_OSPEEDR4 | GPIO_OSPEEDER_OSPEEDR5 |
GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7 |
GPIO_OSPEEDER_OSPEEDR8 | GPIO_OSPEEDER_OSPEEDR9 | GPIO_OSPEEDER_OSPEEDR10 |
GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12); //Ensure maximum speed setting (even though it is unnecessary)
GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR0 | GPIO_PUPDR_PUPDR1 |
GPIO_PUPDR_PUPDR2 | GPIO_PUPDR_PUPDR3 |
GPIO_PUPDR_PUPDR4 | GPIO_PUPDR_PUPDR5 |
GPIO_PUPDR_PUPDR6 | GPIO_PUPDR_PUPDR7 |
GPIO_PUPDR_PUPDR8 | GPIO_PUPDR_PUPDR9 | GPIO_PUPDR_PUPDR10 |
GPIO_PUPDR_PUPDR11 | GPIO_PUPDR_PUPDR12); //Ensure all pull up pull down resistors are disabled
DOT_GPIO_ODR ^= ADOT; // Set pin ADOT
}
void DMAInit(void)
{
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
DMA1_Channel3->CPAR = (uint32_t)&GPIOB->ODR; // DMA channel x peripheral address register
DMA1_Channel3->CMAR = (uint32_t)DataBuffer; // DMA channel x memory address register
DMA1_Channel3->CNDTR = 4*7; // DMA channel x number of data register
DMA1_Channel3->CCR |= DMA_CCR_MSIZE_0; // Memory size 16 bit
DMA1_Channel3->CCR |= DMA_CCR_PSIZE_0; // Peripheral size 16 bit
DMA1_Channel3->CCR |= DMA_CCR_PL_1; // Channel Priority level High
DMA1_Channel3->CCR |= DMA_CCR_MINC; // Memory increment mode
DMA1_Channel3->CCR |= DMA_CCR_CIRC; // Circular mode
DMA1_Channel3->CCR |= DMA_CCR_DIR; // Data transfer direction Memory -> Peripheral
DMA1_Channel3->CCR |= DMA_CCR_EN; // Channel enable
}
void TIM3Init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // TIM3 clock enable
TIM3->PSC = 48 - 1;
TIM3->ARR = 500 - 1;
TIM3->CCR1 = 25;
TIM3->DIER |= TIM_DIER_UDE; // Upload DMA Enable
TIM3->DIER |= TIM_DIER_CC1DE;
TIM3->CR1 |= TIM_CR1_CEN | TIM_CR1_ARPE; // Counter Enable
}
void Configure_RTC(void)
{
/* Enable the peripheral clock RTC */
/* (1) Enable the LSI */
/* (2) Wait while it is not ready */
/* (3) Enable PWR clock */
/* (4) Enable write in RTC domain control register */
/* (5) LSI for RTC clock */
/* (6) Disable PWR clock */
RCC->CSR |= RCC_CSR_LSION; /* (1) */
while((RCC->CSR & RCC_CSR_LSIRDY)!=RCC_CSR_LSIRDY) /* (2) */
{
/* add time out here for a robust application */
}
RCC->APB1ENR |= RCC_APB1ENR_PWREN; /* (3) */
PWR->CR |= PWR_CR_DBP; /* (4) */
RCC->BDCR = (RCC->BDCR & ~RCC_BDCR_RTCSEL) | RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_1; /* (5) */
RCC->APB1ENR &=~ RCC_APB1ENR_PWREN; /* (7) */
}
void Init_RTC(uint32_t Time)
{
/* RTC init mode */
/* Configure RTC */
/* (1) Write access for RTC registers */
/* (2) Enable init phase */
/* (3) Wait until it is allow to modify RTC register values */
/* (4) set prescaler, 40kHz/128 => 312 Hz, 312Hz/312 => 1Hz */
/* (5) New time in TR, 24-hour format */
/* (6) Disable init phase */
/* (7) Disable write access for RTC registers */
RTC->WPR = 0xCA; /* (1) */
RTC->WPR = 0x53; /* (1) */
RTC->ISR |= RTC_ISR_INIT; /* (2) */
while((RTC->ISR & RTC_ISR_INITF)!=RTC_ISR_INITF) /* (3) */
{
/* add time out here for a robust application */
}
RTC->PRER = 0x007F0137; /* (4) */
RTC->TR = RTC_TR_PM | Time; /* (5) */
RTC->ISR &=~ RTC_ISR_INIT; /* (6) */
RTC->WPR = 0xFE; /* (7) */
RTC->WPR = 0x64; /* (7) */
}
void Copy2Buffer()
{
uint16_t n, k = 0x00FF, c = 3;
DigitIndex = 0x01;
for (n = 0; n < 28; n++)
{
if(n==7)
{
k = 0x017F;
c = 2;
DigitIndex = 0x01;
}
if(n==14)
{
k = 0x027F;
c = 1;
DigitIndex = 0x01;
}
if(n==21)
{
k = 0x047F;
c = 0;
DigitIndex = 0x01;
}
if(DigitBuffer[c] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & k ;
else DataBuffer[n] = k;
if(n==0) DataBuffer[n] ^= DotMask;
DigitIndex <<= 0x01;
}
}
int main(void)
{
volatile uint32_t TimeToCompute = 0;
volatile uint32_t DateToCompute = 0;
uint8_t Su, LastSu = 0;
GPIOInit();
DMAInit();
TIM3Init();
Configure_RTC();
Init_RTC(0);
while (1)
{
while(LastSu == Su)
{
TimeToCompute = RTC->TR; /* get time */
DateToCompute = RTC->DR; /* need to read date also */
Su = (uint8_t)(TimeToCompute & RTC_TR_SU);
}
LastSu = Su;
DigitBuffer[1] = seg7[(uint8_t)((TimeToCompute & RTC_TR_ST)>>4)];
DigitBuffer[0] = seg7[(uint8_t)(TimeToCompute & RTC_TR_SU)];
DigitBuffer[3] = seg7[(uint8_t)((TimeToCompute & RTC_TR_MNT)>>12)];
DigitBuffer[2] = seg7[(uint8_t)((TimeToCompute & RTC_TR_MNU)>>8)];
DotMask ^= 0x0800;
Copy2Buffer();
}
}
Убрал ненужный теперь bin2bcd и регулировку яркости, чуть оптимизировал copy2buffer. На индикатор выводятся минуты и секунды, но можно вывести и часы и день недели, месяц, год. Точечки мигают - 1 с светятся, 1 с нет. Обновление на индикаторе раз в секунду.
Размер программы: Code=916 RO-data=236 RW-data=64 ZI-data=1024.
Легко влезет с большим запасом в в классику TINY2313, но почему-то производитель не заложил в этом микроконтроллере ни DMA ни RTC. Что это жадность или наплевательское отношение к пользователям? Разве трудно было сделать какую нибудь типа TINY2313D с вышеперечисленными модулями, ведь сделали же picoPower version: ATtiny2313A.
Поставленная задача считаю выполнена - часы работают, добавить кнопки для установки времени , будильников и прочего - рутинная работа, совсем не трудная.
В общем оказалось, ничего уж такого сложного в этих армах как бы и нет - постепенно, не напрягаясь, можно освоить работу с портами, таймерами, пдп, ртц и другими модулями этих замечательных микроконтроллеров. Непонятно зачем тратить время на изучение пиков, пусть даже «улучшенной» серии чтобы написать на ассемблере часы, постоянно упираясь в ограничения старых технологий. Напоминает мазохизм, типа "стоя, в противогазе и в гамаке". Как это может помочь в последующем изучении более продвинутых микроконтроллеров?
И начинающим непонятно зачем учится на устаревших микроконтроллерах, если все равно необходимо знание английского и знание языка программирования Си. А чтобы учится, производители микроконтроллеров на ядре ARM (в частности ST) сделали все возможное.
А если бы в микроконтроллерах ядро ARM было изначально? Т.е. не Intel бы начала? Мы б сейчас спорили как тяжелы эти 51 микроконтроллеры, ох начинающим их никак не освоить - лучше на армах начинать.
Кто покажет тот порог, который отличает изучение пик, авр от изучения стм32. Мне кажется стм32 все таки легче изучать и главное - намного перспективнее.
Вот сегодня на форуме начинающий спрашивал о неработающей программе на меге128, а вот о фузе (M103C) он не знал, хотя об этом написано 4 странице документации и в интернете есть подобные вопросы с ответами. О том, что на фига этот фуз нужен уж молчу - ну выпускайте и мегу103 и мегу128. Да еще жытаг по умолчанию включен - порт не работает, тоже частый вопрос. Что это? Легкость изучения авр, когда не надо читать документацию, и полно готовых говнопримеров (зачастую не работающих) или это элементарная лень? Предвзятости меня к авр нет - меня устраивает для многих задач любимая мега48, только чуть подправить ее внутренности бы - добавить PLL (в тини некоторых почему то есть), добавить deadtime в в таймере (тоже в тини некоторых есть), таймеров бы 16 разрядных парочку (без RTC обойдусь), добавить ОУ (в некоторых мегах он есть) убрать параллельное программирование с фузами (лучше из программы фузы настраивать), добавить будлоадер, не занимающий основную флэш и программировать по компорту, или по USB (тоже в некоторых есть) - получается практически STM8S003, а он уже есть и дешевле меги48.