Skip to content

Instantly share code, notes, and snippets.

@ajarmst
Created December 10, 2023 00:46
Show Gist options
  • Save ajarmst/387dccd42c67664a7398eb8d9a7b01f0 to your computer and use it in GitHub Desktop.
Save ajarmst/387dccd42c67664a7398eb8d9a7b01f0 to your computer and use it in GitHub Desktop.
I2C Demo 2 - Advanced RTC, Accelerometer
/////////////////////////////////////////////////////////////////////////////
// HC12 Program: I2C Demo 2 - Advanced RTC, Accelerometer
// Processor: MC9S12XDP512
// Bus Speed: 20 MHz (Requires Active PLL)
// Author: AJ Armstrong
// Details: Demonstration of the I2C Driver Libraries
// Date: Dec 2023
// Revision History :
/////////////////////////////////////////////////////////////////////////////
#include <stdio.h> // For sprintf, etc
#include <ctype.h> // For character utilities
#include <string.h> // Various string utilities
#include <hidef.h> // Common defines and macros
#include "derivative.h" // Derivative-specific definitions
#include "pll.h" // Phase-locked loop clock control
#include "swled.h" // GPIO switches and LEDs
#include "segs.h" // 7-Segment Displays
#include "timer.h" // Enhanced capture timer
#include "portj.h" // Interrupt buttons
#include "pit.h" // Periodic Interrupt timer
#include "rti.h" // Realtime interrupts
#include "lcd.h" // LCD Display
#include "sci.h" // Serial communications interface (EIA/TIA-232)
#include "i2c.h" // Carlos Estay's I2C Driver Libraries
/////////////////////////////////////////////////////////////////////////////
// Local Prototypes
/////////////////////////////////////////////////////////////////////////////
//Several demonstration functions for various devices
void Demo_RTC();
void UploadRTCDate(RTC_Read date);
void DownloadRTCDate(RTC_Read* pdate);
void DisplayRTCDate(RTC_Read date);
void Demo_TPS();
void Display_TPS();
void Demo_ACC();
void Display_ACC();
void Demo_CMP();
void Display_CMP();
/////////////////////////////////////////////////////////////////////////////
// Global Variables
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Constants
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Main Entry
/////////////////////////////////////////////////////////////////////////////
void main(void)
{
// Variables
RTC_Read dateTime; // Type for getting RTC data (see i2c.h)
/////////////////////////////////////////////////////////////////////////////
// One-Time Initializations
/////////////////////////////////////////////////////////////////////////////
_DISABLE_COP(); // No watchdog
PLL_To20MHz(); // Configure the main clock to 20 MHz (implications for timers)
SWL_Init(); // Standard initialization for switches and LEDs
SEG_Init(); // Standard initializaton for 7-seg displays
RTI_Init(); // Initialize RTI timer
/////////////////////////////////////////////////////////////////////////////
// Main Program Loop
/////////////////////////////////////////////////////////////////////////////
EnableInterrupts; // Ensure our board will respond to interrupt signals.
LCD_Init(); // Current LCD drivers need RTI and interrupts. //FIXME
LCD_StringJustify(0,"I2C Demo",eLCD_Just_Center);
LCD_StringJustify(1,"========",eLCD_Just_Center);
// First, initialize the I2C at fast (400 kbps) speed
I2C0_InitBus(I2CBus400);
//Run Device Setups
Demo_RTC();
Demo_ACC();
//The big loop
for (;;)
{
//Show current state of RTC
if(!(rtiCount % 10000)) // Every 10 seconds
{
DownloadRTCDate(&dateTime); //Get current datetime from RTC
DisplayRTCDate(dateTime); //Display
}
//Show acceleration on axes
if(!(rtiCount % 1000)) // 1 per second
{
Display_ACC();
}
asm WAI; // Wait for something exciting to happen
}
}
/////////////////////////////////////////////////////////////////////////////
// Functions
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////
// RTC - Real Time Clock (0x68)
//////////////////////////////////////////////////
void Demo_RTC() // Function to demonstrate the RTC (based on previous demo)
{
// This demo will use the M41T81S already built into our board and attached
// at address 0x68 (RTC_ADD in header)
RTC_Read dateTime; // Type for getting RTC data (see i2c.h)
int test = 0; // For capturing return values
char reg = 0; // For capturing single byte results
// Test for STOP flag by trying to read SECONDS
// register from the RTC. If the special bit
// 0x80 (STOP, RTC_SECONDS_ST) is set in the return,
// then is not running.
test = I2C0_RegRead(&reg, RTC_ADD, RTC_SECONDS);
if (reg & RTC_SECONDS_ST) // Stopped
{
//Yellow LED means it was stopped.
SWL_SetLEDs(eYellow);
//Ok. Let's start it by rewriting a zero with
//the stop bit reset
reg = 0; //My current seconds are junk, so discarding
I2C0_RegWrite(RTC_ADD, RTC_SECONDS, reg & ~RTC_SECONDS_ST, IIC0_STOP);
}
//Also, if it was halted, that shows up in the AlarmHour register
//as the special bit 0x40 (RTC_AL_HOUR_HT)
//This is probably because the board was shut off or had a power
//failure.
test = I2C0_RegRead(&reg, RTC_ADD, RTC_AL_HOUR);
if (reg & RTC_AL_HOUR_HT) // Halted
{
//Red LED means it was halted.
SWL_SetLEDs(eRed);
//Unhalt it by rewriting the value with the bit reset
I2C0_RegWrite(RTC_ADD, RTC_AL_HOUR, reg & ~RTC_AL_HOUR_HT, IIC0_STOP);
}
//Ok, that's what we had before. Now let's have a bit more fun. Let's create
//a struct with date/time just before midnight on New Year's eve:
dateTime.Year = 22;
dateTime.Month = Dec;
dateTime.MonthDay = 31;
dateTime.Day = Sat;
dateTime.Hours = 23;
dateTime.Minutes = 59;
dateTime.Seconds = 50;
dateTime.HuSeconds = 0;
//Throw that date up to the RTC
UploadRTCDate(dateTime);
//Remainder is in the main loop, displaying current RTC time, date.
}
//Send a date structure to the RTC
void UploadRTCDate(RTC_Read date)
{
//Upload each field into the appropriate register in my RTC
//Note handling the bitfields because BCD
I2C0_RegWrite(RTC_ADD,RTC_HSECONDS,date.HuSeconds % 10 | ((date.HuSeconds /10 ) << 4),IIC0_STOP);
I2C0_RegWrite(RTC_ADD,RTC_SECONDS,date.Seconds % 10 | ((date.Seconds/10 ) << 4),IIC0_STOP);
I2C0_RegWrite(RTC_ADD,RTC_MINUTES,date.Minutes % 10 | ((date.Minutes/10 ) << 4),IIC0_STOP);
I2C0_RegWrite(RTC_ADD,RTC_CHOUR,date.Hours % 10 | ((date.Hours/10) << 4),IIC0_STOP);
I2C0_RegWrite(RTC_ADD,RTC_DAY,date.Day,IIC0_STOP);
I2C0_RegWrite(RTC_ADD,RTC_DATE,date.MonthDay % 10 | ((date.MonthDay/10 ) << 4),IIC0_STOP);
I2C0_RegWrite(RTC_ADD,RTC_MONTH,date.Month % 10 | ((date.Month/10 ) << 4),IIC0_STOP);
I2C0_RegWrite(RTC_ADD,RTC_YEAR,date.Year % 10 | ((date.Year/10 ) << 4),IIC0_STOP);
}
//Retrieve a date structure from the RTC
void DownloadRTCDate(RTC_Read* pdate)
{
char reg = 0;
//Download each field, manipulate, then stuff into appropriate record in date pointed to by pdate.
I2C0_RegRead(&reg,RTC_ADD,RTC_HSECONDS); //Note handling the bitfields
// The arrow notation (pdate->HuSeconds) is shorthand for (*p).HuSeconds
pdate->HuSeconds = ((reg & 0xF0) >> 4) * 10 + (reg & 0x0F); // First nibble * 10 + second nibble
I2C0_RegRead(&reg,RTC_ADD,RTC_SECONDS);
pdate->Seconds = ((reg & 0xF0) >> 4) * 10 + (reg & 0x0F);
I2C0_RegRead(&reg,RTC_ADD,RTC_MINUTES);
pdate->Minutes = ((reg & 0xF0) >> 4) * 10 + (reg & 0x0F);
I2C0_RegRead(&reg,RTC_ADD,RTC_CHOUR);
pdate->Hours = ((reg & 0x30) >> 4) * 10 + (reg & 0x0F); //Note masking to only 2 bits
I2C0_RegRead(&reg,RTC_ADD,RTC_DAY); // No manipulation needed, load straight into struct
pdate->Day = reg;
I2C0_RegRead(&reg,RTC_ADD,RTC_DATE);
pdate->MonthDay = ((reg & 0x30) >> 4) * 10 + (reg & 0x0F); //Note masking to only 2 bits
I2C0_RegRead(&reg,RTC_ADD,RTC_MONTH);
pdate->Month = ((reg & 0xF0) >> 4) * 10 + (reg & 0x0F);
I2C0_RegRead(&reg,RTC_ADD,RTC_YEAR);
pdate->Year = ((reg & 0xF0) >> 4) * 10 + (reg & 0x0F);
}
void DisplayRTCDate(RTC_Read date)
{
char szBuff[21] = {0}; //Buffer with enough room for one LCD line
char month[4]={0};
char day[4]={0};
switch(date.Day)
{
case Sun: strcpy(day,"Sun"); break;
case Mon: strcpy(day,"Mon"); break;
case Tue: strcpy(day,"Tue"); break;
case Wed: strcpy(day,"Wed"); break;
case Thu: strcpy(day,"Thu"); break;
case Fri: strcpy(day,"Fri"); break;
case Sat: strcpy(day,"Sat"); break;
}
switch(date.Month)
{
case Jan: strcpy(month,"Jan"); break;
case Feb: strcpy(month,"Feb"); break;
case Mar: strcpy(month,"Mar"); break;
case Apr: strcpy(month,"Apr"); break;
case May: strcpy(month,"May"); break;
case Jun: strcpy(month,"Jun"); break;
case Jul: strcpy(month,"Jul"); break;
case Aug: strcpy(month,"Aug"); break;
case Sep: strcpy(month,"Sep"); break;
case Oct: strcpy(month,"Oct"); break;
case Nov: strcpy(month,"Nov"); break;
case Dec: strcpy(month,"Dec"); break;
}
//Generate a Date string from the current date
sprintf(szBuff,"%s, %02d %s 20%02d", day, date.MonthDay, month, date.Year);
//Throw it to the LCD
LCD_StringJustify(2,szBuff,eLCD_Just_Center);
//Now put time on the upper segs.
SEG_8D(0,date.Hours);
SEG_8D(2,date.Minutes);
}
//////////////////////////////////////////////////
// ACC - Accelerometer (0x19)
//////////////////////////////////////////////////
void Demo_ACC()
{
//Initialize the device
//0x19 - Device, 0x20 - CTRL_REG1_A,
//0x27 - 10 Hz, Normal power, all three axes
I2C0_RegWrite(0x19,0x20,0x27,IIC0_STOP);
//Settings
//0x23 - CTRL_REG4_A, 0x20 - Cont. Update,
// Big Endian, +/-2g, high res, 4 wire.
I2C0_RegWrite(0x19,0x23,0xC8,IIC0_STOP);
}
void Display_ACC()
{
char status = 0; //Used for getting status of device
int x=0, y=0, z=0; //Holders for accel raw data
float gX = 0, gY = 0, gZ = 0; //Holders for acceleration in g's
int bHi=0, bLo=0; //Temp variables for high and low order nibbles
char szBuff[21] = {0}; //Buffer with enough room for one LCD line
//First, let's see if there is a value ready to be read
//0x27 is STATUS_REG_A -- accelerometer status
I2C0_RegRead(&status, 0x19, 0x27);
//Bit 3 is Data Available
if(!(status & 0b00001000)) // Zero in that bit means not avail
return; //Nothing to do.
//If I got here, I have data
SWL_SetLEDs(eYellow); //Yellow LED will signify an accelerometer update
//Get two bytes of accel, construct signed int (0x28,0x29 are X)
I2C0_RegRead(&bLo,0x19,0x28);
I2C0_RegRead(&bHi,0x19,0x29);
//Construct X (the shift is to take the left-aligned 12 of 16 bits to right-aligned)
//(the shift was determined experimentally, but I have not calibrated the device)
x = (((int)bHi << 8) + bLo) >> 4; //Milli-g's
gX = x/1000.0f;
//Get two bytes of accel, construct signed int (0x2A,0x2B are Y)
I2C0_RegRead(&bLo,0x19,0x2A);
I2C0_RegRead(&bHi,0x19,0x2B);
//Construct Y
y = (((int)bHi << 8) + bLo) >> 4;
gY = y/1000.0f;
//Get two bytes of accel, construct signed int (0x2C,0x2D are Z)
I2C0_RegRead(&bLo,0x19,0x2C);
I2C0_RegRead(&bHi,0x19,0x2D);
//Construct Z
z = (((int)bHi << 8) + bLo) >> 4;
gZ = z/1000.0f;
//Let's convert this into a nice string for the LCD
//(%+4.1f means minimum four characters, one digit after the dp, show sign)
sprintf(szBuff,"(%+4.1f,%+4.1f,%+4.1f)",gX,gY,gZ);
LCD_StringJustify(3,szBuff,eLCD_Just_Center);
SWL_ClearLEDs(eYellow);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment