Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //********************************************************************
- // Data Acquisition Code for PIC18F4580 Microchip Microprocessor.
- // Controls acquiring data from sensors, writing the data to an SD
- // card for storage, and communicating with a Zigbee IEEE802.15
- // wireless module to send the data to a computer base station for near
- // real-time monitoring.
- //********************************************************************
- //* T O - D O L I S T **********************************************
- //
- // -implement interrupt service functions
- // -Set flags and deal with them in main
- // -rewrite main to handle the flags
- // -if send buffer is sufficiently empty, fill it with more data
- // -else, start loop over
- //********************************************************************
- //* D I S C U S S I O N **********************************************
- // Transmit interrupt cannot be enabled right away. If there is no
- // data to send then the transmit interrupt will always be active,
- // getting us caught in an interrupt loop.
- //
- // Solution: Enable the interrupt each time data is added to the send
- // buffer, and disable the interrupt after the last byte has been sent
- // (curpos == endpos)
- //********************************************************************
- /** C O N F I G U R A T I O N B I T S ******************************/
- //Configuration bits set in MPLAB for now, will put them in code later
- /** I N C L U D E S **************************************************/
- #include "p18f4580.h"
- #include "delays.h"
- #include "stdio.h"
- /** D E F I N E S ****************************************************/
- // NAME REGISTER.BIT
- #define TRMT TXSTAbits.TRMT
- //#define BRGH TXSTAbits.BRGH
- //#define BRG16 BAUDCONbits.BRG16
- #define SPEN RCSTA.7
- #define CREN RCSTAbits.CREN
- #define ADPins 5
- #define USED_AD_PINS 5
- #define LED_ERROR PORTCbits.RC2
- /** S C R E E N D E F I N E S **************************************/
- #define E LATCbits.LATC5
- #define RW LATCbits.LATC4
- #define RS LATCbits.LATC3
- #define DATA PORTD
- /** P R O T O T Y P E S **********************************************/
- void sdINIT();
- void uartINIT();
- void sdWritePacket(unsigned char packetType, unsigned char data);
- void sdHandshake(static unsigned char signalID);
- void rxEnable();
- void adINIT();
- void adCalculate();
- void pinSwitch(unsigned char pin);
- void lcdINIT();
- void writeScreen(unsigned char data[], int length);
- unsigned char dataSend(unsigned char arr[]);
- void ERRORCODE(int x);
- void alertRED();
- void alertGREEN();
- void prepareDataToSend(static char signalID, static unsigned int time, static unsigned char bufferInput);
- void addToBuffer(static unsigned char bufferInput);
- char numToASCII(static unsigned char num);
- void *makeString(static unsigned int toString);
- char makeDigit(static unsigned int *digit, static unsigned int base);
- void globalInterruptINIT();
- void RXInterruptINIT();
- void TXInterruptINIT();
- /** D A T A B U F F E R S A N D F L A G S **********************/
- #pragma udata buffer // buffer is declared to be the 5th bank.
- static unsigned char TXBuffer[128]; //This space is allocated just for the stuff listed here.
- static unsigned char bufferInput;
- static unsigned char signalID;
- static unsigned char num;
- static unsigned int *digit;
- static unsigned int base;
- static unsigned int toString;
- #pragma idata
- static unsigned char TXCurrentPos = 0x00; //after sending a byte, increment the curpos by 1
- static unsigned char TXEndPos = 0x00; //after adding a byte to the buffer, increment the endpos
- static unsigned int i = 0x00;
- static unsigned char ByteReceived = 0x00; // Set in the interrupt so it can be passed to main
- // After main reads it, main should clear it
- static unsigned char countdown = 0x01; //Initally set to '1' so the auto baud byte gets sent.
- static unsigned char waitingForACK = 0;
- static unsigned char currentlySendingHandshake = 0; //If 1 then we're sending handshake
- //else we're sending data
- static unsigned int time = 0x0000;
- /** D E C L A R A T I O N S *****************************************
- //creates a two dimensional array used to store the pins[pinA][data1][data2]
- //and their AD values. [pinB][data1][data2]
- unsigned char DATA_ARRAY[USED_AD_PINS][3];
- //size counts the size of the array we will be sending to the SD card
- //each time we do. This will be used in the handshake to tell the SD
- // module the number of bytes to expect. If we make any changes to the
- // packet scheme this will need to be changed.
- unsigned char size = USED_AD_PINS * 3;
- **********************************************************************/
- /** I N T E R R U P T S **********************************************/
- #pragma interrupt InterruptVectorHigh
- void InterruptVectorHigh(void){
- if(PIR1bits.RCIF == 1){
- ByteReceived = RCREG;
- if(waitingForACK == 1){ //if we're waiting for ACK..
- if(ByteReceived == 0x06){ //and we get ACK
- currentlySendingHandshake = ~currentlySendingHandshake;
- if(currentlySendingHandshake == 1){
- countdown = 13; //Send the 13 bytes needed for the handshake.
- }
- if(currentlySendingHandshake == 0){
- countdown = 13; //Send the thirteen bytes we want to write the sd card.
- }
- if((TXEndPos - TXCurrentPos) > 0){
- PIE1bits.TXIE = 1; //Finally reenable TX INT.
- } //if there is data to send
- waitingForACK = 0; //we're no longer waiting for ACK
- }
- }
- }
- else if (PIR1bits.TXIF == 1){
- if((TXEndPos - TXCurrentPos) > 0){ // if there is data in the buffer
- TXREG = TXBuffer[TXCurrentPos]; // send next byte
- TXCurrentPos = TXCurrentPos + 0x01; // update to the new position
- if(TXCurrentPos == 128){
- TXCurrentPos = 0;
- }
- countdown--;
- }
- if(countdown == 0){
- waitingForACK = 1; //we're now waiting for ack
- PIE1bits.TXIE = 0; //stop sending data until we get ack
- }
- if((TXEndPos - TXCurrentPos) == 0){
- PIE1bits.TXIE = 0; // If we run out of data in the buffer
- // pause TX
- }
- }
- }
- #pragma interruptlow InterruptVectorLow
- void InterruptVectorLow(void){
- }
- /** M A I N ***********************************************************/
- #pragma code
- void main (void){
- //Reset sd card module.
- TRISD = 0x00;
- LATDbits.LATD3 = 0; // Pull RD3 'low', which will activate the reset on the sd card module.
- Delay10KTCYx(10);
- LATDbits.LATD3 = 1; // Pull RD3 'high', activating the sd card module.
- uartINIT();
- rxEnable();
- globalInterruptINIT();
- TXInterruptINIT();
- RXInterruptINIT();
- Delay10KTCYx(100); // Delay a while to give the SD card some time
- sdINIT(); // Send the auto baud bit.
- //Loop to add data to the buffer. Increments the time and
- //signal data so we can make sure everything is getting
- //written to the sd card.
- bufferInput = 0x0000;
- for(time; time < 0x004; time++){
- prepareDataToSend(0x10, time, bufferInput);
- bufferInput = bufferInput + 0x0013;
- }
- alertRED(); // Turn red LED on when all data has been put in buffer
- while(1){
- if((TXEndPos - TXCurrentPos) == 0){
- LATDbits.LATD1 = 1;
- } //if the buffer is empty turn on the green LED
- }
- //We can hook up the Oscilloscope to the red and green lights
- //to time how long it takes for all the data to transfer
- //and get our bit rate
- }
- //*********************************************************************
- // sdINIT is called at power on to set up and initialize the SD module
- //
- // Notes:
- // * After supplying power to the SD module, 500ms wait time must be
- // given before sending or receiving data. During this time the SD
- // module may throw off junk data so RX should be disabed on the
- // PIC.
- // * A single character 'U' is sent over the serial interface so
- // that the SD module can determine the PIC's baud rate. If the
- // character was received successfully, then the module will return
- // the ACK byte (06hex)
- // * After receiving the ACK, the SD module is ready to receive Disk
- // Drive commands from the host PIC as specified in the
- // GOLDELOX-DOS-COMMANDS pdf.
- //*********************************************************************
- void sdINIT(){
- addToBuffer(0x55);
- }
- //*********************************************************************
- // addToBuffer adds a packet of data to the send buffer.
- // toAdd[] = signalID, time3time2time1time0, data3data2data1data0, /n
- void prepareDataToSend(static char signalID, static unsigned int time, static unsigned char bufferInput){
- sdHandshake(signalID);
- addToBuffer(numToASCII(signalID)); //convert the hex value to ascii
- addToBuffer(0x2C); //add a comma
- makeString(time); //makeString adds to buffer
- addToBuffer(0x2C);
- makeString(bufferInput);
- addToBuffer(0x2C);
- addToBuffer(0x0A);
- }
- void addToBuffer(static unsigned char bufferInput){
- TXBuffer[TXEndPos] = bufferInput;
- TXEndPos = TXEndPos + 0x01;
- if(TXEndPos == 128){
- TXEndPos = 0;
- }
- if(waitingForACK == 0){
- PIE1bits.TXIE = 1; //Enable the TX interrupt
- } //If we're not waiting for ack
- }
- char numToASCII(static unsigned char num){
- return (num + 0x30);
- }
- //*********************************************************************
- // This function handles the handshaking with the SD module. It's tasks
- // include sending handshaking packet to the SD module and confirming
- // receipt.
- //
- // Packet Structure:
- // * 40 74 80 [filename; 1 - 12 bytes] 00 [packet size; 4 bytes]
- //
- // Packet example:
- // Write 50 byte of data to the file ABCD in append mode:
- // * 40 74 80 41 42 43 44 00 00 00 00 32
- //
- // After sending the above packet, the SD module should send back 0x06.
- // If it doesn't try sending the packet again. If it does, return.
- //
- // Notes:
- // * Currently supports block sizes of up to 255 bytes. For larger
- // sizes, the function should accept an integer and use masking
- // and shifting to break the integer into four characters that can
- // easily be sent to the SD card. Since we shouldn't get anywhere
- // near 255 bytes, I'm opting to keep the code simpler for now.
- //
- //*********************************************************************
- void sdHandshake(static unsigned char signalID){
- unsigned char arr[13] = {0x40, 0x74, 0x88, signalID, 0x2E, 0x63,
- 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x0D};
- i = 0x00;
- for(i = 0; i < 13; i=i+0x01){
- addToBuffer(arr[i]);
- }
- }
- //**********************************************************************
- // makeDigit and makeString is an awesome hackjob to get around the fact
- // that the PIC18 ISA doesn't include a DIV instruction, and thus DIVs
- // suck balls. Given the number 345, you will need to subtract 100 3
- // times until that number is less than 100
- //**********************************************************************
- char makeDigit(static unsigned int *digit, static unsigned int base){
- int count;
- for(count = 0; *digit >= base; count++){
- *digit -= base;
- }
- return count;
- }
- void *makeString(static unsigned int toString){
- addToBuffer(numToASCII(makeDigit(&toString, 1000)));
- addToBuffer(numToASCII(makeDigit(&toString, 100)));
- addToBuffer(numToASCII(makeDigit(&toString, 10)));
- addToBuffer(numToASCII(makeDigit(&toString, 1)));
- }
- //*********************************************************************
- // uartINIT is called at power on to set up the UART bus to communicate
- // with the SD module.
- //
- // Notes:
- // * The UART bus operates on the PORTC block.
- // * To initialize the bus, SPEN(RCSTA7) and TRISC7 must be set to
- // 1, while TRISC6 must be cleared to 0.
- // * The configuration of the UART bus is configured through the
- // TXSTA, RCSTA, and BAUDCON registers as defined on pages 230-233
- // in the PIC18F4580 specification sheet.
- //*********************************************************************
- void uartINIT(){
- //LATCbits.LATD7 = 1;
- //LATCbits.LATD6 = 0;
- TXSTA = 0b00100110;
- RCSTA = 0b10010000;
- BAUDCON = 0b01001000;
- //SPEN = 1; //serial port enable
- //TXEN = 1; //transfer enable
- //GIE = 1; //global interrupt enable
- //PEIE = 1; //peripheral interrupt enable
- //TXIE = 1; //transfer interrupt enable
- //The following lines set the baud rate
- //BRGH = 1; //BRGH
- //BRG16 = 1; //BRG16
- SPBRGH = 0; //SPBRG values can be taken from the PIC Spec sheet
- SPBRG = 86; //dependant on your desired baud rate and Fosc.
- // in this case we are aiming for a baud rate of 115.2k, with an
- // actual rate of 114.943k, giving a low error rate of .22%.
- // 115.2k falls within the SD modules accepted range of 300 - 256k.
- // These values assume that BRGH and BRG16 values are set.
- }
- //*********************************************************************
- // Enables Interrupts
- //*********************************************************************
- void globalInterruptINIT(){
- INTCONbits.GIE = 1; //global interrupt enable
- INTCONbits.PEIE = 1; //peripheral interrupt enable
- RCONbits.IPEN = 1; // Priority levels enabled; two priority level
- }
- void RXInterruptINIT(){
- PIR1bits.RCIF = 0; //Ensure that the flag is cleared
- PIE1bits.RCIE = 1; //Enable EUSART RX interrupt
- IPR1bits.RCIP = 0; //RX priority low
- }
- void TXInterruptINIT(){
- PIR1bits.TXIF = 0; //Ensure that the flag is cleared
- PIE1bits.TXIE = 0; //Cant start interrupt right away.See discussion
- IPR1bits.RCIP = 0; //TX priority low
- }
- //*********************************************************************
- // Checks flags to determine which interrupt triggered the interrupt
- // vector. Because we want to spend as little time in the interrupt as
- // possible, we will simply set our own private global flag and deal
- // with the interrupt in main
- //*********************************************************************
- void InterruptServiceHigh(){
- //Currently no high level interrupts
- //will eventually be used for ADC interrupts
- }
- //*********************************************************************
- // This function writes data to the SD card by sending commands and
- // data to the SD module. This method should call a submethod to do the
- // handshaking and packet management with the SD module first, and then
- // continue on to send the data after the handshaking is complete.
- //
- // Notes:
- // * Handshaking is done in a seperate function.
- // * variables passed in need to cover at least the data type, the
- // time when the data occured, and the data itself.
- // * There could be a subfunction that catches the data sent to this
- // function and stores it in an array until a predetermined size
- // reached, at which time the buffer would flush all at once. This
- // is what the Cornell team did with a buffer size of 50.
- // * When writing integer values to the register, they should be
- // converted to binary values and there whould be no problems as
- // the decimal values are less that 1023. For now I am working
- // under this assumption.
- //*********************************************************************
- void sdWritePacket(unsigned char packetType, unsigned char data){
- unsigned char arr[] = {packetType, data};
- }
- //*********************************************************************
- // This function takes an array of characters and loads them to the
- // TXREG register for sending on the USART bus. Data that is in the
- // TXREG register is emptied one bit at a time into the transmit
- // register. When the register is empty the TRMT flag is set to 1 and
- // the next byte can be loaded. When a byte is loaded into the TXREG
- // register, it is immidiately sent as long as TXEN bit is set.
- // Currently this will catch the program and wait until the current
- // packet has finished sending until returning to main. If this takes
- // too long then this will be a problem. Options to fix that include
- // increasing the baud rate closer to 256k (the SD module's maxiumum),
- // or using interrupts to come back to this stage every time TXREG
- // clears. This would be the best option as the program could queue up
- // packets as it is sending, ensuring that as soon as one packet is
- // finished, another is ready to be sent. But I'm rambling now, so..
- //*********************************************************************
- unsigned char dataSend(unsigned char arr[]){
- int count;
- int x = sizeof(arr);
- for(count = 0; count < x; count++){
- while(TRMT == 0){
- }
- TXREG = arr[count];
- TRMT = 0;
- }
- }
- //*********************************************************************
- // This method sends the handshake packet to the SD module and listens
- // for 0x06 after the send is complete. Currently the loop is hard
- // coded for 12 bytes because tat is the size of the packet. This
- // allows for filenames of 4 characters. For a longer or shorter
- // filename the number in the loop will have to be changed.
- //*********************************************************************
- unsigned char handshakeSend(unsigned char arr[]){
- int count;
- for(count = 0; count < 12; count++){
- while(TRMT == 0){
- //while already transmitting, wait
- }
- //Then we can send the next byte
- TXREG = arr[count];
- TRMT = 1; //Set TRMT to 1. Will clear automagically
- }
- }
- //*********************************************************************
- // This function enables receiving data on the USART bus. This is in a
- // seperate function because during the first 500ms of the SD module
- // starting it may throw off junk data.
- //*********************************************************************
- void rxEnable(){
- CREN = 1;
- }
- //*********************************************************************
- // This function initializes the analog to digital converter block
- // (A/D).
- // Configuration of the A/D block is done in three registers:
- // * ADCON0 - controls the operation, so will be used to select from
- // which pin the voltage should be read
- // * ADCON1 - configures the functions on the port pins
- // * ADCON2 - configures the A/D clock source, acquisition time, and
- // justification.
- //
- // The holding capacitor must be given time to charge to the sensor's
- // voltage level, with the time needed varying depending on the
- // impedance of the sensor, the voltage the PIC is running at, and the
- // impedance of the wire. This is something that will have to be
- // determined once we have the sensors in hand
- //
- // Notes:
- // * The A/D block has pins primarily on the A block, and are
- // desiganted by ANx on the pin diagram
- // * The last four bits on ADCON1 select which pins (AN0 - 10)
- // should be configured as analog inputs. 0000 means that they are
- // all analog inputs, but this may not be desired. We may wish to
- // use them as digital I/O pins instead
- // * Bits 4 and 3 of ADCON1 select the pic as the voltage reference
- // source if cleared, otherwise pins AN3:4 are used as reference
- //*********************************************************************
- void adINIT(){
- ADCON1 = 0b00000000;
- ADCON2 = 0b10101010;
- }
- //*********************************************************************
- // This function controls the A/D block to grab values from all of the
- // sensors in order and store their values in an array to later use to
- // build and send a data packet to the peripherals. The results from
- // the conversion are stored in the registers ADRESH and ADRESL in the
- // form 000000XX | XXXXXXXX, where ADRESH is the first byte and ADRESL
- // is the second.
- //
- // Notes:
- // * It should be configured so that after setting Go_Done there is
- // some wait time before the actual conversion occurs, and enough
- // wait time for the conversion to fully finish.
- // * For the greatest accuracy, the processor must go into sleep
- // mode during conversion. I'm unsure if this is done
- // automatically when the conversion starts or if it must be put
- // to sleep manually
- //*********************************************************************
- void adCalculate(){
- unsigned char pinNumber;
- for(pinNumber = 0x00; pinNumber <= 0x0a; pinNumber = (pinNumber + 0x01)){
- pinSwitch(pinNumber);
- ADCON0bits.GO_DONE = 1; //start AD conversion
- //saveDataToArray(pinNumber);
- }
- }
- //*********************************************************************
- // This method changes the register ADCON0 to indicate the desired
- // pin on which we wish to perform an AD conversion. The position
- // should be kept track of in a for loop in the parent function and
- // passed in as a parameter.
- //*********************************************************************
- void pinSwitch(unsigned char pinNumber){
- unsigned char temp;
- temp = ADCON0; //read
- temp &= 0xc3; //clear
- temp |= (pinNumber << 2); //mask and shift
- ADCON0 = temp; //write
- }
- //*********************************************************************
- // This function initializes the LCD screen by sending initialization
- // characters or something
- //*********************************************************************
- void lcdINIT(){
- unsigned char arr[6] = {0x38, 0x0c, 0x01, 0x06, 0x02, 0x01};
- TRISD = 0x00;
- TRISCbits.TRISC5 = 0;
- TRISCbits.TRISC4 = 0;
- TRISCbits.TRISC3 = 0;
- LATDbits.LATD5 = 0;
- LATDbits.LATD4 = 0;
- LATDbits.LATD3 = 0;
- Delay10KTCYx(1000);
- DATA = 0b00111000; //functionSet
- Delay100TCYx(1);
- E=1;
- Delay1KTCYx(2);
- E=0;
- Delay1KTCYx(5);
- DATA = 0b00001100; //Display On/Off
- Delay100TCYx(1);
- E=1;
- Delay1KTCYx(2);
- E=0;
- Delay1KTCYx(5);
- DATA = 0b00000001; //Clear Display
- Delay100TCYx(1);
- E=1;
- Delay1KTCYx(2);
- E=0;
- Delay1KTCYx(65);
- DATA = 0b00000110; //EntrySet
- Delay100TCYx(1);
- E=1;
- Delay1KTCYx(2);
- E=0;
- Delay1KTCYx(5);
- DATA = 0b00000010; //Return home
- Delay100TCYx(1);
- E=1;
- Delay1KTCYx(2);
- E=0;
- Delay1KTCYx(65);
- DATA = 0x01; //registerset
- Delay100TCYx(1);
- E=1;
- Delay1KTCYx(2);
- E=0;
- Delay1KTCYx(0);
- LATCbits.LATC3 = 1;
- }
- //*********************************************************************
- // This function writes data to the screen by setting the RD pins to
- // the data and toggling the enable pin.
- //*********************************************************************
- void writeScreen(unsigned char data[], int length){
- int count = 0;
- for(count = 0; count < length; count++){
- LATD = data[count];
- Delay1KTCYx(1); //delay .1ms @ 10Mhz
- E = 1;
- Delay1KTCYx(1); //delay .1ms @ 40mhz
- E = 0;
- Delay1KTCYx(1); //delay .1ms @ 40mhz
- }
- }
- //*********************************************************************
- // This function will flash an LED attached to an unused pin in case
- // an error occurs and a message is not able to be printed to the LCD
- // screen, or the LCD screen never gets implemented. This function will
- // flash an LED on or off every .1 seconds a specified number of times,
- // and then leave a one second pause before starting to flash again.
- // The number of flashes could be counted and referenced in the code
- // or later a PDF to find out what went wrong. For example, one error
- // code could be used to indicate that there is no SD card in the
- // SD module. If the LCD screen is working, it could be used to display
- // additional information related to the error, and then the primary
- // reason for the LED would be to attract attention to the error.
- //*********************************************************************
- void ERRORCODE(int x){
- int count;
- TRISD = 0b00000000;
- while(1){
- for(count = 0; count < x; count++){
- LATDbits.LATD1 = 1;
- Delay10KTCYx(10);
- LATDbits.LATD1 = 0;
- Delay10KTCYx(10);
- }
- Delay10KTCYx(200);
- }
- }
- void alertRED(){
- TRISD = 0b00000000;
- LATDbits.LATD0 = ~LATDbits.LATD0;
- Delay10KTCYx(10);
- }
- void alertGREEN(){
- TRISD = 0b00000000;
- LATDbits.LATD1 = ~LATDbits.LATD1;
- Delay10KTCYx(10);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement