Last active
May 3, 2020 07:59
-
-
Save Electronza/e2c10fda6d1edcf66d65cfe3b4e5a4ae to your computer and use it in GitHub Desktop.
Lightning detection using AS3935 and PIC18F4520
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Project page | |
# https://medium.com/electronza/lightning-detection-using-as3935-and-pic18f4520-microcontroller-695bbfb9dfc5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Project name: | |
Thunder_Test (Demonstration of the AS3935 Thunder Click) | |
* Revision History: | |
20150416: | |
- alpha release; | |
* Description: | |
This code demonstrates how to use AS3935 Thunder Click | |
using a PICDEM 2 Plus board w/ PIC18F4520 microcontroller. | |
* Test configuration: | |
MCU: PIC18F4520 | |
http://ww1.microchip.com/downloads/en/DeviceDoc/39631E.pdf | |
dev.board: PICDEM 2 Plus | |
http://ww1.microchip.com/downloads/en/DeviceDoc/41584B.pdf | |
Oscillator: External 4MHz canned oscillator | |
Ext. Modules: Character Lcd 2x16 | |
Thunder Click board from Mikroelektronika | |
SW: mikroC PRO for PIC | |
http://www.mikroe.com/mikroc/pic/ | |
* NOTES: | |
- On PICDEM 2 Plus the LCD is enabled by setting pin D7 high | |
- THE LCD R/W pin, which is connected to pin D5, must be set low | |
*/ | |
// Lcd module connections | |
sbit LCD_D4 at LATD0_bit; | |
sbit LCD_D5 at LATD1_bit; | |
sbit LCD_D6 at LATD2_bit; | |
sbit LCD_D7 at LATD3_bit; | |
sbit LCD_RS at LATD4_bit; | |
sbit LCD_RW at LATD5_bit; | |
sbit LCD_EN at LATD6_bit; | |
sbit LCD_PW at LATD7_bit; | |
sbit LCD_D4_Direction at TRISD0_bit; | |
sbit LCD_D5_Direction at TRISD1_bit; | |
sbit LCD_D6_Direction at TRISD2_bit; | |
sbit LCD_D7_Direction at TRISD3_bit; | |
sbit LCD_RS_Direction at TRISD4_bit; | |
sbit LCD_RW_Direction at TRISD5_bit; | |
sbit LCD_EN_Direction at TRISD6_bit; | |
sbit LCD_PW_Direction at TRISD7_bit; | |
// End LCD module connections | |
#define INDOOR 0x12 | |
#define OUTDOOR 0x0E | |
//long int value; | |
//short int value1; | |
unsigned short int tmp1; | |
long int tmp2; | |
int message_count = 0; | |
char msg_str[4]; | |
char Out_Text[11]; | |
int Total_Lightings_Detected = 0; | |
unsigned short interrupt_detected = 0; | |
unsigned short interrupt_source, buffer; | |
char i; | |
// SPI module conections | |
sbit Thunder_CS at LATC2_bit; | |
sbit Thunder_CS_Direction at TRISC2_bit; | |
// Interrupt pin | |
// sbit Thunder_INT at LATB0_bit; | |
sbit Thunder_INT_Direction at TRISB0_bit; | |
// Now we set the SPI communication | |
// NB! chip uses SPI MODE1 | |
// SPI.setDataMode(SPI_MODE1); | |
// NB! max SPI clock speed that chip supports is 2MHz, | |
// but never use 500kHz, because that will cause interference | |
// to lightning detection circuit | |
/******************************************************************************* | |
* Interrupt External INT1 | |
*******************************************************************************/ | |
void interrupt(){ // Interrupt rutine | |
if(INT0IF_bit == 1 ) { // Checks Receive Interrupt Flag bit | |
interrupt_detected = 1; // Set local interrupt flag | |
INT0IF_bit = 0; // Clear Interrupt Flag | |
} | |
} | |
// Those are the same functions from the PIC32 example from Mikroe | |
/******************************************************************************* | |
* Function Thunder_Write(unsigned short address, unsigned short data1) | |
* ------------------------------------------------------------------------------ | |
* Overview: Function writes desired byte into specified register address | |
* Input: register address, byte | |
* Output: Nothing | |
*******************************************************************************/ | |
void Thunder_Write(unsigned short address, unsigned short data1) { | |
address.B7 = 0; | |
address.B6 = 0; | |
Thunder_CS = 0; | |
SPI_Write(address); | |
SPI_Write(data1); | |
Thunder_CS = 1; | |
} | |
/******************************************************************************* | |
* Function Thunder_Read(unsigned short address) | |
* ------------------------------------------------------------------------------ | |
* Overview: Function reads byte from specified address | |
* Input: register address | |
* Output: desired byte | |
*******************************************************************************/ | |
unsigned short Thunder_Read(unsigned short address) { | |
unsigned short tmp = 0; | |
address.B7 = 0; | |
address.B6 = 1; | |
Thunder_CS = 0; | |
SPI_Write(address); | |
tmp = SPI_Read(buffer); | |
Thunder_CS = 1; | |
return tmp; | |
} | |
/******************************************************************************* | |
* Function Thunder_Init() | |
* ------------------------------------------------------------------------------ | |
* Overview: Function Initializes Thunder chip | |
* Input: register address, data | |
* Output: Nothing | |
*******************************************************************************/ | |
void Thunder_Init(void) { | |
unsigned short temp; | |
Thunder_CS_Direction = 0; // Set CS directions as output | |
Thunder_CS = 1; // Set CS to idle | |
Thunder_Write(0x3C, 0x96); // set all registers in default mode | |
Delay_ms(2); | |
Thunder_Write(0x3D, 0x96); // calibrate internal oscillator | |
Delay_ms(2); | |
temp = Thunder_Read(0x00) & 0xC1; | |
Thunder_Write(0x00, ((INDOOR << 1) | temp)); // set to indoor | |
Delay_ms(2); | |
temp = Thunder_Read(0x01) & 0x80; | |
Thunder_Write(0x01, 0x21 | temp); // set NFL and WDTreshold (was 44) | |
Delay_ms(2); | |
temp = Thunder_Read(0x02) & 0x80; // clear statistics, min number of ligtning, spike rejection | |
Thunder_Write(0x02, 0x42 | temp); | |
} | |
/******************************************************************************* | |
* Function Thunder_Calibrate() | |
* ------------------------------------------------------------------------------ | |
* Overview: Function aclibrates Thunder chip | |
* Input: Nothing | |
* Output: returns 1 if calibrated, 0 otherwise | |
*******************************************************************************/ | |
void Thunder_Calibrate(void) { | |
// The ideea is to output the LCO frequency on IRQ pin | |
// It should be as close as possible to 500KHz (within +/- 3.5%) | |
// Otherwise sensitivity is reduced | |
// declaration of some variables to be used within the function | |
unsigned short functmp; | |
long int functmp1; | |
long int bestcal; | |
long int current_cal; | |
unsigned short bestcap; | |
// needed for debug purposes (LCD output of calibration information) | |
char cal_str[4]; | |
char cal_str1[8]; | |
// Frequency Division Ratio for the Antenna Tuning is 1:16 | |
// this means we have to set REG0x03[7] = 0, REG0x03[6] = 0 | |
// see page 23 of AS3935 datasheet | |
// To do this we first read the actual value in REG0x00 | |
// bitmask REG0x03[7] = 0, REG0x03[6] = 0 | |
// then write back values | |
functmp = Thunder_Read(0x03) & 0x3F; | |
Thunder_Write(0x03, functmp); | |
Delay_ms(2); | |
// Here we start | |
Lcd_Cmd(_LCD_CLEAR); // Clear display | |
Lcd_Out(1,1, "Calibrating..."); | |
Delay_ms(500); // enough time to see the message | |
// For each value of the calibration capacitors we measure the frequency on INT pin | |
// over 100ms period of time. The closest to 3125, the better. | |
bestcal = 110; // we set a value out of range | |
for(i=0; i<16; i++) { // We swa | |
functmp = 0x80 + i; | |
Thunder_Write(0x08, functmp); | |
Delay_ms(50); // wait to stabilize | |
// We print the capacitor value on lcd | |
Lcd_Cmd(_LCD_CLEAR); // Clear display | |
Lcd_Out(1,1, "C = "); | |
IntToStr(i, cal_str) ; | |
Lcd_Out(1,4, cal_str); | |
// Now we measure the frequency | |
functmp1 = 0; // we use functmp1 to count the pulses | |
// We set Timer1 to measure for 100ms | |
// see http://www.libstock.com/projects/view/398/timer-calculator | |
// Expected pulse count value is 3125 (500000 HZ: 16 = 31250 pulses/sec) | |
INTCON.GIE = 1; // enable interrupts | |
INTCON.INT0IE = 1; // enable INT1 interrupt | |
T1CON = 0x11; // 1;2 prescaler | |
T1CON.T1RUN = 1; | |
TMR1H = 0x3C; | |
TMR1L = 0xB0; | |
TMR1IF_bit = 0; // interrupt cleared, timer started | |
while(TMR1IF_bit == 0) { | |
if (interrupt_detected ==1) { | |
functmp1++; | |
interrupt_detected = 0; | |
} | |
} | |
INTCON.GIE = 0; // disable interrupts de ce am facut asta??? | |
// Nice print of frequency | |
Lcd_Out(2,1, "f = "); | |
IntToStr(functmp1, cal_str1) ; | |
Lcd_Out(2,4, cal_str1); | |
delay_ms(100); // time to see what is on the LCD | |
current_cal = abs (functmp1 - 3125); | |
if (current_cal < bestcal) { | |
bestcal = current_cal; | |
bestcap = i; | |
} | |
} | |
// write best value in the capacitor register | |
// we also stop the LCO frequency output and go to normal mode | |
Thunder_Write(0x08, bestcap); | |
// exit flag to stop the program if AS3935 if it's not calibrated | |
if (bestcal > 109) { // error greater than 3.5% | |
Lcd_Cmd(_LCD_CLEAR); // Clear display | |
Lcd_Out(1,1, "Calibration"); | |
Lcd_Out(2,1, "error..."); | |
while(1){ | |
// stop here if not calibrated | |
}; | |
} | |
else { | |
Lcd_Cmd(_LCD_CLEAR); // Clear display | |
Lcd_Out(1,1, "Calibrated"); | |
ShortToStr(bestcap, cal_str); | |
Lcd_Out(2,1, "C = "); | |
Lcd_Out(2,4, cal_str); | |
delay_ms(500); | |
Lcd_Cmd(_LCD_CLEAR); // Clear display | |
Thunder_Write(0x03, 0x00); | |
delay_ms(2) ; | |
bestcap = Thunder_Read(0x03); | |
delay_ms(2) ; | |
} | |
} | |
/******************************************************************************* | |
* Function Thunder_Read_Energy() | |
* ------------------------------------------------------------------------------ | |
* Overview: Function reads energy of detected thunder | |
* Input: Nothing | |
* Output: Measured result | |
*******************************************************************************/ | |
long int Thunder_Read_Energy() { | |
unsigned short low_byte, mid_byte; | |
long int Out_thunder_energy; | |
Out_thunder_energy = Thunder_Read(0x06) & 0x1F; | |
mid_byte = Thunder_Read(0x05); | |
low_byte = Thunder_Read(0x04); | |
Out_thunder_energy = (Out_thunder_energy << 8); | |
Out_thunder_energy = (Out_thunder_energy | mid_byte); | |
Out_thunder_energy = (Out_thunder_energy << 8); | |
Out_thunder_energy = (Out_thunder_energy | low_byte); | |
return Out_thunder_energy; | |
} | |
/******************************************************************************* | |
* Function Thunder_Read_distance() | |
* ------------------------------------------------------------------------------ | |
* Overview: Function reads distance from detected thunder | |
* Input: Nothing | |
* Output: Measured result | |
*******************************************************************************/ | |
unsigned int Thunder_Read_distance() { | |
int Out_thunder_distance; | |
Out_thunder_distance = Thunder_Read(0x07) & 0x3F; | |
return Out_thunder_distance; | |
} | |
void main(){ | |
Thunder_INT_Direction = 1; // B0 is input | |
// LCD inintialize | |
LCD_PW_Direction = 0; | |
LCD_RW_Direction = 0; | |
LCD_RW = 0; | |
LCD_PW = 1; | |
Delay_ms(200); | |
// We must init the LCD before performing AS3935 calibrartion as the | |
// calibration routine uses LCD. | |
Lcd_Init(); | |
Lcd_Cmd(_LCD_CLEAR); // Clear display | |
Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off | |
// LCD is now ready | |
// Initializing SPI communication | |
SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4, // Initialize PIC as master | |
_SPI_DATA_SAMPLE_END, // Data sampled at end | |
_SPI_CLK_IDLE_LOW, | |
_SPI_HIGH_2_LOW); | |
// SPI Clock is 1MHz, SPI MODE 1 | |
// Maximum SPI clock for AS3935 is 2MHz. Avoid using 500KHz clock as it | |
// will intefere with the lightning detection circuits | |
Thunder_Init(); | |
Thunder_Calibrate (); | |
// Power up the sensor | |
// not needed anymore, sensor is powered up by the Thunder_init() routine | |
// Thanks Exploit for pointing it out, see comments below | |
// tmp1 = Thunder_Read(0x00) & 0xFE; | |
// Thunder_Write(0x00, tmp1); | |
// Delay_ms(2); | |
// Now we show some working parameters | |
// Disturbers enabled, or not? | |
tmp1 = Thunder_Read(0x00) & 0x20; | |
Lcd_Cmd(_LCD_CLEAR); | |
//Delay_ms(10); | |
Lcd_Out(1,1, "Disturbers"); | |
//ShortToStr(tmp1, msg_str); | |
if (tmp1 == 0x20) | |
Lcd_Out(2,1, "enabled"); | |
else | |
Lcd_Out(2,1, "disabled"); | |
delay_ms(1000); | |
//Are we in indoor, or outdoor mode | |
tmp1 = Thunder_Read(0x00) & 0x3E; | |
Lcd_Cmd(_LCD_CLEAR); | |
Lcd_Out(1,1, "Sensor is set to"); | |
tmp1 = tmp1>>1; | |
if (tmp1 == INDOOR) | |
Lcd_Out(2,1, "indoor mode"); | |
else | |
Lcd_Out(2,1, "outdoor mode"); | |
delay_ms(1000); | |
// Change noise level to 3 ( I want | |
tmp1 = Thunder_Read(0x01) & 0x8F; | |
tmp1 = tmp1 | 0x20; | |
Thunder_Write(0x01, tmp1); | |
//Showing noise level | |
tmp1 = Thunder_Read(0x01) & 0x70; | |
Lcd_Cmd(_LCD_CLEAR); | |
Lcd_Out(1,1, "Noise level"); | |
ShortToStr(tmp1>>4, msg_str); | |
Lcd_Out(2,1, msg_str); | |
delay_ms(1000); | |
//Show watchdog treshold | |
tmp1 = Thunder_Read(0x01) & 0x0F; | |
Lcd_Cmd(_LCD_CLEAR); | |
Lcd_Out(1,1, "Watchdog set to"); | |
ShortToStr(tmp1, msg_str); | |
Lcd_Out(2,1, msg_str); | |
delay_ms(1000); | |
//Show spike rejection | |
tmp1 = Thunder_Read(0x02) & 0x0F; | |
Lcd_Cmd(_LCD_CLEAR); | |
Lcd_Out(1,1, "Spike rejection"); | |
ShortToStr(tmp1, msg_str); | |
Lcd_Out(2,1, msg_str); | |
delay_ms(1000); | |
// We are now ready to go with the main routine | |
// We reenable the INT0 interrupt | |
INTCON.GIE = 1; // enable interrupts | |
INTCON.INT0IE = 1; // enable INT0 interrupt | |
INTCON2.INTEDG0 = 1; // interrupt on raising edge (mandatory) | |
// This will clear any AS3935 interrupts | |
// Don't worry. AS3935 issues a lot of interrupts | |
delay_ms(2); | |
interrupt_source = Thunder_Read(0x03); // reading reg 0x03 clears interrupt | |
interrupt_detected = 0; // this will clear any leftover interrupts | |
while(1){ | |
// this prints a nice message counter | |
ShortToStr(message_count, msg_str); | |
Lcd_Cmd(_LCD_CLEAR); | |
Lcd_Out(1,1, msg_str); | |
message_count++; | |
if (message_count == 100) | |
message_count=0; | |
if (interrupt_detected == 0){ // nothing happens | |
Lcd_Out(1,6, "Waiting..."); | |
Delay_ms(150); | |
} | |
else { | |
// We read the AS3935 registers to see what caused the interrupt | |
delay_ms(2); // 2ms delay between interrupt and reading of register 0x03 | |
// Time to read the 0x03 register which stores interupt codes | |
interrupt_source = Thunder_Read(0x03) & 0x0F; | |
if (interrupt_source = 0x01) // is noise | |
Lcd_Out(1,6, "Just noise"); | |
if (interrupt_source = 0x04) // is disturber | |
Lcd_Out(1,6, "Disturbers"); | |
if (interrupt_source = 0x08){ // is lightning | |
Lcd_Out(1,6, "Lightning "); | |
// now we get the distance to the lightning event | |
Lcd_Out(2,1, "Dist:"); | |
tmp1 = Thunder_Read_distance(); | |
ShortToStr(tmp1, msg_str); | |
Lcd_Out(2,6, msg_str); | |
} | |
interrupt_detected = 0; | |
Delay_ms(500); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment