//#define INCLUDE_TEST_HARNESS
/****************************************************************************
Module
PIC32_AD_Lib.c
Description
This is a module implementing the basic functions to use the A/D
converter on the PIC32MX170F256B
Notes
History
When Who What/Why
-------------- --- --------
11/03/20 14:55 jec cleaned up typos and clarified a comment
10/27/20 16:10 jec cleaned up the documentation to meet SPDL Standards
10/20/20 16:38 jec Began Coding
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "PIC32_AD_Lib.h"
#include <xc.h>
#include <stdbool.h>
/*--------------------------- External Variables --------------------------*/
/*----------------------------- Module Defines ----------------------------*/
// We configure the A/D to use 2 8-word buffers, so it is limited to
// converting 8 channels.
#define MAX_CHANNELS 8
/*------------------------------ Module Types -----------------------------*/
/*---------------------------- Module Functions ---------------------------*/
static uint8_t CountBits(uint16_t num2Count);
/*---------------------------- Module Variables ---------------------------*/
static uint8_t numChanInSet; // used to iterate in read requests
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
ADC_ConfigAutoScan
Parameters
uint16_t whichPins specifies which of the ANx pins will be converted
a 1 in a bit position indicates that that ANx channel is to be
converted e.g: to convert on AN0, set bit 0
Returns
bool true if no errors
Description
configures the A/D converter subsystem for auto-sampling on a set of pins
Notes
This configures the A/D to use 2 8-word buffers, so it is limited to
converting 8 channels.
Author
J. Edward Carryer, 10/20/20 15:49
****************************************************************************/
bool ADC_ConfigAutoScan( uint16_t whichPins){
bool returnVal = true; // assume no errors
// first test to be sure that the number of 1s in whichPins
// is less than the maximum allowed number of channels
if (MAX_CHANNELS < (numChanInSet = CountBits(whichPins)))
{
returnVal = false;
}else // OK, looks good
{
AD1CON1bits.ON = 0; // disable ADC
// AD1CON1<2>, ASAM : 1 = Sampling begins immediately after last conversion completes
// AD1CON1<4>, CLRASAM : 0 = buffer contents will be overwritten by the next conversion sequence
// AD1CON1<7:5>, SSRC : 111 = Internal counter ends sampling and starts conversion (auto convert)
// AD1CON1<10:8>, FORM : 000 = unsigned integer data format
// AD1CON1<13>, SIDL : 0 = Continue module operation when the device enters Idle mode
// AD1CON1<15>, ON : 0 = ADC remains off
AD1CON1bits.ASAM = 1; // 1 = Sampling begins immediately after last conversion completes
AD1CON1bits.CLRASAM = 0;// 0 = buffer contents will be overwritten by the next conversion sequence
AD1CON1bits.SSRC = 0b111;// 111 = Internal counter ends sampling and starts conversion (auto convert)
// AD1CON1SET = 0x00e4; // to set everything above in one fell swoop
// AD1CON2<0>, ALTS : 0 = Always use Sample A input multiplexer settings
// AD1CON2<1>, BUFM : 1 = Buffer configured as two 8-word buffers, ADC1BUF7-ADC1BUF0, ADC1BUFF-ADCBUF8
// AD1CON2<10>, CSCNA : 1 = Scan inputs
// AD1CON2<12>, OFFCAL : 0 = Disable Offset Calibration mode
// AD1CON2<15:13>,VCFG : 000 = Vrefh = AVDD, Vrefl = AVss
AD1CON2bits.BUFM = 1; // 1 = Buffer configured as two 8-word buffers
AD1CON2bits.CSCNA = 1; // 1 = Scan inputs
// AD1CON2 = 0x0402; // to set everything above in one fell swoop
// AD2CON2<5:2>, SMPI : Interrupt flag set at after numChanInSet completed conversions
AD1CON2SET = (numChanInSet-1) << 2;
// AD1CON3<7:0>, ADCS : 1 = TPB * 2 * (ADCS<7:0> + 1) = 4 * TPB = TAD
// AD1CON3<12:8>, SAMC : 0x0f = Acquisition time = AD1CON3<12:8> * TAD = 15 * TAD
// AD1CON3<15>, ADRC : 0 = Clock derived from Peripheral Bus Clock (PBCLK)
AD1CON3bits.ADCS = 1; // 1 = TPB * 2 * (ADCS<7:0> + 1) = 4 * TPB = TAD
AD1CON3bits.SAMC = 0x0f;// 0x0f = Acquisition time = AD1CON3<12:8> * TAD = 15 * TAD
// AD1CON3 = 0x0f01; // to set everything above in one fell swoop
// AD1CHS is ignored in scan mode, but we'll clear it to be sure
AD1CHS = 0;
// select which pins to use for scan mode, a 1 indicates that the corresponding ANx
// input will be converted
AD1CSSL = whichPins;
AD1CON1bits.ON = 1; // enable ADC
}
return returnVal;
}
/****************************************************************************
Function
ADC_MultiRead
Parameters
uint32_t *adcResults pointer to array to hold conversion set results
this must have room for at least as many results as numPins in
ADC_ConfigAutoScan
Returns
nothing
Description
Reads the most recent conversion results from the channel set and copies
the results to the array passed as a pointer to this function
lowest numbered converted channel is in adcResults[0]
Notes
None.
Author
J. Edward Carryer, 10/20/20 16:39
****************************************************************************/
void ADC_MultiRead(uint32_t *adcResults){
uint8_t i;
uint32_t LocalResult;
volatile uint32_t *resultSet;
// stop automatic sampling during the read to be sure we get a coherent set
AD1CON1bits.ASAM = 0;
if( AD1CON2bits.BUFS == 1){
// 1 = ADC is currently filling buffer 0x8-0xF, user should access data in 0x0-0x7
resultSet = &ADC1BUF0;
}else{
// 0 = ADC is currently filling buffer 0x0-0x7, user should access data in 0x8-0xF
resultSet = &ADC1BUF8;
}
for (i=0; i < numChanInSet; i++)
{
// read the results from the ADC1BUFx registers. They are 16 bytes apart in
// the memory map. That's 4 uint32_t apart, hence *4
LocalResult= adcResults[i] = *(resultSet+(4*i)); // read the results from the ADC1BUFx registers
}
AD1CON1bits.ASAM = 1; // restart automatic sampling
IFS0CLR = _IFS0_AD1IF_MASK; // clear ADC interrupt flag, see table 7-1, pg 68
}
// count the number of bits set in v. Algorithm from K&R
static uint8_t CountBits(uint16_t num2Count)
{
uint8_t count; // count accumulates the total bits set in num2Count
for (count = 0; num2Count; count++)
{
num2Count &= num2Count - 1;// clear the least significant bit set
}
return count;
}
#ifdef INCLUDE_TEST_HARNESS
// PIC32MX170F256B Configuration Bit Settings for ME218 operating at 40MHz from internal fast oscillator (8Mhz)
// 'C' source line config statements
// DEVCFG3
#pragma config USERID = 0xFFFF // Enter Hexadecimal value (Enter Hexadecimal value)
#pragma config PMDL1WAY = OFF // Peripheral Module Disable Configuration (Allow multiple reconfigurations)
#pragma config IOL1WAY = OFF // Peripheral Pin Select Configuration (Allow multiple reconfigurations)
// DEVCFG2
#pragma config FPLLIDIV = DIV_2 // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20 // PLL Multiplier (20x Multiplier)
#pragma config FPLLODIV = DIV_2 // System PLL Output Clock Divider (PLL Divide by 2)
// DEVCFG1
#pragma config FNOSC = FRCPLL // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF // Secondary Oscillator Enable (Disabled)
#pragma config IESO = OFF // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FPBDIV = DIV_2 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/2)
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window Size is 25%)
// DEVCFG0
#pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1)
#pragma config PWP = OFF // Program Flash Write Protect (Disable)
#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF // Code Protect (Protection Disabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <stdio.h>
#include "bitdefs.h"
#include "terminal.h"
//these were the problematic ones for XinYi
#define CHANNEL_SET (BIT5HI | BIT9HI| BIT12HI)
uint32_t ResultsArray[MAX_CHANNELS];
void main(void)
{
uint8_t numChannelsToTest;
numChannelsToTest = CountBits(CHANNEL_SET);
Terminal_HWInit();
clrScrn();
goHome();
puts("\rTest harness for PIC32_AD_Lib.c\r");
if ( false == ADC_ConfigAutoScan(0b111111111))
{
printf("ADC_ConfigAutoScan() failed when requesting too many channels (correct behavior)\n\r");
}else{}
printf("Testing %d channels\n\r", numChannelsToTest);
// configure channels
if ( false == ADC_ConfigAutoScan(CHANNEL_SET))
{
printf("ADC_ConfigAutoScan() failed \n\r");
}else
{
// Now get conversion results and print them to the screen
while(!IsNewKeyReady())
{
uint16_t delayCounter;
uint8_t channelCounter;
ADC_MultiRead(ResultsArray);
clrLine();
for(channelCounter=0; channelCounter < numChannelsToTest; channelCounter++)
{
printf("%d:%d ", channelCounter, ResultsArray[channelCounter]);
}
printf("\r");
// now, delay a little bit
for (delayCounter = 0; delayCounter< 65535; delayCounter++){}
}
}
while(1){}
}
#endif