Use To Advantage

Используй с пользой!

  • Увеличить размер шрифта
  • Размер шрифта по умолчанию
  • Уменьшить размер шрифта
Home

Разработка таймера на STM32 Часть 2 - логика работы + видео

E-mail Печать
1.9/5 (1601 голоса)

В предыдущей статье мы разобрали детали из который будет собираться таймер. У устройства есть две кнопки, которые используются для
управления режимами: одна для старта/стопа, вторая для выбора режимов. Цифровой индикатор на три цифры, трехцветный светодиод.

 


Логика работы таймера исходит из пользовательских функций.

Функции будут следующими:
1. Таймер. 
2. Секундомер. 
3. Термометр
4. Эмулятор броска двух кубиков.
5. Режим релаксации.

Расмотрим алгоритмы по функциям.
Таймер. При включении начинает обратный отсчет с некоторого начального значения. По умолчанию начальное значение 30 секунд. Но его можно настроить. После окончания отсчета звучит писк. При включении режима таймера, на табло отображается начальное значение. Отсчет начинается по кнопке Старт. Для перехода в режим настройки таймера нужно нажать кнопку Старт и не отпуская ее нажать кнопку Выбор. При этом начинает мигать настраиваемая цифра. Переход по цифрам при помощи кнопки Выбор. Увеличение значения при помощи кнопки Старт.
Секундомер. Просто считает секунды и отображает на табло. Поскольку табло у нас трехзначное, то значит есть возможность посчитать до 999 секунд.  или 16 минут и 39 секунд. При входе в режим, отображаются нули, по кнопке Старт начинается отсчет. При повторном нажатии отсчет прекращается. При следующем нажатии начинается отсчет опять с нулевых значений. Для упрощение первая цифра всегда будет обозначать минуту, а две последние секунды, т.е. секундомер может работать до 9 мин 59 секунд.
Температура
Отображается текущая температура, которая считана с датчика.  Никаких настроек. Изменение температуры отображаются раз 1/10 секунды.
Кубик
При нажатии на старт случайным образом происходит выброс цифр от 1 до 6 на двух крайних цифрах.
Релаксация.

По очереди цветные светодиоды медленно зажигаются и медленно гаснут.

При нажатии кнопки Выбор происходит смена режима по кругу. Таймер->Секундомер->Температура->Кубики->Релаксация->Таймер

На видео можно посмотреть как это работает реально.




Поскольку памяти в контроллере не слишком много, в STM32F030F4P6  всего 16 килобайт, то используется просто С, не С++ и при этом весь описанный алгоритм уложился в 10 килобайт.

Исходя из таблицы, которая взята  из даташита STM32F030F4P6 можно понять какие основные и альтернативные функции можно задействовать на каждой ноге контроллера

 

Pin number

Pin name

(function after reset)

Pin type

IO Structure

Notes

 Pin functions

TSSOP20

Alternate functions

Additional functions

2

PF0-OSC_IN (PF0)

I/O

FT

 

I2C1_SDA(5)

OSC_IN

3

PF1-OSC_OUT

(PF1)

I/O

FT

 

I2C1_SCL(5)

OSC_OUT

4

NRST

I/O

RST

 

Device reset input / internal reset output

(active low)

5

VDDA

S

   

Analog power supply

6

PA0

I/O

TTa

 

USART1_CTS(2),

USART2_CTS(3)(5),

USART4_TX(5)

ADC_IN0,

RTC_TAMP2,

WKUP1

7

PA1

I/O

TTa

 

USART1_RTS(2),

USART2_RTS(3)(5),

EVENTOUT,

USART4_RX(5)

ADC_IN1


8

PA2

I/O

TTa

 

USART1_TX(2),

USART2_TX(3)(5),

TIM15_CH1(3)(5)

ADC_IN2

9

PA3

I/O

TTa

 

USART1_RX(2),

USART2_RX(3)(5),

TIM15_CH2(3)(5)

ADC_IN3

10

PA4

I/O

TTa

 

SPI1_NSS,

USART1_CK(2)

USART2_CK(3)(5),

TIM14_CH1,

USART6_TX(5)

ADC_IN4

11

PA5

I/O

TTa

 

SPI1_SCK, USART6_RX(5)

ADC_IN5

12

PA6

I/O

TTa

 

SPI1_MISO,

TIM3_CH1,

TIM1_BKIN,

TIM16_CH1,

EVENTOUT

USART3_CTS(5)

ADC_IN6

13

PA7

I/O

TTa

 

SPI1_MOSI,

TIM3_CH2,

TIM14_CH1,

TIM1_CH1N,

TIM17_CH1,

EVENTOUT

ADC_IN7

14

PB1

I/O

TTa

 

TIM3_CH4,

TIM14_CH1,

TIM1_CH3N,

USART3_RTS(5)

ADC_IN9

16

VDD

S

   

Digital power supply

17

PA9

I/O

FT

 

USART1_TX,

TIM1_CH2,

TIM15_BKIN(3)(5)

I2C1_SCL(2)(5)

-

18

PA10

I/O

FT

 

USART1_RX,

TIM1_CH3,

TIM17_BKIN

I2C1_SDA(2)(5)

-

19

PA13 (SWDIO)

I/O

FT

(7)

IR_OUT, SWDIO

-

20

PA14 (SWCLK)

I/O

FT

(7)

USART1_TX(2),

USART2_TX(3)(5),

SWCLK

-

1

BOOT0

I

B

 

Boot memory selection

15

VSS

S

   

Ground

16

VDD

S

   

Digital power supply

1.     PC13, PC14 and PC15 are supplied through the power switch. Since the switch only sinks a limited amount of current  (3 mA), the use of GPIOs PC13 to PC15 in output mode is limited: 

-  The speed should not exceed 2 MHz with a maximum load of       30 pF.

-  These GPIOs must not be used as current sources (e.g. to drive an LED).

2.     This feature is available on STM32F030x6 and STM32F030x4 devices only.

3.     This feature is available on STM32F030x8 devices only.

4.     For STM32F030x4/6/8 devices only.

5.     For STM32F030xC devices only.

6.     On LQFP32 package, PB2 and PB8 should be treated as unconnected pins (even when they are not available on the package, they are not forced to a defined level by hardware).

7.     After reset, these pins are configured as SWDIO and SWCLK alternate functions, and the internal pull-up on SWDIO pin and internal pull-down on SWCLK pin are activated.

Нам необходимо использовать два GPIO для кнопок, интерфейс SPI, есть свободный SPI1 и интерфейс I2C1, на PA6 будем использовать ШИМ, для этого задействуется TIMER3


Получилось следующее

  1. boot0
  2. PF0    OSC_IN
  3. PF1    OSC_OUT   
  4. NRST  - резет  
  5. VDDA  -    3.3V
  1. PA0 - 
  2. PA1 -       кнопка 1
  3. PA2 -       кнопка 2
  4. PA3 -  
  1. PA4    spi nss  LE
  2. PA5   - SPI1_SCK    клок
  3. PA6   - SPI1_MISO  не нужен, шим на светодиоды
  1. PA7  -  SPI1_MOSI выход данных
  2. PB1   -  динамик
  1. VSS   -  3.3 v
  2. VDD   - земля
  1. PA9   -  I2C1_SCL(2)(5)       -- для опроса температуры
  2. PA10 -  I2C1_SDA(2)(5)      -- для опроса температуры
  3. SWDIO_PA13    - отладчик
  4. SWCLK_pa14     - отладчик



Нам нужно проводить отображение цифр, логично нагрузить этой задачей один из таймеров, например TIM14, который будет генерировать прерывание и в нем будем менять цифры, если что-то изменилось с последнего момента

Программу условно можно разделить на пользовательскую логику и аппаратный интерфейс. Подробно будем рассматривать воможности аппаратного интерфейса.

Задействованы следующие аппаратные возможности:
1. Подключение внешнего кварца на 8 MГц
2. GPIO -  PA1, PA2   - считывание значения кнопок в разных режимах.
3. Отображение значений на трехцифровом дисплее и режимов на трехцветном светодиоде при помощи интерфейса SPI
4. Получение данных от датчика температуры по интерфейсу I2C
5. Использование таймера для выполнения работы по прерываниям (обновление дисплея)
6. Использование таймера для генерации ШИМ
7. Использование  GPIO для генерации простейшего звука (без использования АЦП)


Инициализация кварца

// запуск внешнего кварца
    RCC->CR|=RCC_CR_HSEON; //Запускаем генератор HSE.
    while (!(RCC->CR & RCC_CR_HSERDY)) {}; // Ждем готовности
    RCC->CFGR &=~RCC_CFGR_SW; //Сбрасываем биты
    RCC->CFGR |= RCC_CFGR_SW_HSE; //Переходим на HSE 8Mгц

Отображение будет происходить при помощи драйвера светодиодов, который представляет собой обычный сдвиговый регистр. Соединение по интерфейсу SPI
Инициализация

//Включаем тактирование шины А
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
// инициализация периферии
GPIO_InitTypeDef GPIO_InitStructure;
// a4 для защелки
              GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4    ;
              GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;
              GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_1;
              GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
              GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //ножка пуш-пул
              GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  SPI_InitTypeDef SPI_InitStructure; // Тактирование модуля SPI1 и порта А
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // Настраиваем ноги SPI1 для работы в режиме альтернативной функции
 GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_0);
 GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_0); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 //GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_5;
 GPIO_Init(GPIOA, &GPIO_InitStructure); //Заполняем структуру с параметрами SPI модуля SPI_Direction_1Line_Tx;
 SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex; //полный дуплекс
 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // передаем по 8 бит
 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;// SPI_CPOL_Low; // Полярность и
 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // фаза тактового сигнала
 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // Управлять состоянием сигнала NSS программно
 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // Предделитель SCK
 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // Первым отправляется старший бит
 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // Режим - мастер
 SPI_Init(SPI1, &SPI_InitStructure); //Настраиваем SPI1
 SPI_Cmd(SPI1, ENABLE); // Включаем модуль SPI1.... // Поскольку сигнал NSS контролируется программно, установим его в единицу
 // Если сбросить его в ноль, то наш SPI модуль подумает, что
 // у нас мультимастерная топология и его лишили полномочий мастера.
 SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);
..../ замедлитель по частоте
void delay_ms(uint32_t ms)
{
  volatile uint32_t nCount; //переменная для счета
  RCC_ClocksTypeDef RCC_Clocks; //переменная для считывания текущей частоты
  RCC_GetClocksFreq (&RCC_Clocks); //считываем текущую тактовую частоту
  nCount=(RCC_Clocks.HCLK_Frequency/10000)*ms; //пересчитываем мс в циклы
  for (; nCount!=0; nCount--); //гоняем пустые циклы
}
....// отправка данных по SPI
void DataSend(uint16_t data)
{
  GPIO_SetBits(GPIOA, GPIO_Pin_4); // защелка пока закрыта для приема данных
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); // проверить на готовность
  GPIO_ResetBits(GPIOA, GPIO_Pin_4); // сбросить защелку, дальше должны идти данные
  delay_ms(1);
  SPI_SendData8(SPI1, data);// отправка по 8 бит, для полной засветки нужно отправлять 4 раза
 while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET) //Передатчик занят?
  ; // значит ничего не делаем
 delay_ms(1);
 GPIO_SetBits(GPIOA, GPIO_Pin_4); //защелка установлена, передача закончена}


Здесь интересно, у нас есть 4 байта, которые мы передаем в сдвиговый регистр, для того чтобы засветить три цифры и многоцветрый светодиод. Передача идет по одному байту (8 бит).
Готовим байты для передачи, каждой цифре на индикаторе соответствуют определенные светодиоды

#define DIG0 0b00111111
#define DIG1 0b00000110
#define DIG2 0b01011011
#define DIG3 0b01001111
#define DIG4 0b01100110
#define DIG5 0b01101101
#define DIG6 0b01111100
#define DIG7 0b00000111
#define DIG8 0b01111111
#define DIG9 0b01100111
#define POINT 0b10000000
#define LED_RED 0b00000100
#define LED_BLUE 0b00000001
#define LED_GREEN 0b00000010
#define MINUS 0b01000000
#define ZERO 0b00000000uint8_t time[3]; // буфер для отображение цифр
uint8_t point[3]; // буфер для отображения точек
uint8_t point_buff[3]; // предыдущее отображение точек
uint8_t time_buff[3]; // предыдущее отображение цифр
uint8_t mode_buff=0; // предыдущей режим// массив для упрощения вывода
uint8_t digit[]={DIG0,DIG1,DIG2,DIG3,DIG4,DIG5,DIG6,DIG7,DIG8,DIG9,MINUS,ZERO};/*
 * засветить светодиод в зависимости от режима
 */void LedShow(){
  switch (mode) {
  case MODE_TIMER:
  DataSend(LED_BLUE);
  break;
 case MODE_STOPWATCH:
 DataSend(LED_RED);
 break;
 case MODE_TEMP:
 DataSend(0); // выключить
 break;
 case MODE_DICE: // кубики
 DataSend(LED_GREEN);
 break;
 /* case MODE_RELAX:
 break;
 case MODE_BRIGHT:
 break;
 */
 default:
 DataSend(0);
  }}
/*
 * очистка экрана, но светодиоды оставляем
 *
 *
 */
void Clear(void)
{
  TIM3->CCR1=1050;
  LedShow(); // засветить светодиод
  DataSend(0);
  DataSend(0);
  DataSend(0);
  TIM3->CCR1=setbrightness;
}/*
 * проверка на изменение буфера, если изменился, то нужно обновить
 * если не изменился, то не обновлять чтобы небыло мелькания
 *
 *
 */
uint8_t IsChanged(){
  if (mode!=mode_buff){
  return 1;
  }
  uint8_t i=0;
  for (i=0;i<3;i++){
  if (time[i]!=time_buff[i]){
  return 1;
  }
  if (point[i]!=point_buff[i]){
  return 1;
  }
  }
  return 0;}/*
 * обновление отображения
 */
void Refresh(void)
{
  TIM3->CCR1=1050;
  //delay_ms(10);
 LedShow(); // послали данные светодиода
 uint8_t i=0;
 for (i=0;i<3;i++){
 DataSend(digit[time[i]]^point[i]);
 time_buff[i]=time[i];
 point_buff[i]=point[i];
 }
 /*
  DataSend(digit[time[1]]^point[1]);
  DataSend(digit[time[2]]^point[2]);
time_buff[0]=time[0];
  time_buff[1]=time[1];
  time_buff[2]=time[2];
*/
  mode_buff=mode;
  TIM3->CCR1=setbrightness;
}
/*
 * Выключает одну указанную цифру для мелькания при настройке
 */void Blink(uint8_t Num)
{
  TIM3->CCR1=1050;
  LedShow();  if (Num == 0){
  DataSend(0);
  DataSend(digit[time[1]]);
  DataSend(digit[time[2]]);
  }
  if (Num == 1){
  DataSend(digit[time[0]]);
  DataSend(0);
  DataSend(digit[time[2]]);
  }
  if (Num == 2){
  DataSend(digit[time[0]]);
  DataSend(digit[time[1]]);
  DataSend(0);
  }
  TIM3->CCR1=setbrightness;
}






Отображение данных будет происходить при помощи в прерывании от таймера TIM14

//Таймер 14 у нас висит на шине APB1, включить тактирование
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);
// 14 таймер для обновления экрана
TIM_SETUP.TIM_CounterMode =
TIM_CounterMode_Up;  //считаем вверх
TIM_SETUP.TIM_Period = 100;     //период таймера 1/10 секунды
TIM_SETUP.TIM_Prescaler =8000 ; //TIMER_PRESCALER;   //предделитель 
TIM_TimeBaseInit(TIM14,&amp;TIM_SETUP);//Разрешаем соответствующее прерывание
TIM_ITConfig(TIM14, TIM_IT_Update,ENABLE);
TIM_Cmd(TIM14, ENABLE);
NVIC_EnableIRQ(TIM14_IRQn);    __enable_irq();   // разрешены прерывания вообще

Само прерывание от таймера

 void TIM14_IRQHandler()
{
// отображение проверка на изменение и отображение
   uint8_t _0,_1,_2;
    uint16_t all;
    rand();   // сбить генератор псевдослучайных чисел чем чаще вызываем, тем меньше вероятность одинаковых бросков
 if (mode==MODE_TIMER){  //таймер
      switch (started){
        case MODE1_START:
    if (alltime==0){   // больше ничего считать не нужно, досчитали до нуля
        Beep(); 
          time[0]=0;
          time[1]=0;
          time[2]=0;
        if (IsChanged()==1){ // проверка на изменение
      Refresh();        }
        started = MODE1_STOP; // останавливаем таймер
  TIM_ClearITPendingBit(TIM14, TIM_IT_Update);
  return;
   }
     alltime--;
     all = alltime/10; // разбиваем на каждую цифру 
     _2 = all/100;
     _1 = (all-(_2*100))/10;
     _0 = all-(_2*100)-(_1*10);
     time[0]=_0;
     time[1]=_1;
     time[2]=_2;
    break;      case MODE1_STOP:   // ничего не делать
    break;
      case MODE1_SET:
    break;
  }    }
   if (mode==MODE_STOPWATCH) {   //секундомер
       if (alltime==6000){   // 10 минут
     Beep();
     alltime=0;
     time[0]=0;
     time[1]=0;
     time[2]=0;
     point[2]=POINT;
     }
     if (started==0){   // больше ничего считать не нужно
      TIM_ClearITPendingBit(TIM14, TIM_IT_Update);
      return;
     }
     alltime++;
      all = alltime/10;
      /* это одна минута и секунды*/
  _2 = all/60;
  _1 = (all-(_2*60))/10;
  _0 = all-(_2*60)-(_1*10);
  point[2]=POINT; // у секундомера точка вторая
  time[0]=_0;
  time[1]=_1;
  time[2]=_2;
    }
    if (mode==2){     // температура
   ReadTemp(); // просто считали температуру
    }
    if (mode==3){ // бросок кубика
        if (started==1) {
      alltime++;
      time[0] = rand() % 6 + 1;
      time[2] = rand() % 6 + 1;
      if (alltime>30){
          started = 0;
        alltime =0;
    }
    }
  }
    if (mode==4){    // релакс
    if (started==1) { 
        if (down==1){ // уменьшаем яркость
      brigthness=brigthness-10;
        } else {
      brigthness=brigthness+10;
        }
        if (brigthness<700){
      down=0;
        }
        if (brigthness>1100){
      down=1;
      switch ( color ) { // меняем цвет
        case LED_GREEN:
  color = LED_BLUE;
  break;
        case LED_BLUE:
    color = LED_RED;
    break;
        case LED_RED:
    color = LED_GREEN;
  break;
  }
     DataSend(color); // отправили данные
     DataSend(0);
     DataSend(0);
     DataSend(0);
     }
  TIM3->CCR1=brigthness;
    }    TIM_ClearITPendingBit(TIM14, TIM_IT_Update);
    return;
}
if (IsChanged()==1){
     Refresh();
}
TIM_ClearITPendingBit(TIM14,TIM_IT_Update);
}

Поскольку мы используем драйвер светодиодов  STP16CP05MTR у
которого есть вывод OE, то можем управлять яркостью свечения просто
включая или выключая отображение при помощи шим. Я соединил вывод PA6
контроллера с выводом OE драйвера , активировал на A6 таймер 3 и
подключил на A6

GPIO_InitTypeDef GPIO_InitStructure;
// a6 для шим
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //ножка пуш-пул
GPIO_Init( GPIOA , &amp;GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_1);
// таймер 3
TIM_SETUP.TIM_CounterMode = TIM_CounterMode_Up; //считаем вверх
TIM_SETUP.TIM_Period = 1023; //период таймера 1023 отсчета
TIM_SETUP.TIM_Prescaler = 0; //предделитель откл
TIM_TimeBaseInit(TIM3, &amp;TIM_SETUP);
TIM_OCInitTypeDef PWM_SETUP; //структура настройки ШИМ
PWM_SETUP.TIM_Pulse = 0; // начальное значение
PWM_SETUP.TIM_OCMode = TIM_OCMode_PWM1; //режим1 center align
PWM_SETUP.TIM_OutputState =TIM_OutputState_Enable; //подключаем к выходу
PWM_SETUP.TIM_OCPolarity = TIM_OCPolarity_High; //положительная полярность
TIM_OC1Init(TIM3, &amp;PWM_SETUP);
TIM_Cmd(TIM3, ENABLE);
TIM3->CCR1=setbrightness; // значение для ШИМ по умолчанию



Считывание значений при нажатии кнопок, для этого необходимо включить тактирование шины А и инициализировать структуру GPIO

//Включаем тактирование шины А
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
// инициализация периферии
GPIO_InitTypeDef GPIO_InitStructure;
// a1,a2 кнопка
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_InitStructure.GPIO_PuPd =GPIO_PuPd_NOPULL; // подтяжка на самой плате
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //ножка пуш-пул
GPIO_Init(GPIOA, &amp;GPIO_InitStructure);

В самом цикле программы считываем значения и изменяем данные для отображения

/*
 * started = 0 - на паузе
 * started = 1 - работает
 * started = 2 - настройка все на паузу и настраиваем таймер
 *
 */#define MODE1_STOP 0
#define MODE1_START 1
#define MODE1_SET 2
/*
 *
 * mode = 0 - таймер синий
 * mode = 1 - секундомер красный
 * mode = 2 - температура
 * mode = 3 - кубики зеленый
 * mode = 4 - релаксация красно-зеленый
 */#define MODE_TIMER 0
#define MODE_STOPWATCH 1
#define MODE_TEMP 2
#define MODE_DICE 3
#define MODE_RELAX 4
#define MODE_BRIGHT 5
...............          while(1) {
        if ((GPIOA->IDR &amp; 0x06)==0x06) {  // нажаты две кнопки проверка битов
      if (mode==MODE_TIMER) {      
          if (started!=MODE1_SET){   // включили настройку
       started=MODE1_SET;
    timerset=2;
    alltime = settime*10; // каждую десятую секунду обновление
    time[0]=settime/100;
    time[1]=(settime-time[0]*100)/10;
    time[2]=(settime-time[0]*100-time[1]*10);
        } else {
      started=MODE1_STOP;
      timerset=4;
        }
     } 
        if ((GPIOA->IDR &amp; 0x02)==0x02 &amp; ((GPIOA->IDR&amp; 0x04) != 0x04)) { //Кнопка A1 нажата второй бит, A2 не нажата
        if (!button_flag) {
    button_flag=1;
        if (mode==0) {
    TimerA1();
        } else if (mode==1){
   if (started==0) {
       started = 1;
       alltime =0;
       point[0] = 0;
       point[1] = 0;
       point[2] = 0;
       point[2]=POINT;
   } else {
       started = 0;
  }
   }
      } else if (mode==MODE_DICE){
     switch (started) {
  case MODE1_STOP:
  started = 1;
  alltime = 0;
   break;
  case MODE1_START:
   break;
       case MODE1_SET:
  setbrightness=setbrightness+50;
  if (setbrightness>1000){
      setbrightness = 700;
  }
  TIM3->CCR1=setbrightness;
   break;
       }
  }  else if (mode==4){
   if (started==0) {
       started = 1;
       alltime = 0;
   } else {started = 0;}
  }
      delay_ms(100);
      } else {
        button_flag=0;
      }
 if((GPIOA->IDR &amp; 0x04) == 0x04 &amp;(GPIOA->IDR &amp; 0x02)!=0x02){ //Кнопка A2 нажата 3-й бит
  if (!button2_flag){  // переключение по второй кнопке
        button2_flag = 1;
 
 if (mode ==MODE_TIMER) {
        point[0] = 0;
        point[1] = 0;
        point[2] = 0;
        point[2]=POINT;
    switch (started){
        case MODE1_SET:
          if (timerset==2){   // настройка первой цифры
  timerset=1;
      }else   if (timerset==1){   // настройка первой цифры
  timerset=0;
      } else if (timerset==0){
   timerset=4;
   started=MODE1_STOP;
      }
      break;
        case MODE1_STOP:
      mode=MODE_STOPWATCH;
      started = MODE1_STOP;
      alltime=0;
      time[0]=0;
      time[1]=0;
      time[2]=0;
      break;
        case MODE1_START:
      mode=MODE_STOPWATCH;
      started = MODE1_STOP;
      alltime=0;
      time[0]=0;
      time[1]=0;
      time[2]=0;
      break;
        }
 switch (started){
  case MODE1_SET:
  if (timerset==2){ // настройка первой цифры
  time[2]++;
  if (time[2]>9){
  time[2]=0;
  }  }else if (timerset==1){ // настройка первой цифры
  time[1]++;
  if (time[1]>9){
  time[1]=0;
  }
  //Refresh();
  } else if (timerset==0){
 time[0]++;
  if (time[0]>9){
  time[0]=0;
  }  }
  settime = time[2]*100+time[1]*10+time[0];
  Refresh();  break;
  case MODE1_START:
  started =MODE1_STOP;
  alltime=settime;
  Refresh();
  break;
  case MODE1_STOP: // стартуем, обновление по прерыванию
  alltime=settime*10; // таймер работает на одну десятую секунды
  started =MODE1_START;
  break;  }
     if (IsChanged()==1){
     Refresh();
     }
      } else
      if (mode ==1){
    mode=2;
      } else....

Получение данных по I2C. Адрес LM75A формируется аппаратно, просто замыканием  на корпус ножек A0,A1,A2, поскольку у нас других устройств нет, то мы можем себе это позволить.

#define I2CADDR (0b01001000 << 1)uint8_t I2C_Read_Transaction (uint8_t Adress, uint8_t Register, uint8_t *Data, uint8_t Size)
{
 u8 Count=0; // Счётчик успешно принятых байт
 // Старт
 I2C_Start_Direction_Adress_Size (I2C_Transmitter, Adress, 1);
 // Сейчас либо I2C запросит первый байт для отправки,
 // Либо взлетит NACK-флаг, говорящий о том, что микросхема не отвечает.
 // Если взлетит NACK-флаг, отправку прекращаем.
 u8 temp = I2C_BUS->ISR;
 while ((((I2C_BUS->ISR &amp; I2C_ISR_TC)==0) &amp;&amp; ((I2C_BUS->ISR &amp; I2C_ISR_NACKF)==0)) &amp;&amp; (I2C_BUS->ISR &amp; I2C_ISR_BUSY))
 {
 if (I2C_BUS->ISR &amp; I2C_ISR_TXIS) I2C_BUS->TXDR = Register; // Отправляю адрес регистра
 }
 // Повторный старт
 I2C_Start_Direction_Adress_Size (I2C_Receiver, Adress, Size);
 // Принимаем байты до тех пор, пока не взлетит TC-флаг.
 // Если взлетит NACK-флаг, приём прекращаем.
 while ((((I2C_BUS->ISR &amp; I2C_ISR_TC)==0) &amp;&amp; ((I2C_BUS->ISR &amp; I2C_ISR_NACKF)==0)) &amp;&amp; (I2C_BUS->ISR &amp; I2C_ISR_BUSY))
 {
 if (I2C_BUS->ISR &amp; I2C_ISR_RXNE) *(Data+Count++) = I2C_BUS->RXDR; // Принимаю данные
 }
 I2C_Stop();
 return Count;
 if (Count == Size) return 1; return 0;
}/*
 * чтение температуры с датчика
 */
uint16_t ReadTemp(){ uint16_t cou;
 uint8_t dec;
 uint8_t buff[2]; buff[0]=0;
 buff[1]=0; time[0] = 0;
 time[1] = 0;
 time[2] = 0;
 point[0] = 0;
 point[1] = 0;
 point[2] = 0; // градусы
 cou=I2C_Read_Transaction (I2CADDR, 0x00, buff, 2);
 if (cou!=2){ // не считали
 return 0;
 }
 uint16_t temp;
 int hi,lo;
 temp = buff[0]<<8;
 temp|= buff[1]&amp;0xE0;
 hi = temp>>8;
 if (temp<0) {
 //lo = ((8-(temp>>5))&amp;7); десятые опускаем
 lo = ((temp>>5)&amp;7);
 lo=8-lo;
 dec = hi/10;
 time[1] = dec;
 time[0] = hi-dec*10;
 time[2] = 10; // это минус
 point[0] = POINT;
 } else {
 lo = ((temp>>5)&amp;7);
 lo*=125;
 dec = hi/10;
 time[2] = dec;
 time[1] = hi-dec*10;
 time[0] = lo/100;
 point[1] = POINT;
 }
}

В итоге получилось компактное многофункциональное устройство на STM32, работающее от двух пальчиковых батарей.  Исходный код и проект для CoCox можно взять здесь>>>

Теги: STM32, таймер, программирование, I2C, SPI,

Еще по теме

Полезно? Поделитесь ссылкой! E-mail Сохраните на будущее!  

Разделы

Поиск

Авторизация


(c) 2010-2015 Используй с пользой. При цитировании обязательна рабочая ссылка на http://www.useto.ru.