News:

;) This forum is the property of Proton software developers

Main Menu

ADC interrupt without Adin command?

Started by Maxi, Jan 04, 2024, 08:14 AM

Previous topic - Next topic

Maxi

Gentlemen, there is something I have wanted to do for a long time, but I have never succeeded.
I want to read the analog input with interrput without using the adin command.
So to give an example, reading the analog value in the background while the 1000ms led flashes.
Is there anyone who can help me with this?
The processor is not very important, it can be 12F675, 12F683 or 12F1822 of course.
Thank you and happy new year.

Stephen Moss

1) Create an interrupt handler (see ON_Hardware_Interrupt in the Positron Manual)
2) Write the applicable values (see the device datasheet) to the ADC (ADCON) register(s) to select the Channel (input Pin), set it to an Analogue input (required on some devices), turn on the ADC and set the result to Right Justified. You could use left justified, but particularly on a 16bit ADC you would have to adjust the value to account for the fact that only the 10 or 12 MSB contain the result, setting to right justified so that bits 0-9 (or 11 as applicable) contain the ADC value is simpler.
3) Write to the Interrupt (INTCON) register(s) to enable the ADC interrupt
4) When you want to sample the ADC set the Gone/Done bit to 1 to start the conversion. i.e. ADCON0.1 = 1, or if you have aliased to a more logical name something like ADC_GO_DONE = 1.

When the ADC conversion has completed it will set the corresponding interrupt flag (IF) resulting in a jump to the interrupt handler, if you have more than one interrupt enabled you will need to check the IF flags to see which interrupt(s) were triggered. If the ADC caused the interrupt, then read the ADC value into a variable (i.e. My_Value = ADRES (ADC result register value)) and use as applicable to your application.
Don't forget to clear any set interrupt flags before exiting your interrupt handle, otherwise you will not get any further interrupts of that type.

You will need to study the ADC section of the datasheet to know which Tosc setting you need to use to give the ADC the time it needs to correctly read and convert the input voltage.
Depending on the device used you may need to set the voltage refence bits, some have a single reference to set the voltage that will result in the maximum ADC value, while other devices also have one to set the voltage that will result in a value of 0. Unless your input signal only varies by a small amount it is better to set the reference voltage to the PIC's supply voltage.

Also try searching the forum as there may be code examples posted here that can help you, although you will learn more by studying the Positron manual and the Datasheet and writing the code yourself. If you get really stuck post your code and someone will cast an eye over it and point out any corrections.
 

top204

#2
Stephen's mechanism overview is correct and is the same for all devices, but some devices may need different SFRs (Special Function Registers) altered for the ADC and the interrupt handler mechanism, so it is important to read the device's datasheet.

As an example, the code listing below shows one way of using an interrupt handler to take a reading from the ADC on a PIC18F25K20 device:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A demonstration of reading the ADC via an interrupt on a PIC18F25K20 device.
' The same principle holds for all devices, but some will require different SFRs to be manipulated for the ADC and interrupt setup.
'
' Written for the Positron8 compiler by Les Johnson.
'
    Device = 18F25K20                               ' Tell the compiler what device to compile for
    Declare Xtal = 16                               ' Tell the compiler what frequency the device will be operating at (in MHz)
    On_Hardware_Interrupt Goto ISR_Handler          ' Tell the compiler where to jump to when a High Level Interrupt is triggered
'
' Setup USART1
'
    Declare Hserial1_Baud = 9600                    ' Set the Baud rate to 9600
    Declare HRSOut1_Pin = PORTC.6                   ' Set the TX pin
'
' Create any variables for the demonstration here
'
    Dim tADC_Ready As Bit                           ' Holds 1 if an interrupt ADC reading has taken place (must be reset in the main program)
    Dim wADC_Value As Word                          ' Holds the 10-bit value read from the ADC

    Dim wADRESL_SFR As ADRESL.Word                  ' Make a 16-bit SFR from SFRs ADRESL and ADRESH
    Dim wANSEL_SFR  As ANSEL.Word                   ' Make a 16-bit SFR from SFRs ANSEL and ANSELH

'-------------------------------------------------------------------------
' The main program starts here
' Read the ADC from pin AN1 via an interrupt and transmit the value to a serial terminal
'
Main:
    Setup()                                         ' Setup the program and peripherals

    Do                                              ' Create a loop
        ADCON0bits_GO_DONE = 1                      ' Start an ADC reading. When the ADC has been read, it will trigger an interrupt
        If tADC_Ready = 1 Then                      ' Is an ADC reading ready?
            HRsoutLn "ADC=", Dec wADC_Value         ' Yes. So display it on a serial terminal
            DelayMs 200                             ' A delay so the value can be seen changing
            tADC_Ready = 0                          ' Reset the ADC reading ready flag
        EndIf
    Loop                                            ' Do it forever

'-------------------------------------------------------------------------
' Setup the program and peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
    ADC_Init()                                      ' Initialise the ADC and set it for 10-bit operation
    ANSELbits_ANS1 = 1                              ' Make pin AN1 analogue
    tADC_Ready = 0                                  ' Clear the ADC ready flag
    ADC_Channel(1)                                  ' Set the ADC channel to read from

    PIE1bits_ADIE = 1                               ' Enable an ADC interrupt
    INTCONbits_PEIE = 1                             ' Enable peripheral interrupts
    INTCONbits_GIE = 1                              ' Enable global interrupts
EndProc

'-------------------------------------------------------------------------
' Initialise the ADC peripheral on a PIC18F25K20 device
' Input     : None
' Output    : None
' Notes     : Setup for: -Vref is VSS and +Vref is VDD
'           :            Clock is fOSC/32
'           :            Right Justified for 10-bit readings
'
Proc ADC_Init()
    ADCON1 = %00000000                              ' -Vref is VSS. +Vref is VDD
    ADCON2 = %10000010                              ' Right Justified for 10-bit operation. fOSC/32
    ADCON0 = %00000000                              ' ADC disabled. Channel default is AN0
EndProc

'-------------------------------------------------------------------------
' Set the channel to use for an ADC reading on a PIC18F25K20 device
' Input     : pChan holds the channel to read from the ADC peripheral
' Output    : None
' Notes     : Make sure the channel's pin has been set to analogue mode, before taking ADC readings
'
Proc ADC_Channel(pChan As Byte)
    pChan = pChan << 2                              ' Move the channel bits to their correct position (bits 2 to 5)
    ADCON0 = ADCON0 & %11000011                     ' Clear the channel bits of the ADCON0 SFR
    ADCON0 = ADCON0 | pChan                         ' Or in the new channel value to the ADCON0 SFR
    ADCON0bits_ADON = 1                             ' Enable the ADC (just in case it had not been previously enabled)
EndProc

'-------------------------------------------------------------------------
' Interrupt Handler
' Input     : None
' Output    : tADC_Ready holds 1 if an ADC reading is ready
'           : wADC_Value holds the ADC reading value
' Notes     : Handles an ADC reading interrupt
'
ISR_Handler:
    Context Save                                    ' Save any compiler system variables and SFRs that are used within the interrupt handler

    If PIR1bits_ADIF = 1 Then                       ' Was it an ADC sample ready that triggered the interrupt?
        tADC_Ready = 1                              ' Yes. So set the flag
        wADC_Value = wADRESL_SFR                    ' Store the ADC reading's value
        PIR1bits_ADIF = 0                           ' Clear the ADC interrupt flag
    EndIf

    Context Restore                                 ' Restore any compiler system variables and SFRs, then exit the interrupt

Maxi

I will try this at the first opportunity, thank you all.
(I will write here the positive or negative results)