YAMBC - Yet another msp430-based clock - Часть 2. Софт

Первая часть (железо)
Перейдем к софту!
Программа написана под IAR Kickstart. Размер кода ограничен 4Кб, но для нашего проекта нам хватит.
Давайте рассмотрим выстраданный код работы с часами по i2c. Библиотеки и примеры от TI совершенно неработоспособны. Путем долгих экспериментов удалось подобрать работающее решение.
Пару констант для читабельности кода

  unsigned char CLOCK_ADR = 0x50;
  unsigned char time_adr = 0x02;  
  unsigned char clc_off = {0xC0};   
  unsigned char clc_on = {0x40}; 
  unsigned char sec, min, hour, day, month; 

Инициализиуем модуль USCI_B0. Этот модуль умеет аппаратный i2c.
void i2c_init(void)
{
    P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
    P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
    
    UCB0CTL1 |= UCSWRST;                      // Enable SW reset
    UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     // I2C Master, synchronous mode
    UCB0CTL1 = UCSSEL_2 + UCSWRST ;           // Use SMCLK, keep SW reset
    UCB0BR0 = 10;                             // fSCL = SMCLK/10 = 100kHz
    UCB0BR1 = 0; 
    UCB0I2CSA = CLOCK_ADR;                    // Slave Address is 050h
    UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
}

Чтобы установить время, часы нужно остановить. Для этого в 7 значащий бит часов нужно записать единичку
void clock_off(void)
{
  while (UCB0STAT & UCBBUSY);            
    UCB0CTL1 |= UCTR + UCTXSTT;
    
    
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = clc_off;         // Load TX buffer          
  
    UCB0CTL1 &= (~UCTXNACK);
    
    UCB0CTL1 |= UCTXSTP;              //Send stop message    
    
    IFG2 &= ~(UCB0TXIFG + UCB0RXIFG); //clear if any isr still pending
    UCB0STAT &= ~UCNACKIFG;
}

Теперь можно установить время специальной функцией:

void mem_write(unsigned char adr, unsigned char ss, unsigned char mm, 
               unsigned char hh, unsigned char d, unsigned char M)
{
    while (UCB0STAT & UCBBUSY);            
    UCB0CTL1 |= UCTR + UCTXSTT;
    
    
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = adr;         // Load TX buffer          
    
  //  sec, min, hour, day, month; 
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = ss;         // Load TX buffer          
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = mm;         // Load TX buffer          
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = hh;         // Load TX buffer          
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = d;         // Load TX buffer          
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = M;         // Load TX buffer          
    
    delay();
    
    UCB0CTL1 |= UCTXSTP;              //Send stop message    
    
    IFG2 &= ~(UCB0TXIFG + UCB0RXIFG);        //clear if any isr still pending
    UCB0STAT &= ~UCNACKIFG;

}

Вызываем, например, так:

  mem_write(time_adr, 0, 0x11, 0x23, 0x14, 0x15);
//0 секунд, 11 минут 23 часа...


Запускаем часы — чтобы начали считать время:
void clock_on(void)
{
    while (UCB0STAT & UCBBUSY);            
    UCB0CTL1 |= UCTR + UCTXSTT;
    
    
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = clc_on;         // Load TX buffer          
  
    UCB0CTL1 &= (~UCTXNACK);
    
    UCB0CTL1 |= UCTXSTP;              //Send stop message    
    
    IFG2 &= ~(UCB0TXIFG + UCB0RXIFG);        //clear if any isr still pending
    UCB0STAT &= ~UCNACKIFG;
}

Читаем время в цикле функцией mem_read
void mem_read(unsigned char adr)
{
    while (UCB0STAT & UCBBUSY);            
    UCB0CTL1 |= UCTR + UCTXSTT;
    
    
    while (!(IFG2 & UCB0TXIFG)); //Wait until the data has been shifted out
    UCB0TXBUF = adr;         // Load TX buffer          
    
    while( UCB0CTL1 & UCTXSTT); //ensure that start is sent

    
    UCB0CTL1 &= (~UCTR);        //receiver mode
    UCB0CTL1 |= UCTXSTT;        // I2C TX, start condition
    
    while(UCB0CTL1 & UCTXSTT);  ///ensure that start is sent
    unsigned char xz;
    while(! (IFG2 & UCB0RXIFG)); //wait while rx flag is set or nack is set
    xz = UCB0RXBUF;
  //  sec, min, hour, day, month; 
    while(! (IFG2 & UCB0RXIFG)); //wait while rx flag is set or nack is set
    sec = UCB0RXBUF;
    while(! (IFG2 & UCB0RXIFG));
    min = UCB0RXBUF;
    while(! (IFG2 & UCB0RXIFG));
    hour = UCB0RXBUF;
    while(! (IFG2 & UCB0RXIFG)); 
    day = UCB0RXBUF;
    while(! (IFG2 & UCB0RXIFG)); 
    month = UCB0RXBUF;
    
    
    UCB0CTL1 |= UCTXSTP;              //Send stop message    
  
    IFG2 &= ~(UCB0TXIFG + UCB0RXIFG);        //clear if any isr still pending
    UCB0STAT &= ~UCNACKIFG;
}

Переменная xz не зря носит говорящее название. Дело в том, при первом вызове функции mem_read после повторного старта в первый байт считается записанное в памяти значение по адресу time_adr. При втором вызове в первый байт считывается какой-то мусор, а все остальные байты смещаются на один адрес — т.е. секунды живут в минутах, минуты в часах и т.д. Таким образом секунды надо читать с адреса 0x3 а не в 0x2. Но кроме этого глюка, часы работают хорошо и стабильно с часовым кварцем на 12.5 пикофарад.

Комментарии (0)

RSS свернуть / развернуть
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.