Показать сообщение отдельно
Непрочитано 19.12.2013, 21:23  
nahimovv
Заблокирован
 
Регистрация: 25.04.2013
Сообщений: 1,431
Сказал спасибо: 0
Сказали Спасибо 385 раз(а) в 254 сообщении(ях)
nahimovv на пути к лучшему
По умолчанию TIM + DMA

Изюминкой STM32, ИМХО, является возможность связывать таймера между собой и широкие возможности DMA. Здесь возможности практически безграничны, что позволяет делать многие вещи по сути хардварными.
Процесс этот, порой, мозговыносящий и без хороших знаний работы модулей МК, логического анализатора и других средств отладки не сулит ничего хорошего. Однако результаты того стоят.

Самый простой пример - подключение 4-х разрядного LED-семисегментника с общим анодом (максимум до 8 разрядов). Сегменты подключены к PA0-PA7 через резисторы 200 Ом, аноды - к PA8-PA11 непосредственно, без ключей.
Всё это подключалось к STM32F0Discovery, МК STM32F051. Особенностью такого подключения является разнояркость разрядов, для компенсации которой был тоже задействован DMA. Итого задействовано - 1 таймер и 2 канала DMA (при подключении ключей в цепях анодов достаточно 1 таймера и 1 канала DMA). Результат - нет прерываний, обновление информации и коррекция разнояркости по сути хардварное.

Как это работает:

В общем случае - таймер периодически пинает DMA, DMA загружает данные из буфера в порт, в данном случае это GPIOA. Т.к. сегменты и разряды подключены к одному порту и их коммутация происходит одновременно, то никакого гашения/включения разрядов на время смены информации на сегментах не требуется, подсветка нерабочих сегментов отсутствует.

В данном случае, с коррекцией разнояркости - сначала таймер пинает один канал DMA для загрузки TIM_ARR, чем и определяется яркость разряда, потом пинает другой канал DMA, который и загружает порт данными о текущем разряде и состоянии сегментов.


Примерный код:

Здесь всё стандартно и ничего интересного.
Код:
#define LOW_BYTE(var)    (*(__IO uint8_t *)((uint32_t)&(var)))
   
#define DIGITS          0x04    // количество разрядов LED
#define LED_PORT        GPIOA

#define DIGIT_1     GPIO_ODR_11
#define DIGIT_2     GPIO_ODR_8
#define DIGIT_3     GPIO_ODR_9
#define DIGIT_4     GPIO_ODR_10

#define DIGIT_5     GPIO_ODR_12
#define DIGIT_6     GPIO_ODR_13
#define DIGIT_7     GPIO_ODR_14
#define DIGIT_8     GPIO_ODR_15

#define SEG_A       GPIO_ODR_0
#define SEG_B       GPIO_ODR_2
#define SEG_C       GPIO_ODR_6
#define SEG_D       GPIO_ODR_4
#define SEG_E       GPIO_ODR_3
#define SEG_F       GPIO_ODR_1
#define SEG_G       GPIO_ODR_7
#define SEG_H       GPIO_ODR_5

const uint8_t ten2led[] =
{
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,          // "0"
SEG_B | SEG_C,                                          // "1"
SEG_A | SEG_B | SEG_G | SEG_E | SEG_D,                  // "2"
SEG_A | SEG_B | SEG_G | SEG_C | SEG_D,                  // "3"
SEG_F | SEG_G | SEG_B | SEG_C,                          // "4"
SEG_A | SEG_F | SEG_G | SEG_D | SEG_C,                  // "5"
SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,          // "6"
SEG_A | SEG_B | SEG_C,                                  // "7"
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,  // "8"
SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G,          // "9"

SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G,          // "A"
SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,                  // "b"
SEG_A | SEG_D | SEG_E | SEG_F,                          // "C"
SEG_B | SEG_C | SEG_D | SEG_E | SEG_G,                  // "d"
SEG_A | SEG_D | SEG_E | SEG_F | SEG_G,                  // "E"
SEG_A | SEG_E | SEG_F | SEG_G,                          // "F"
0x00                                                    // " "

};

uint32_t tmp  = 0; 
uint32_t ttmp = 0;

uint8_t Ress[11];
Здесь содержится информация о разрядах и сегментах, которые DMA загружает в порт. Старшие байты DIGIT_Х содержат информацию о включённом разряде (в данном случае "1" соответствует включённому разряду) и при выполнении программы меняться не должны!
Младшие байты содержат информацию о включённых сегментах. Изначально содержат 0xFF, т.е. все сегменты выключены.
Код:
uint16_t Data_Buffer[DIGITS]= 
{
DIGIT_1 | 0xFF, DIGIT_2 | 0xFF, DIGIT_3 | 0xFF, DIGIT_4 | 0xFF
    
};
Здесь содержатся значения яркости для каждого разряда которые DMA заносит в таймер. Изначально содержатся базовые значения яркости.
Код:
uint16_t TIM_Buffer[DIGITS]= 
{
1000, 1000, 1000, 1000
    
};
Здесь содержатся значения яркости для различных высвечиваемых символов. 1000 - наибольшая яркость, 770 - наименьшая.
В данном случае значений всего три, что вполне достаточно. В других случаях можно задать для каждого символа свои значения.
Код:
const uint16_t TIM_ARR_val[] =
{
1000,   // "0"
770,    // "1"
920,    // "2"
920,    // "3"
920,    // "4"
920,    // "5"
1000,   // "6"
770,    // "7"
1000,   // "8"
1000,   // "9"

1000,   // "A"
920,    // "b"
920,    // "C"
920,    // "d"
920,    // "E"
920,    // "F"
770,    // " "

};
Настройки DMA, как по учебнику, ничего особенного.
Код:
void Init_DMA(void)
{    
RCC-›AHBENR |= RCC_AHBENR_DMA1EN;
    
DMA1_Channel3-›CPAR = (uint32_t)&GPIOA-›ODR;     // DMA channel x peripheral address register
DMA1_Channel3-›CMAR = (uint32_t)Data_Buffer;     // DMA channel x memory address register    
DMA1_Channel3-›CNDTR = DIGITS;                   // 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)TIM_Buffer;      // DMA channel x memory address register    
DMA1_Channel4-›CNDTR = DIGITS;                   // 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

}
Настройка таймера.
Настройка предделителя: (SystemCoreClock/(DIGITS * 50 * 1000)) - 1, где 50 значение в герцах на один разряд, 1000 - базовая константа яркости.
TIM3-›ARR = 1000 - 1, где 1000 - базовая константа яркости на время настройки таймера. При работе изменяется через DMA.
TIM3-›CCR1 = 25, где 25 - абстрактная константа, определяет промежуток времени между загрузкой значения яркости для разряда и последующим обновлением GPIOA.
Код:
void Init_TIM3(void)
{
RCC-›APB1ENR |= RCC_APB1ENR_TIM3EN;   // TIM3 clock enable

TIM3-›PSC  = (SystemCoreClock/(DIGITS * 50 * 1000)) - 1;  //240 
TIM3-›ARR  = 1000 - 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

}
Bin2BCD, в коментах не нуждается.
Код:
uint32_t Bin2BCD(uint32_t value, uint8_t *Res)
{
uint8_t *n = Res;

while (value › 0)
 {
 *Res++ = value % 10;
 value /= 10;
 }

return (uint32_t)(Res - n);
 
}
Примерный майн - инкрементальный счётчик с инкрементом значения через ~100 миллисекунд.
Код:
int main(void)
{
 
Init_GPIO();	

Init_DMA();    
Init_TIM3();    

while(1)
 {
   Bin2BCD(tmp, Ress);
   ttmp = DIGITS;
     
   while(ttmp--)
    {        
     TIM_Buffer[ttmp] = TIM_ARR_val[Ress[ttmp]];           // Загружается значение яркости в соответствии с высвечиваемым  
                                                           // символом для соответствующего разряда
     LOW_BYTE(Data_Buffer[ttmp]) = ~ten2led[Ress[ttmp]];   // Загружаются данные по сегментам для соответствующего разряда
        
    }

   TIM_Buffer[1] += 100;                                   // Для децимальной точки увеличиваем яркость    
   LOW_BYTE(Data_Buffer[1]) -= SEG_H;                      // Засвечиваем точку в соответствующем разряде
    
   tmp++;
                          
   Delay_mS(100);  // delay 100ms
    
 }    

while(1); 
 
}

Последний раз редактировалось nahimovv; 20.12.2013 в 23:44.
nahimovv вне форума  
Эти 11 пользователя(ей) сказали Спасибо nahimovv за это сообщение:
-jonns- (21.03.2017), -shiva- (28.12.2013), dadigor (05.09.2016), dosikus (19.12.2013), kot-69 (20.12.2013), krug_vv (21.12.2013), MaxiMuz79 (20.12.2013), neyvert (28.12.2013), Nick19 (26.04.2015), sun4in (21.12.2013), ut1wpr (20.12.2013)