Commit the AM1805 library
AM1805.cpp
- Committer:
- marcusC
- Date:
- 2015-12-24
- Revision:
- 0:5e0616a05642
File content as of revision 0:5e0616a05642:
/* AM1805 Sample code: external RTC module is used by host MCU */ #include "mbed.h" #include "AM1805.h" #define I2C_SDA p28 #define I2C_SCL p27 #define AM1805_I2C_ADDRESS 0xD2 /* Register Map */ #define AM1805_REG_HUNDREDTHS 0x00 #define AM1805_REG_ALM_HUN 0x08 #define AM1805_REG_STATUS 0x0F #define AM1805_REG_CTRL1 0x10 #define AM1805_REG_CTRL2 0x11 #define AM1805_REG_INTMASK 0x12 #define AM1805_REG_SLEEPCTRL 0x17 #define AM1805_REG_TIM_CTRL 0x18 #define AM1805_REG_CDTIM 0x19 #define AM1805_REG_TIMINIT 0x1A #define AM1805_REG_WDT 0x1B #define AM1805_REG_OSCSTATUS 0x1D #define AM1805_REG_ID0 0x28 #define AM1805_REG_EXTADDR_REG 0x3F /* Register Value */ #define AM1805_VALUE_ID0 0x18 #define AM1805_DEBUG 0 I2C i2c(I2C_SDA, I2C_SCL); #if AM1805_DEBUG Serial pc(p13,p14); #endif static uint8_t get_extension_address(uint8_t address) { uint8_t xadd; char temp; am1805_register_read(AM1805_REG_EXTADDR_REG, &temp, 1); temp = temp & 0xC0; if (address < 64) { xadd = 0x8; } else if (address < 128) { xadd = 0x9; } else if (address < 192) { xadd = 0xA; } else { xadd = 0xB; } return (xadd | temp); } /* Set one or more bits in the selected register, selected by 1's in the mask */ static void setreg(char address, uint8_t mask) { char temp; am1805_register_read(address, &temp, 1); temp |= mask; am1805_register_write(address, temp); } /* Clear one or more bits in the selected register, selected by 1's in the mask */ static void clrreg(char address, uint8_t mask) { char temp; am1805_register_read(address, &temp, 1); temp &= ~mask; am1805_register_write(address, temp); } static bool am1805_verify_product_id(void) { char who_am_i[1]; am1805_register_read(AM1805_REG_ID0, &who_am_i[0], 1); #if AM1805_DEBUG pc.printf("ID:%x\r\n",who_am_i[0]); #endif if (who_am_i[0] != AM1805_VALUE_ID0) return false; else return true; } bool am1805_init(void) { bool transfer_succeeded = false; i2c.frequency(400000); /* Read and verify product ID */ transfer_succeeded = am1805_verify_product_id(); return transfer_succeeded; } void am1805_register_read(char register_address, char *destination, uint8_t number_of_bytes) { i2c.write(AM1805_I2C_ADDRESS, ®ister_address, 1, 1); i2c.read(AM1805_I2C_ADDRESS, destination, number_of_bytes); } void am1805_register_write(char register_address, uint8_t value) { char tx_data[2]; tx_data[0] = register_address; tx_data[1] = value; i2c.write(AM1805_I2C_ADDRESS, tx_data, 2); } void am1805_burst_write(uint8_t *tx_data, uint8_t number_of_bytes) { i2c.write(AM1805_I2C_ADDRESS, (char *)tx_data, number_of_bytes); } void am1805_config_input_interrupt(input_interrupt_t index_Interrupt) { switch(index_Interrupt) { case XT1_INTERRUPT: /* Set input interrupt pin EX1T */ clrreg(AM1805_REG_STATUS,0x01); // Clear EX1 setreg(AM1805_REG_INTMASK,0x01); // Set EX1E break; case XT2_INTERRUPT: /* Set input interrupt pin WDI */ clrreg(AM1805_REG_STATUS,0x02); // Clear EX2 setreg(AM1805_REG_INTMASK,0x02); // Set EX2E break; default: #if AM1805_DEBUG pc.printf("Wrong Input Interrupt Index\r\n"); #endif break; } } // Read a byte from local RAM uint8_t am1805_read_ram(uint8_t address) { uint8_t xadd; char temp; uint8_t reg_ram = 0; xadd = get_extension_address(address); // Calc XADDR value from address am1805_register_write(AM1805_REG_EXTADDR_REG, xadd); // Load the XADDR register reg_ram = (address & 0x3F) | 0x40; // Read the data am1805_register_read(reg_ram, &temp, 1); #if AM1805_DEBUG pc.printf("Read from addr:%x Data:%x\r\n",address,temp); #endif return (uint8_t)temp; } // Write a byte to local RAM void am1805_write_ram(uint8_t address, uint8_t data) { uint8_t xadd; uint8_t reg_ram = 0; xadd = get_extension_address(address); // Calc XADDR value from address am1805_register_write(AM1805_REG_EXTADDR_REG, xadd); // Load the XADDR register reg_ram = (address & 0x3F) | 0x40; am1805_register_write(reg_ram, data); // Write the data } /* * hundredth : 0 ~ 99 * second : 0 ~ 59 * minute : 0 ~ 59 * weekday : 0 ~ 6 * month : 1 ~ 12 * year : 0 ~ 99 * mode : 0 ~ 2 */ void am1805_set_time(time_reg_struct_t time_regs) { char temp; uint8_t temp_buff[9]; /* * Determine whether 12 or 24-hour timekeeping mode is being used * and set the 1224 bit appropriately */ if (time_regs.mode == 2) // 24-hour day { clrreg(AM1805_REG_CTRL1, 0x40); } else if (time_regs.mode == 1) // 12-hour day PM { time_regs.hour |= 0x20; // Set AM/PM setreg(AM1805_REG_CTRL1, 0x40); } else // 12-hour day AM { setreg(AM1805_REG_CTRL1, 0x40); } /* Set the WRTC bit to enable counter writes. */ setreg(AM1805_REG_CTRL1, 0x01); /* Set the correct century */ if (time_regs.century == 0) { clrreg(AM1805_REG_STATUS, 0x80); } else { setreg(AM1805_REG_STATUS, 0x80); } /* Write all of the time counters */ temp_buff[0] = AM1805_REG_HUNDREDTHS; temp_buff[1] = time_regs.hundredth; temp_buff[2] = time_regs.second; temp_buff[3] = time_regs.minute; temp_buff[4] = time_regs.hour; temp_buff[5] = time_regs.date; temp_buff[6] = time_regs.month; temp_buff[7] = time_regs.year; temp_buff[8] = time_regs.weekday; /* Write the values to the AM18XX */ am1805_burst_write(temp_buff, sizeof(temp_buff)); /* Load the final value of the WRTC bit based on the value of protect */ am1805_register_read(AM1805_REG_CTRL1, &temp, 1); temp &= 0x7E; // Clear the WRTC bit and the STOP bit //temp_buff[1] |= (0x01 & (~protect)); // Invert the protect bit and update WRTC am1805_register_write(AM1805_REG_CTRL1, temp); return; } void am1805_get_time(time_reg_struct_t *time_regs) { char temp_buff[8]; char time_mode; /* Read the counters. */ am1805_register_read(AM1805_REG_HUNDREDTHS, temp_buff, 8); time_regs->hundredth = temp_buff[0]; time_regs->second = temp_buff[1]; time_regs->minute = temp_buff[2]; time_regs->hour = temp_buff[3]; time_regs->date = temp_buff[4]; time_regs->month = temp_buff[5]; time_regs->year = temp_buff[6]; time_regs->weekday = temp_buff[7]; /* Get the current hours format mode 12:24 */ am1805_register_read(AM1805_REG_CTRL1, &time_mode, 1); if ((time_mode & 0x40) == 0) { /* 24-hour mode. */ time_regs->mode = 2; time_regs->hour = time_regs->hour & 0x3F; // Get tens:ones } else { /* 12-hour mode. Get PM:AM. */ time_regs->mode = (time_regs->hour & 0x20) ? 1 : 0; // PM : AM time_regs->hour &= 0x1F; // Get tens:ones } time_regs->hundredth = temp_buff[0]; time_regs->second = temp_buff[1]; time_regs->minute = temp_buff[2]; time_regs->hour = temp_buff[3]; time_regs->date = temp_buff[4]; time_regs->month = temp_buff[5]; time_regs->year = temp_buff[6]; time_regs->weekday = temp_buff[7]; #if AM1805_DEBUG pc.printf("hundredth:%x\r\n",time_regs->hundredth); pc.printf("second:%x\r\n",time_regs->second); pc.printf("minute:%x\r\n",time_regs->minute); pc.printf("hour:%x\r\n",time_regs->hour); pc.printf("date:%x\r\n",time_regs->date); pc.printf("month:%x\r\n",time_regs->month); pc.printf("year:%x\r\n",time_regs->year); pc.printf("weekday:%x\r\n",time_regs->weekday); #endif } void am1805_config_alarm(time_reg_struct_t time_regs, alarm_repeat_t repeat, interrupt_mode_t intmode, interrupt_pin_t pin) { char temp; uint8_t temp_buff[9]; /* Determine whether a 12-hour or a 24-hour time keeping mode is being used */ if (time_regs.mode == 1) { /* A 12-hour day PM */ time_regs.hour = time_regs.hour | 0x20; // Set AM/PM } /* Write all of the time counters */ temp_buff[0] = AM1805_REG_ALM_HUN; temp_buff[1] = time_regs.hundredth; temp_buff[2] = time_regs.second; temp_buff[3] = time_regs.minute; temp_buff[4] = time_regs.hour; temp_buff[5] = time_regs.date; temp_buff[6] = time_regs.month; temp_buff[7] = time_regs.weekday; clrreg(AM1805_REG_TIM_CTRL, 0x1C); // Clear the RPT field clrreg(AM1805_REG_INTMASK, 0x64); // Clear the AIE bit and IM field clrreg(AM1805_REG_STATUS, 0x04); // Clear the ALM flag if (pin == PIN_FOUT_nIRQ) { /* Interrupt on FOUT/nIRQ */ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get the Control2 Register temp = (temp & 0x03); // Extract the OUT1S field if (temp != 0) // Not already selecting nIRQ { setreg(AM1805_REG_CTRL2, 0x03); // Set OUT1S to 3 } } if (pin == PIN_PSW_nIRQ2) { /* Interrupt on PSW/nIRQ2 */ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get the Control2 Register temp &= 0x1C; // Extract the OUT2S field if (temp != 0) // Not already selecting nIRQ { clrreg(AM1805_REG_CTRL2, 0x1C); // Clear OUT2S setreg(AM1805_REG_CTRL2, 0x0C); // Set OUT2S to 3 } } if (repeat == ONCE_PER_10TH_SEC) { /* 10ths interrupt */ temp_buff[1] |= 0xF0; repeat = ONCE_PER_SECOND; // Select correct RPT value } if (repeat == ONCE_PER_100TH_SEC) { /* 100ths interrupt */ temp_buff[1] = 0xFF; repeat = ONCE_PER_SECOND; // Select correct RPT value } if (repeat != 0) // Don't initiate if repeat = 0 { temp = (repeat << 2); // Set the RPT field to the value of repeat setreg(AM1805_REG_TIM_CTRL, temp); // Was previously cleared setreg(AM1805_REG_INTMASK, (intmode << 5)); // Set the alarm interrupt mode setreg(AM1805_REG_INTMASK, 0x60); // Set the alarm interrupt mode am1805_burst_write(temp_buff, 8); // Execute the burst write setreg(AM1805_REG_INTMASK, 0x04); // Set the AIE bit } else setreg(AM1805_REG_INTMASK, 0x60); // Set IM field to 0x3 (reset value) to minimize current draw return; } void am1805_config_countdown_timer(count_down_range_t range, int32_t period, count_down_repeat_t repeat, interrupt_pin_t pin) { uint8_t tm; uint8_t trpt; uint8_t tfs; uint8_t te; char temp; char tctrl; int32_t timer; char oscmode; /* 0 = XT, 1 = RC */ am1805_register_read(AM1805_REG_OSCSTATUS, &oscmode, 1); oscmode = (oscmode & 0x10) ? 1 : 0; /* disable count down timer */ if (pin == INTERNAL_FLAG) { te = 0; } else { te = 1; if (repeat == SINGLE_LEVEL_INTERRUPT) { /* Level interrupt */ tm = 1; // Level trpt = 0; // No repeat if (range == PERIOD_US) { /* Microseconds */ if (oscmode == 0) { /* XT Mode */ if (period <= 62500) // Use 4K Hz { tfs = 0; timer = (period * 4096); timer = timer / 1000000; timer = timer - 1; } else if (period <= 16384000) // Use 64 Hz { tfs = 1; timer = (period * 64); timer /= 1000000; timer = timer - 1; } else // Use 1 Hz { tfs = 2; timer = period / 1000000; timer = timer - 1; } } else { /* RC Mode */ if (period <= 2000000) { // Use 128 Hz tfs = 0; timer = (period * 128); timer /= 1000000; timer = timer - 1; } else if (period <= 4000000) { // Use 64 Hz tfs = 1; timer = (period * 64); timer /= 1000000; timer = timer - 1; } else { // Use 1 Hz tfs = 2; timer = period / 1000000; timer = timer - 1; } } } else { /* Seconds */ if (period <= 256) { /* Use 1 Hz */ tfs = 2; timer = period - 1; } else { /* Use 1/60 Hz */ tfs = 3; timer = period / 60; timer = timer - 1; } } } else { /* Pulse interrupts */ tm = 0; // Pulse trpt = repeat & 0x01; // Set up repeat if (repeat < REPEAT_PLUSE_1_128_SEC) { tfs = 0; if (oscmode == 0) { timer = (period * 4096); timer /= 1000000; timer = timer - 1; } else { timer = (period * 128); timer /= 1000000; timer = timer - 1; } } else if (repeat < REPEAT_PLUSE_1_64_SEC) { tfs = 1; timer = (period * 128); timer /= 1000000; timer = timer - 1; } else if (period <= 256) { /* Use 1 Hz */ tfs = 2; timer = period - 1; } else { /* Use 1/60 Hz */ tfs = 3; timer = period / 60; timer = timer - 1; } } } am1805_register_read(AM1805_REG_TIM_CTRL, &tctrl, 1); // Get TCTRL, keep RPT, clear TE tctrl = tctrl & 0x1C; am1805_register_write(AM1805_REG_TIM_CTRL, tctrl); tctrl = tctrl | (te * 0x80) | (tm * 0x40) | (trpt * 0x20) | tfs; // Merge the fields if (pin == PIN_FOUT_nIRQ) // generate nTIRQ interrupt on FOUT/nIRQ (asserted low) { clrreg(AM1805_REG_CTRL2, 0x3); // Clear OUT1S } if (pin == PIN_PSW_nIRQ2) // generate nTIRQ interrupt on PSW/nIRQ2 (asserted low) { am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get OUT2S if ((temp & 0x1C) != 0) { temp = (temp & 0xE3) | 0x14; // If OUT2S != 0, set OUT2S to 5 } am1805_register_write(AM1805_REG_CTRL2, temp); // Write back } if (pin != 0) { clrreg(AM1805_REG_STATUS,0x08); // Clear TIM setreg(AM1805_REG_INTMASK,0x08); // Set TIE am1805_register_write(AM1805_REG_CDTIM, timer); // Initialize the timer am1805_register_write(AM1805_REG_TIMINIT, timer); // Initialize the timer repeat am1805_register_write(AM1805_REG_TIM_CTRL, tctrl); // Start the timer } return ; } /** Parameter: * timeout - minimum timeout period in 7.8 ms periods (0 to 7) * mode - sleep mode (nRST modes not available in AM08xx) * 0 => nRST is pulled low in sleep mode * 1 => PSW/nIRQ2 is pulled high on a sleep * 2 => nRST pulled low and PSW/nIRQ2 pulled high on sleep * error ?returned value of the attempted sleep command * 0 => sleep request accepted, sleep mode will be initiated in timeout seconds * 1 => illegal input values * 2 => sleep request declined, interrupt is currently pending * 3 => sleep request declined, no sleep trigger interrupt enabled **/ void am1805_set_sleep(uint8_t timeout, uint8_t mode) { uint8_t slres = 0; char temp = 0; #if AM1805_DEBUG am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get SLST bit (temp & 0x08) if ( ( temp & 0x08 ) == 0) { pc.printf("Previous Sleep Failed\r\n"); } else { pc.printf("Previous Sleep Successful\r\n"); } clrreg(AM1805_REG_CTRL2,0x08); // Clear SLST am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get SLST bit (temp & 0x08) if ( ( temp & 0x08 ) == 0) { pc.printf("Clear Succ\r\n"); } else { pc.printf("Clear Fail\r\n"); } clrreg(AM1805_REG_CTRL2,0x08); // Clear SLST #endif if (mode > 0) { /* Sleep to PSW/nIRQ2 */ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Read OUT2S temp = (temp & 0xE3) | 0x18; // MUST NOT WRITE OUT2S WITH 000 am1805_register_write(AM1805_REG_CTRL2, temp); // Write value to OUT2S slres = 0; } temp = timeout | (slres << 6) | 0x80; // Assemble SLEEP register value am1805_register_write(AM1805_REG_SLEEPCTRL, temp); // Write to the register #if AM1805_DEBUG /* Determine if SLEEP was accepted */ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get SLP bit (temp & 0x80) if ( ( temp & 0x80 ) == 0) { char reg_wdi_value = 0; /* SLEEP did not happen - determine why and return reason. */ am1805_register_read(AM1805_REG_INTMASK, &temp, 1); // Get status register interrupt enables am1805_register_read(AM1805_REG_WDT, ®_wdi_value, 1); // Get WDT register if ((( temp & 0x0F ) == 0) & (((reg_wdi_value & 0x7C) == 0) || ((reg_wdi_value & 0x80) == 0x80))) { pc.printf("No trigger interrupts enabled\r\n"); } else { pc.printf("Interrupt pending\r\n"); } } else { pc.printf("SLEEP request successful\r\n"); } #endif }