Вот и пришло время выложить исходный код прошивки МК, но сначала я хочу поблагодарить bigbee за новенький корпус для моих часов, который он отпечатал и выслал мне совершенно бесплатно. Так же спасибо Chameleonka за предложение напечатать корпус.
Итак, программа была написана на Code Compmser Studio V 5.3.0
Расписывать код досконально я не буду, но важные моменты (на мой взгляд) распишу.
Сразу хочу оговориться, что данная программа не идеальна и не оптимизирована.
Приступим
В программу включен файл Symbols.h, содержащий таблицу символов, иконки панели уведомлений, цифры для часов. Все данные представлены в виде массивов, в которых хранятся данные для попиксельного отображения на экране.
Так же включен файл TI_USCI_I2C_master.h. Это стандартная библиотека от TI для работы с модулем USCI в качестве I2C master для приема — передачи данных.
#include <msp430g2553.h>
#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 0x68 // 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*) 0x104d;
contrast = *a++;
pwm_width = 0x00ff | (*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, 0x3D); // Charge pump ON
LcdWrite(LCD_C, 0x01); // Charge pump=4
LcdWrite(LCD_C, 0xA4); //
LcdWrite(LCD_C, 0x2F); //
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 = 0x0fff; // период ШИМ
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 |= 0x01; // устанавливаем 0-ой пин как вход для АЦП
Прерывания
Во всех обработчиках прерываний сначала отключаются другие прерывания, которые при завершении обработчика включаются обратно.
При приеме данных по BT происходит прерывание либо Timer1 если часы в активном режиме, либо P2, если часы в режиме энергосбережения.
В режиме LPM3 SMCLK отключен, соответственно таймер не работает. Если произошло прерывание порта P2 от BT, переводим МК в активный режим и переводим пин BT_RXD на вход Timer1.
По окончанию приема данных (символ конца сообщения 0x00) происходит обработка полученной строки. Первый символ строки — идентификатор. «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(0x7f);
} else
while (inputString[il] != 0x00) {
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] != 0x00) {
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(0x7f);
} else
while (inputString[il] != 0x00) {
LcdCharacter(inputString[il]);
if (++il > k){
Lcd_set_pos(0, ++z);
k += 15;
}
}
break;
}
case '5': { // сохранение присланного времени
edit_time = 1;
s10 = inputString[1] & 0x0f;
s1 = inputString[2] & 0x0f;
m10 = inputString[3] & 0x0f;
m1 = inputString[4] & 0x0f;
h10 = inputString[5] & 0x0f;
h1 = inputString[6] & 0x0f;
dw = (inputString[7] & 0x0f) + 1 ;
d10 = inputString[8] & 0x0f;
d1 = inputString[9] & 0x0f;
mo10 = inputString[10] & 0x0f;
mo1 = inputString[11] & 0x0f;
ye10 = inputString[14] & 0x0f;
ye1 = inputString[15] & 0x0f;
set_time = 1;
break;
}
default: {
break;
}
}
if (!multiscreen)
for (il = 313; il > 0; il--)
inputString[il] = 0;
Обработчик прерывания P2 так же проверяет нажатие кнопок. Если кнопка нажата, происходит выход из режима энергосбережения. Так же для каждой кнопки в обработчике прерывания P2 есть цикл проверки «длинного» нажатия.
Обработчик прерывания WDT+ проверяет состояние BT, дает команду для обновления времени с ЧРВ, дает команду на включение АЦП.
Обработчик прерывания ADC10 дает команду для обновления состояния аккумулятора. Прерывание ADC10 срабатывает после окончания преобразования.
// запись данных во FLASH
char *Flash_ptrC;
Flash_ptrC = (char *) 0x1040; // 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 = 0x00; // Dummy write to erase Flash seg C
FCTL1 = FWKEY + WRT; // Set WRT bit for write operation
Flash_ptrC = (char *) 0x1040; // Point to beginning
*Flash_ptrC++ = s10; // 0x1040
*Flash_ptrC++ = s1; // 0x1041
*Flash_ptrC++ = m10; // 0x1042
*Flash_ptrC++ = m1; // 0x1043
*Flash_ptrC++ = h10; // 0x1044
*Flash_ptrC++ = h1; // 0x1045
*Flash_ptrC++ = dw; // 0x1046
*Flash_ptrC++ = d10; // 0x1047
*Flash_ptrC++ = d1; // 0x1048
*Flash_ptrC++ = mo10; // 0x1049
*Flash_ptrC++ = mo1; // 0x104a
*Flash_ptrC++ = ye10; // 0x104b
*Flash_ptrC++ = ye1; // 0x104c
*Flash_ptrC++ = contrast; // 0x104d
*Flash_ptrC++ = (pwm_width & 0xff00) >> 8; // 0x104e
*Flash_ptrC++ = (timer_off & 0xff00) >> 8; // 0x104f
*Flash_ptrC++ = (timer_off & 0x00ff); // 0x1050
FCTL1 = FWKEY; // Clear WRT bit
FCTL3 = FWKEY + LOCK; // Set LOCK
// чтение данных из FLASH
char *a;
a = (char*) 0x1040;
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 = 0x00ff | (*a++) << 8;
timer_off = ((*a++) & 0x00ff) << 8;
timer_off |= ((*a++) & 0x00ff);
Исходники программы тут
И еще фото новенького корпуса:
Как всегда жду с нетерпением вопросов и комментариев.
Часть 1 — Начало
Часть 2 — Плата и компоненты
Автор: winKING