Умные часы своими руками за 1500 рублей. Часть 3 – программа на МК

360bd3ba4c7bb3ef7f59a435330e4b1d.jpgВот и пришло время выложить исходный код прошивки МК, но сначала я хочу поблагодарить bigbee за новенький корпус для моих часов, который он отпечатал и выслал мне совершенно бесплатно. Так же спасибо Chameleonka за предложение напечатать корпус.

Итак, программа была написана на Code Compmser Studio V 5.3.0Расписывать код досконально я не буду, но важные моменты (на мой взгляд) распишу.Сразу хочу оговориться, что данная программа не идеальна и не оптимизирована.Приступим

В программу включен файл Symbols.h, содержащий таблицу символов, иконки панели уведомлений, цифры для часов. Все данные представлены в виде массивов, в которых хранятся данные для попиксельного отображения на экране.Так же включен файл TI_USCI_I2C_master.h. Это стандартная библиотека от TI для работы с модулем USCI в качестве I2C master для приема — передачи данных.

Включения и определения #include #include «Symbols.h» #include «TI_USCI_I2C_master.h»

/* Bit operations */ #define BIT_SET (lval, mask) ((lval) |= (mask)) #define BIT_CLR (lval, mask) ((lval) &= ~(mask)) #define BIT_TEST (val, mask) (((val) & (mask))==(mask)) /* BSL */ #define TXD BIT1 // P1.1: BSL TxD #define RXD BIT5 // P1.5: BSL RxD /* BT */ #define BT_TXD BIT1 // P2.1: UART BT TxD #define BT_RXD BIT0 // P2.0: UART BT RxD #define BT_PWR BIT2 // P2.2, P3.2 #define BT_LED BIT3 // P3.3 /* LCD */ #define PIN_RESET BIT2 // P1.2 RESET #define PIN_SCE BIT3 // P1.3 CS #define PIN_SDIN BIT4 // P1.4 SDA //mosi #define PIN_SCLK BIT1 // P3.1 SCK #define PIN_LED BIT0 // P3.0 подсветка дисплея #define LCD_C 0 // Command #define LCD_D 1 // Data /* Buttons & vibro */ #define B_CENT BIT4 // P2.4 #define B_UP BIT3 // P2.3 #define B_DOWN BIT5 // P2.5 #define vibro BIT4 // P3.4 /* System configuration */ #define TIMER1A_CLOCK 1000000L // Timer1_A clock rate (1 MHz) #define UART_BAUD 9600 // desired UART baud rate #define BT_BITTIME (TIMER1A_CLOCK/UART_BAUD) // Bit interval #define BT_HALF_BT ((BT_BITTIME+1)/2) // Half-bit interval #define Slave_Address 0×68 // address RTC Определение функций void check_akkum (void); // проверка состояния аккумулятора void check_bluetooth (void); // проверка состояния BT

void uart_tx_bt (char c); // отправка символа по BT void uart_puts_bt (char const* s); // отправка строки по BT

void get_time_from_rtc (void); // получение времени с ЧРВ и запись во FLASH void set_time_to_rtc (void); // запись времени на ЧРВ

void LcdCharacter (char character); // вывести символ void LcdClear (void); // отчистить весь экран void clear_1(void); // отчистить экран, кроме статус бара void Lcd_set_pos (unsigned char c, unsigned char r); // установить позицию в символах на строку void Lcd_set_pos_pix (unsigned char c, unsigned char r); // установить позицию в пикселях на строку void lcd_contrast (unsigned char contrast2); // установить контрастность экрана void lcd_dig (unsigned char num, unsigned char pos_x, unsigned char pos_y); // цифры часов void lcd_dot (unsigned char num, unsigned char pos_x, unsigned char pos_y); // двоеточия часов void LcdWrite (unsigned char dc, unsigned char data); // отправка комманды/данных на экран void LcdString (char *characters); // вывести строку void lcd_show_sms (unsigned char a); // значек смс void lcd_show_call (unsigned char a); // значек звонка void lcd_show_bt (unsigned char a); // значек BT void lcd_show_bat (unsigned char proc); // значек аккумулятора void lcd_set_time_big (void); // вывести большие часы void lcd_set_time_small (void); // вывести малые часы (в статус баре) void lcd_show_main (void); // показать главный экран (дата и большие часы)

void menu_setting (unsigned char submenu); // меню void down_sub_menu (void); // передвижение по меню void up_sub_menu (void); // и изменения в подменю void parse_string (void); // разбор принятой строки Настраиваем модули МК для работы с внешними модулями часов: DCO — Частота встроенного генератора 8MHz; Timer0 — ШИМ для подсветки дисплея; Timer1 — Программный UART для общения с BT; WDT+ — Интервальный таймер для проверок часов; FLASH — Хранение изменяемых настроек и даты/времени; ADC10 — АЦП для определения напряжения на аккумуляторе. Инициализация WDTCTL = WDTPW + WDTHOLD; // отключаем ватчдог // init clocks BCSCTL1 = CALBC1_8MHZ; DCOCTL = CALDCO_8MHZ; BCSCTL2 = 0; // MCLK = 8MHz/1, SMCLK = 8MHz/1 BCSCTL3 = LFXT1S_2; // Mode 2 for LFXT1: VLO = 12kHz

__delay_cycles (800000);

// сажаем BSL ноги на выход и лог. 0 BIT_SET (P1DIR, TXD | RXD); BIT_CLR (P1OUT, TXD | RXD);

edit_time = 0; // берем инфу о настройках экрана из FLASH’а char *a; a = (char*) 0×104d; contrast = *a++; pwm_width = 0×00ff | (*a++) << 8; timer_off = ((*a++) & 0x00ff) << 8; timer_off |= ((*a++) & 0x00ff);

// настройка портов для LCD BIT_SET (P1DIR, PIN_RESET + PIN_SCE + PIN_SDIN); BIT_SET (P3DIR, PIN_SCLK); BIT_CLR (P1OUT, PIN_RESET + PIN_SDIN); BIT_CLR (P3OUT, PIN_SCLK); BIT_SET (P1OUT, PIN_SCE); BIT_SET (P3DIR, PIN_LED); BIT_SET (P3OUT, PIN_LED); BIT_SET (P1OUT, PIN_RESET);

// последовательность инициализации LCD LcdWrite (LCD_C, 0xE2); // сброс програмный LcdWrite (LCD_C, 0×3D); // Charge pump ON LcdWrite (LCD_C, 0×01); // Charge pump=4 LcdWrite (LCD_C, 0xA4); // LcdWrite (LCD_C, 0×2F); // LcdWrite (LCD_C, 0xC0); // нормальное верх-низ LcdWrite (LCD_C, 0xA0); // нормальное лево-право __delay_cycles (800000); LcdWrite (LCD_C, 0xAF); // Display ON

LcdClear (); lcd_contrast (contrast); BIT_SET (P3SEL, PIN_LED); BIT_SET (P3DIR, PIN_LED);

// таймер ШИМ для подсветки TACTL = TASSEL_2 + ID_0 + MC_1 + TACLR; // SMCLK/1 + прямой счет до TACCR0 + counter clear TACCR0 = 0×0fff; // период ШИМ TACCR2 = pwm_width; // заполнение ШИМ для подсветки TACCTL2 = OUTMOD_6; // выход ШИМ

// init Bluetooth BIT_SET (P3DIR, BT_PWR); BIT_CLR (P3REN, BT_PWR); BIT_SET (P3OUT, BT_PWR); BIT_SET (P2DIR, BT_PWR); BIT_CLR (P2REN, BT_PWR); BIT_SET (P2OUT, BT_PWR); //питание БТ ON BIT_CLR (P3DIR, BT_LED); //состояние бт bt_on = 1; lcd_show_bt (1);

//Timer1 для uart TA1CTL = TASSEL_2 + ID_3 + MC_2 + TAIE; //SMCLK/8 + Continous up + interrupt enable TA1CCTL1 = OUT; //Tx TA1CCTL0 = CM_2 + SCS + CAP + CCIE; //Rx

//Порты Rx и TX для БТ BIT_SET (P2SEL, BT_TXD + BT_RXD); BIT_SET (P2DIR, BT_TXD);

//таймер для выкл. экрана BIT_SET (TA1CCTL2, CCIE);

// Настройка кнопок BIT_CLR (P2DIR, B_CENT|B_UP|B_DOWN); // направление пинов — IN BIT_SET (P2REN, B_CENT|B_UP|B_DOWN); // подключение резисторов BIT_SET (P2IE, B_CENT|B_UP|B_DOWN); // Разрешение прерываний BIT_SET (P2IES, B_CENT|B_UP|B_DOWN); // Прерывание происходит по 1/0 (отпусканию/нажатию) BIT_CLR (P2IFG, B_CENT|B_UP|B_DOWN); // Очистка флага прерываний

// WDT+ как интервальный таймер, частота 12 КГц — ACLK — VLO WDTCTL = WDTPW + WDTTMSEL + WDTSSEL; BIT_SET (IE1, WDTIE);

// vibro — выход BIT_SET (P3DIR, vibro); BIT_CLR (P3OUT, vibro);

lcd_show_main (); // главный экран __enable_interrupt (); edit_time = 0; get_time = 0; set_time = 0; get_time_from_rtc (); // обновляем время __delay_cycles (8000000); set_time_to_rtc (); // обновляем время

// init ADC ADC10CTL0 = SREF_1 + ADC10SR + REF2_5V + ADC10SHT_2 + REFON + ADC10ON + ADC10IE; ADC10CTL1 = INCH_0; ADC10AE0 |= 0×01; // устанавливаем 0-ой пин как вход для АЦП ПрерыванияВо всех обработчиках прерываний сначала отключаются другие прерывания, которые при завершении обработчика включаются обратно.При приеме данных по BT происходит прерывание либо Timer1 если часы в активном режиме, либо P2, если часы в режиме энергосбережения.В режиме LPM3 SMCLK отключен, соответственно таймер не работает. Если произошло прерывание порта P2 от BT, переводим МК в активный режим и переводим пин BT_RXD на вход Timer1.По окончанию приема данных (символ конца сообщения 0×00) происходит обработка полученной строки. Первый символ строки — идентификатор.»1» — телефон подключился к часам;»2» — входящее смс;»3» — входящий звонок;»4» — текст, который нужно просто вывести на экран часов;»5» — с телефона отправлены дата и время.Разбор строки unsigned int il; unsigned char z, k;

switch (inputString[0]) { case '1': { // индикация успешно подключившегося к BT телефона BIT_SET (P3OUT, vibro); __delay_cycles (1600000); BIT_CLR (P3OUT, vibro); __delay_cycles (1600000); BIT_SET (P3OUT, vibro); __delay_cycles (1600000); BIT_CLR (P3OUT, vibro); __delay_cycles (1600000); BIT_SET (P3OUT, vibro); __delay_cycles (1600000); BIT_CLR (P3OUT, vibro); lcd_show_bt (2); bt_connect = 1; break; } case '2': { // индикация входящего смс current_screen = 1; lcd_show_sms (1); lcd_show_call (0); clear_1(); Lcd_set_pos (0, 1); z = 1; il = 1; k = 15; if (multiscreen) { current_screen = 4; while (il < 105) { LcdCharacter(inputString[il]); if (++il > k){ Lcd_set_pos (0, ++z); k += 15; } } LcdCharacter (0×7f); } else while (inputString[il] != 0×00) { LcdCharacter (inputString[il]); if (++il > k){ Lcd_set_pos (0, ++z); k += 15; } } BIT_SET (P3OUT, vibro); __delay_cycles (2800000); BIT_CLR (P3OUT, vibro); __delay_cycles (2400000); BIT_SET (P3OUT, vibro); __delay_cycles (8000000); BIT_CLR (P3OUT, vibro); break; } case '3': { // индикация входящего звонка current_screen = 1; lcd_show_sms (0); lcd_show_call (1); clear_1(); Lcd_set_pos (0, 2); il = 1; z = 2; k = 15; while (inputString[il] != 0×00) { LcdCharacter (inputString[il]); if (++il > k){ Lcd_set_pos (0, ++z); k += 15; } } call_true = 1; BIT_SET (P3OUT, vibro); __delay_cycles (1600000); BIT_CLR (P3OUT, vibro); __delay_cycles (800000); BIT_SET (P3OUT, vibro); __delay_cycles (8000000); BIT_CLR (P3OUT, vibro); __delay_cycles (1600000); BIT_SET (P3OUT, vibro); __delay_cycles (1600000); BIT_CLR (P3OUT, vibro); __delay_cycles (800000); BIT_SET (P3OUT, vibro); __delay_cycles (8000000); BIT_CLR (P3OUT, vibro); break; } case '4': { // отображение присланного текста current_screen = 1; lcd_show_sms (0); lcd_show_call (0); clear_1(); Lcd_set_pos (0, 1); il = 1; z = 1; k = 15; if (multiscreen) { current_screen = 4; while (il < 105) { LcdCharacter(inputString[il]); if (++il > k){ Lcd_set_pos (0, ++z); k += 15; } } LcdCharacter (0×7f); } else while (inputString[il] != 0×00) { LcdCharacter (inputString[il]); if (++il > k){ Lcd_set_pos (0, ++z); k += 15; } } break; } case '5': { // сохранение присланного времени edit_time = 1; s10 = inputString[1] & 0×0f; s1 = inputString[2] & 0×0f; m10 = inputString[3] & 0×0f; m1 = inputString[4] & 0×0f; h10 = inputString[5] & 0×0f; h1 = inputString[6] & 0×0f; dw = (inputString[7] & 0×0f) + 1; d10 = inputString[8] & 0×0f; d1 = inputString[9] & 0×0f; mo10 = inputString[10] & 0×0f; mo1 = inputString[11] & 0×0f; ye10 = inputString[14] & 0×0f; ye1 = inputString[15] & 0×0f; set_time = 1; break; } default: { break; } } if (! multiscreen) for (il = 313; il > 0; il--) inputString[il] = 0; Обработчик прерывания P2 так же проверяет нажатие кнопок. Если кнопка нажата, происходит выход из режима энергосбережения. Так же для каждой кнопки в обработчике прерывания P2 есть цикл проверки «длинного» нажатия.Обработчик прерывания WDT+ проверяет состояние BT, дает команду для обновления времени с ЧРВ, дает команду на включение АЦП.Обработчик прерывания ADC10 дает команду для обновления состояния аккумулятора. Прерывание ADC10 срабатывает после окончания преобразования.Работа с FLASH // запись данных во FLASH char *Flash_ptrC; Flash_ptrC = (char *) 0×1040; // Point to beginning of seg C FCTL2 = FWKEY + FSSEL_1 + FN1; // MCLK/3 for Flash Timing Generator FCTL1 = FWKEY + ERASE; // Set Erase bit FCTL3 = FWKEY; // Clear LOCK *Flash_ptrC = 0×00; // Dummy write to erase Flash seg C FCTL1 = FWKEY + WRT; // Set WRT bit for write operation Flash_ptrC = (char *) 0×1040; // Point to beginning *Flash_ptrC++ = s10; // 0×1040 *Flash_ptrC++ = s1; // 0×1041 *Flash_ptrC++ = m10; // 0×1042 *Flash_ptrC++ = m1; // 0×1043 *Flash_ptrC++ = h10; // 0×1044 *Flash_ptrC++ = h1; // 0×1045 *Flash_ptrC++ = dw; // 0×1046 *Flash_ptrC++ = d10; // 0×1047 *Flash_ptrC++ = d1; // 0×1048 *Flash_ptrC++ = mo10; // 0×1049 *Flash_ptrC++ = mo1; // 0×104a *Flash_ptrC++ = ye10; // 0×104b *Flash_ptrC++ = ye1; // 0×104c *Flash_ptrC++ = contrast; // 0×104d *Flash_ptrC++ = (pwm_width & 0xff00) >> 8; // 0×104e *Flash_ptrC++ = (timer_off & 0xff00) >> 8; // 0×104f *Flash_ptrC++ = (timer_off & 0×00ff); // 0×1050 FCTL1 = FWKEY; // Clear WRT bit FCTL3 = FWKEY + LOCK; // Set LOCK

// чтение данных из FLASH char *a; a = (char*) 0×1040; s10 = *a++; s1 = *a++; m10 = *a++; m1 = *a++; h10 = *a++; h1 = *a++; dw = *a++; d10 = *a++; d1 = *a++; mo10 = *a++; mo1 = *a++; ye10 = *a++; ye1 = *a++; contrast = *a++; pwm_width = 0×00ff | (*a++) << 8; timer_off = ((*a++) & 0x00ff) << 8; timer_off |= ((*a++) & 0x00ff); Исходники программы тут

И еще фото новенького корпуса: ff1197fa73a6b045edc6b7198b7e94ff.jpg8709e0523425c51c37603ca4082df26d.jpg

Как всегда жду с нетерпением вопросов и комментариев.

Часть 1 — НачалоЧасть 2 — Плата и компоненты

© Habrahabr.ru