#include <EFM8LB1.
h>
#include <stdio.h>
#define SYSCLK 72000000L // SYSCLK frequency in Hz
#define BAUDRATE 115200L // Baud rate of UART in bps
#define SARCLK 18000000L
#define LCD_RS P1_7
// #define LCD_RW Px_x // Not used in this code. Connect to GND
#define LCD_E P2_0
#define LCD_D4 P1_3
#define LCD_D5 P1_2
#define LCD_D6 P1_1
#define LCD_D7 P1_0
#define CHARS_PER_LINE 16
#define VDD 3.3035
unsigned char overflow_count;
char _c51_external_startup (void)
{
// Disable Watchdog with 2-byte key sequence
SFRPAGE = 0x00;
WDTCN = 0xDE; //First key
WDTCN = 0xAD; //Second key
#if (SYSCLK == 48000000L)
SFRPAGE = 0x10;
PFE0CN = 0x10; // SYSCLK < 50 MHz.
SFRPAGE = 0x00;
#elif (SYSCLK == 72000000L)
SFRPAGE = 0x10;
PFE0CN = 0x20; // SYSCLK < 75 MHz.
SFRPAGE = 0x00;
#endif
#if (SYSCLK == 12250000L)
CLKSEL = 0x10;
CLKSEL = 0x10;
while ((CLKSEL & 0x80) == 0);
#elif (SYSCLK == 24500000L)
CLKSEL = 0x00;
CLKSEL = 0x00;
while ((CLKSEL & 0x80) == 0);
#elif (SYSCLK == 48000000L)
// Before setting clock to 48 MHz, must transition to 24.5 MHz first
CLKSEL = 0x00;
CLKSEL = 0x00;
while ((CLKSEL & 0x80) == 0);
CLKSEL = 0x07;
CLKSEL = 0x07;
while ((CLKSEL & 0x80) == 0);
#elif (SYSCLK == 72000000L)
// Before setting clock to 72 MHz, must transition to 24.5 MHz first
CLKSEL = 0x00;
CLKSEL = 0x00;
while ((CLKSEL & 0x80) == 0);
CLKSEL = 0x03;
CLKSEL = 0x03;
while ((CLKSEL & 0x80) == 0);
#else
#error SYSCLK must be either 12250000L, 24500000L, 48000000L, or
72000000L
#endif
P0MDOUT |= 0x10; // Enable UART0 TX as push-pull output
XBR0 = 0x01; // Enable UART0 on P0.4(TX) and P0.5(RX)
XBR1 = 0X00;
XBR2 = 0x40; // Enable crossbar and weak pull-ups
#if (((SYSCLK/BAUDRATE)/(2L*12L))>0xFFL)
#error Timer 0 reload value is incorrect because
((SYSCLK/BAUDRATE)/(2L*12L))>0xFF
#endif
// Configure Uart 0
SCON0 = 0x10;
CKCON0 |= 0b_0000_0000 ; // Timer 1 uses the system clock divided by 12.
TH1 = 0x100-((SYSCLK/BAUDRATE)/(2L*12L));
TL1 = TH1; // Init Timer1
TMOD &= ~0xf0; // TMOD: timer 1 in 8-bit auto-reload
TMOD |= 0x20;
TR1 = 1; // START Timer1
TI = 1; // Indicate TX0 ready
return 0;
}
// Uses Timer3 to delay <us> micro-seconds.
void Timer3us(unsigned char us)
{
unsigned char i; // usec counter
// The input for Timer 3 is selected as SYSCLK by setting T3ML (bit 6) of
CKCON0:
CKCON0|=0b_0100_0000;
TMR3RL = (-(SYSCLK)/1000000L); // Set Timer3 to overflow in 1us.
TMR3 = TMR3RL; // Initialize Timer3 for first overflow
TMR3CN0 = 0x04; // Sart Timer3 and clear overflow flag
for (i = 0; i < us; i++) // Count <us> overflows
{
while (!(TMR3CN0 & 0x80)); // Wait for overflow
TMR3CN0 &= ~(0x80); // Clear overflow indicator
}
TMR3CN0 = 0 ; // Stop Timer3 and clear overflow flag
}
void waitms (unsigned int ms)
{
unsigned int j;
unsigned char k;
for(j=0; j<ms; j++)
for (k=0; k<4; k++) Timer3us(250);
}
/*
void TIMER0_Init(void)
{
TR0=0; // Stop Timer/Counter 0
TR0 = 0;
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = 0;
TL0 = 0;
TF0 = 0;
}
*/
void LCD_pulse (void)
{
LCD_E=1;
Timer3us(40);
LCD_E=0;
}
void LCD_byte (unsigned char x)
{
// The accumulator in the C8051Fxxx is bit addressable!
ACC=x; //Send high nible
LCD_D7=ACC_7;
LCD_D6=ACC_6;
LCD_D5=ACC_5;
LCD_D4=ACC_4;
LCD_pulse();
Timer3us(40);
ACC=x; //Send low nible
LCD_D7=ACC_3;
LCD_D6=ACC_2;
LCD_D5=ACC_1;
LCD_D4=ACC_0;
LCD_pulse();
}
void WriteData (unsigned char x)
{
LCD_RS=1;
LCD_byte(x);
waitms(2);
}
void WriteCommand (unsigned char x)
{
LCD_RS=0;
LCD_byte(x);
waitms(5);
}
void LCD_4BIT (void)
{
LCD_E=0; // Resting state of LCD's enable is zero
// LCD_RW=0; // We are only writing to the LCD in this program
waitms(20);
// First make sure the LCD is in 8-bit mode and then change to 4-bit mode
WriteCommand(0x33);
WriteCommand(0x33);
WriteCommand(0x32); // Change to 4-bit mode
// Configure the LCD
WriteCommand(0x28);
WriteCommand(0x0c);
WriteCommand(0x01); // Clear screen command (takes some time)
waitms(20); // Wait for clear screen command to finsih.
}
void LCDprint(char * string, unsigned char line, bit clear)
{
int j;
WriteCommand(line==2?0xc0:0x80);
waitms(5);
for(j=0; string[j]!=0; j++) WriteData(string[j]);// Write the message
if(clear) for(; j<CHARS_PER_LINE; j++) WriteData(' '); // Clear the rest of
the line
}
int getsn (char * buff, int len)
{
int j;
char c;
for(j=0; j<(len-1); j++)
{
c=getchar();
if ( (c=='\n') || (c=='\r') )
{
buff[j]=0;
return j;
}
else
{
buff[j]=c;
}
}
buff[j]=0;
return len;
}
void InitADC (void)
{
SFRPAGE = 0x00;
ADEN=0; // Disable ADC
ADC0CN1=
(0x2 << 6) | // 0x0: 10-bit, 0x1: 12-bit, 0x2: 14-bit
(0x0 << 3) | // 0x0: No shift. 0x1: Shift right 1 bit. 0x2: Shift right 2
bits. 0x3: Shift right 3 bits.
(0x0 << 0) ; // Accumulate n conversions: 0x0: 1, 0x1:4, 0x2:8, 0x3:16,
0x4:32
ADC0CF0=
((SYSCLK/SARCLK) << 3) | // SAR Clock Divider. Max is 18MHz. Fsarclk =
(Fadcclk) / (ADSC + 1)
(0x0 << 2); // 0:SYSCLK ADCCLK = SYSCLK. 1:HFOSC0 ADCCLK = HFOSC0.
ADC0CF1=
(0 << 7) | // 0: Disable low power mode. 1: Enable low power mode.
(0x1E << 0); // Conversion Tracking Time. Tadtk = ADTK / (Fsarclk)
ADC0CN0 =
(0x0 << 7) | // ADEN. 0: Disable ADC0. 1: Enable ADC0.
(0x0 << 6) | // IPOEN. 0: Keep ADC powered on when ADEN is 1. 1: Power
down when ADC is idle.
(0x0 << 5) | // ADINT. Set by hardware upon completion of a data
conversion. Must be cleared by firmware.
(0x0 << 4) | // ADBUSY. Writing 1 to this bit initiates an ADC
conversion when ADCM = 000. This bit should not be polled to indicate when a
conversion is complete. Instead, the ADINT bit should be used when polling for
conversion completion.
(0x0 << 3) | // ADWINT. Set by hardware when the contents of
ADC0H:ADC0L fall within the window specified by ADC0GTH:ADC0GTL and
ADC0LTH:ADC0LTL. Can trigger an interrupt. Must be cleared by firmware.
(0x0 << 2) | // ADGN (Gain Control). 0x0: PGA gain=1. 0x1: PGA
gain=0.75. 0x2: PGA gain=0.5. 0x3: PGA gain=0.25.
(0x0 << 0) ; // TEMPE. 0: Disable the Temperature Sensor. 1: Enable the
Temperature Sensor.
ADC0CF2=
(0x0 << 7) | // GNDSL. 0: reference is the GND pin. 1: reference is the
AGND pin.
(0x1 << 5) | // REFSL. 0x0: VREF pin (external or on-chip). 0x1: VDD
pin. 0x2: 1.8V. 0x3: internal voltage reference.
(0x1F << 0); // ADPWR. Power Up Delay Time. Tpwrtime = ((4 * (ADPWR +
1)) + 2) / (Fadcclk)
ADC0CN2 =
(0x0 << 7) | // PACEN. 0x0: The ADC accumulator is over-written. 0x1:
The ADC accumulator adds to results.
(0x0 << 0) ; // ADCM. 0x0: ADBUSY, 0x1: TIMER0, 0x2: TIMER2, 0x3:
TIMER3, 0x4: CNVSTR, 0x5: CEX5, 0x6: TIMER4, 0x7: TIMER5, 0x8: CLU0, 0x9: CLU1,
0xA: CLU2, 0xB: CLU3
ADEN=1; // Enable ADC
}
void InitPinADC (unsigned char portno, unsigned char pinno)
{
unsigned char mask;
mask=1<<pinno;
SFRPAGE = 0x20;
switch (portno)
{
case 0:
P0MDIN &= (~mask); // Set pin as analog input
P0SKIP |= mask; // Skip Crossbar decoding for this pin
break;
case 1:
P1MDIN &= (~mask); // Set pin as analog input
P1SKIP |= mask; // Skip Crossbar decoding for this pin
break;
case 2:
P2MDIN &= (~mask); // Set pin as analog input
P2SKIP |= mask; // Skip Crossbar decoding for this pin
break;
default:
break;
}
SFRPAGE = 0x00;
}
unsigned int ADC_at_Pin(unsigned char pin)
{
ADC0MX = pin; // Select input from pin
ADINT = 0;
ADBUSY = 1; // Convert voltage at the pin
while (!ADINT); // Wait for conversion to complete
return (ADC0);
}
float Volts_at_Pin(unsigned char pin)
{
return ((ADC_at_Pin(pin)*VDD)/0b_0011_1111_1111_1111);
}
float get_halfperiod_P0_1()
{
// Measure half period at pin P0.1 using timer 0
unsigned int overflow_count=0;
TR0 = 0;
TMOD&=0b_1111_0000; // Set the bits of Timer/Counter 0 to zero
TMOD|=0b_0000_0001; // Timer/Counter 0 used as a 16-bit timer
TR0=0; // Stop Timer/Counter 0
TH0=0;
TL0=0; // Reset the timer
TF0=0;
while (P0_1==1); // Wait for the signal to be zero
while (P0_1==0); // Wait for the signal to be one
TR0=1; // Start timing
while (P0_1==1); // Wait for the signal to be zero
{
if(TF0)
{
TF0=0;
overflow_count++;
}
}
TR0=0; // Stop timer 0
// [TH0,TL0] is half the period in multiples of 12/CLK, so:
//return (TH0*0x100+TL0)*2; // Assume Period is unsigned int
return ((overflow_count*65536.0+TH0*256.0+TL0)*(12.0/SYSCLK)*1000000L);
float get_halfperiod_P0_2()
{
unsigned int overflow_count=0;
TR0 = 0;
TMOD&=0b_1111_0000; // Set the bits of Timer/Counter 0 to zero
TMOD|=0b_0000_0001; // Timer/Counter 0 used as a 16-bit timer
TR0=0; // Stop Timer/Counter 0
TH0=0;
TL0=0; // Reset the timer
TF0=0;
while (P0_2); // Wait for the signal to be zero
while (!P0_2); // Wait for the signal to be one
TR0=1; // Start timing
while (P0_2); // Wait for the signal to be zero
{
if(TF0)
{
TF0=0;
overflow_count++;
}
}
TR0=0; // Stop timer 0
// [TH0,TL0] is half the period in multiples of 12/CLK, so:
//return (TH0*0x100+TL0)*2; // Assume Period is unsigned int
return ((overflow_count*65536.0+TH0*256.0+TL0)*(12.0/SYSCLK)*1000000L);
float getPeriodDiff(float period)
{
unsigned char overflow_count = 0;
// initalize timer 0
TR0 = 0;
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = 0;
TL0 = 0;
TF0 = 0;
// goto start of first square wave
while (P0_1); //Let's make Digital 0 is ref
while (!P0_1); // Let's make Digital 1 is test
// measure until the start of second signal
if (!P0_2)
{
// if the other signal is off when we start, just need to wait for the next
rising edge
TR0 = 1;
while (!P0_2)
{
if (TF0)
{
TF0 = 0;
overflow_count++;
}
}
TR0 = 0;
return (overflow_count*65536.0+TH0*256.0+TL0)*(12.0/SYSCLK)*1000000L;
} else {
// if the other signal is on when we start, we need to measure next time
when it goes HIGH
TR0 = 1;
while (P0_2)
{
if (TF0)
{
TF0 = 0;
overflow_count++;
}
}
while (!P0_2) {
if (TF0) {
TF0 = 0;
overflow_count++;
}
}
TR0 = 0;
return ((overflow_count*65536.0+TH0*256.0+TL0)*(12.0/SYSCLK)*1000000L -
(period));
}
void main (void)
{
char buff[17];
float v[4];
float halfperiod_ref=0;
float halfperiod_test=0;
float period_diff=0;
// Configure the LCD
LCD_4BIT();
//TIMER0_Init();
waitms(500); // Give PuTTy a chance to start before sending
printf("\x1b[2J"); // Clear screen using ANSI escape sequence.
printf ("ADC test program\n"
"File: %s\n"
"Compiled: %s, %s\n\n",
__FILE__, __DATE__, __TIME__);
InitPinADC(2, 2); // Configure P2.2 as analog input
InitPinADC(2, 3); // Configure P2.3 as analog input
InitPinADC(2, 4); // Configure P2.4 as analog input
InitPinADC(2, 5); // Configure P2.5 as analog input
InitADC();
while(1)
{
// Read 14-bit value from the pins configured as analog inputs
v[0] = Volts_at_Pin(QFP32_MUX_P2_2);
v[1] = Volts_at_Pin(QFP32_MUX_P2_3);
v[2] = Volts_at_Pin(QFP32_MUX_P2_4);
v[3] = Volts_at_Pin(QFP32_MUX_P2_5);
r", v[0], v[1], v[2], v[3]);
sprintf(buff,"V1=%.2f V2=%.2f",v[0],v[1]);
LCDprint(buff,1,1);
waitms(500);
halfperiod_ref = get_halfperiod_P0_1();
waitms(500);
halfperiod_test = get_halfperiod_P0_2();
waitms(500);
period_diff = getPeriodDiff(2*(halfperiod_ref));
if (halfperiod_test/1000 > 1 && halfperiod_ref/1000 >1)
{
printf("\nperiod/2 tes: %.3f\r\n", halfperiod_test/1000);
printf("\nperiod/2 ref: %.3f\r\n", halfperiod_ref/1000);
printf("\ndif: %.3f\r\n", period_diff/(1000));
printf("Phase = %.2fdeg", (-360.0 * period_diff / (2.0 *
halfperiod_ref))+1.0);
sprintf(buff,"phase= %.2f ", (-360.0 * period_diff / (2.0 *
halfperiod_ref))+2.0);
LCDprint(buff,2,1);
}
}
}