Решил все таки ради интереса избавится от наследия HAL - вычистил проект от всего "лишнего". Та же програмка типа секундомер.
c HAL, там правда осталась только ХАЛовская настройка тактирования:
Program Size: Code=2236 RO-data=280 RW-data=36 ZI-data=1028
очищенная:
Program Size: Code=720 RO-data=236 RW-data=12 ZI-data=1028
Если оставить чистый main:
Program Size: Code=300 RO-data=208 RW-data=0 ZI-data=1024
Получается стартап и настройка тактирования (а она очень мощная) заняли всего то жалких 300 байт. Наша програмка занимает совсем немного - 420 байт, что вполне так на уровне - может даже и лучше будет результат чем у стареньких, неохота сравнивать. Правда ассемблерщики на стареньких могут побороться, но думаю оно того не стоит.
И так, такой способ динамической индикации (в прерывании таймера) имеет один существенный недостаток. Например нам надо дрыгать ногами с точными временными интервалами - известный датчик DS18B20 требует именно такого подхода. Выставили в "1" ногу порта и ожидаем пока не истечет нужное время, а тут бац и прерывание и все наши времена полетели в неизвестную даль. Конечно это все легко обходится, но требует лишних телодвижений. А тут разработчики микроконтроллеров на ядре АРМ подложили нам приятный сюрприз в виде DMA (есть еще в STM8L и вроде в XMEGAх).
Пришло время рассказать об одной потрясающей штуке под названием DMA(ПДП) – то есть прямой доступ к памяти (Direct Memory Access). Поясню что же это такое.
В двух словах – прямой доступ к памяти позволяет перемещать данные без(!) участия центрального процессора. То есть процессор работает себе преспокойненько, не отвлекается ни на что, а DMA в этот момент может пересылать огромные массивы данных, например, в USART. Думаю сразу понятно насколько это полезно, ведь процессору теперь не надо отвлекаться от основной полезной работы.
А в контроллерах STM32F10x даже не один, а два контроллера прямого доступа к памяти! И, соответственно, у каждого несколько каналов (у DMA1 – 7, а у DMA2 – 5).
Пробуем применить для динамической индикации. Один хороший товарищ HHIMERA уже прошел тяжелый джедайский путь в освоении DMA и любезно выложил код на известном форуме "Казус" на Казус
Воспользуемся его трудами - копипастим и чуть правим под нашу задачу:
#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 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
};
uint16_t BufferTIM[4*7]=
{
500, 500, 500, 500, 500, 500, 500,
500, 500, 500, 500, 500, 500, 500,
500, 500, 500, 500, 500, 500, 500,
500, 500, 500, 500, 500, 500, 500
};
void Bin2Bcd(uint16_t value) // HHIMERA(c) copypast
{
uint8_t *p = DigitBuffer;
while (value > 0)
{
*p++ = seg7[value % 10];
value /= 10;
}
}
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
//---------------------------
DMA1_Channel4->CPAR = (uint32_t)&TIM3->ARR; // DMA channel x peripheral address register
DMA1_Channel4->CMAR = (uint32_t)BufferTIM; // DMA channel x memory address register
DMA1_Channel4->CNDTR = 4*7; // DMA channel x number of data register
DMA1_Channel4->CCR |= DMA_CCR_MSIZE_0; // Memory size 16 bit
DMA1_Channel4->CCR |= DMA_CCR_PSIZE_0; // Peripheral size 16 bit
DMA1_Channel4->CCR |= DMA_CCR_PL; // Channel Priority level Very High
DMA1_Channel4->CCR |= DMA_CCR_MINC; // Memory increment mode
DMA1_Channel4->CCR |= DMA_CCR_CIRC; // Circular mode
DMA1_Channel4->CCR |= DMA_CCR_DIR; // Data transfer direction Memory -> Peripheral
DMA1_Channel4->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 Copy2Buffer()
{
uint16_t n;
DigitIndex = 0x01;
for (n = 0; n < 7; n++)
{
if(DigitBuffer[3] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & 0x00FF ;
else DataBuffer[n] = 0x00FF;
DigitIndex <<= 0x01;
}
DigitIndex = 0x01;
for (n = 7; n < 14; n++)
{
if(DigitBuffer[2] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & 0x017F ;
else DataBuffer[n] = 0x017F;
DigitIndex <<= 0x01;
}
DigitIndex = 0x01;
for (n = 14; n < 21; n++)
{
if(DigitBuffer[1] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & 0x027F ;
else DataBuffer[n] = 0x027F;
DigitIndex <<= 0x01;
}
DigitIndex = 0x01;
for (n = 21; n < 28; n++)
{
if(DigitBuffer[0] & DigitIndex) DataBuffer[n] = ~((uint16_t)DigitIndex) & 0x047F ;
else DataBuffer[n] = 0x047F;
DigitIndex <<= 0x01;
}
}
int main(void)
{
GPIOInit();
DMAInit();
TIM3Init();
Counter = 1234;
Bin2Bcd(Counter);
Copy2Buffer();
while (1)
{
}
}
Програмка должна отобразить на индикаторе "1234", что она успешно и делает - фото не выкладываю - поверьте на слово.