Jump to content

Vini

Members
  • Posts

    2
  • Joined

  • Last visited

Posts posted by Vini

  1. I have now had a look at the other ADC. It is quite harder to configure since it has 3 functionalities (CPU temperature sensor, or resistive Touch Panel controller, or single-ended ADC), but I was able to figure it out enough to use it. If you use the ADC, you must interrupt the other functionalities or build a driver to cope with them (temperature sensor and Touch Panel/ADC). Note that I am using Python 3 (because I'm familiar with it) to configure the ADC and read the ADC's values, which is probably very slow. A C/C++ program would be way faster in that regard (e.g., the code used to acquire the CPU temperature: https://github.com/armbian/build/blob/e9fb9542c763a88c8e689c3a14fc135bccea55f1/packages/bsp/sunxi-temp/sunxi_tp_temp.c).

     

    Here is the code for those that it could interest.

    #!/usr/bin/env python3
    """
    To use this script you must remove kernel drivers related to the A20's adc.
    
    Note that this script has been developped on a pcDuino 3, running on Armbian
    (Linux pcduino3 5.15.13-sunxi #trunk.0004 SMP Wed Jan 5 17:53:11 UTC 2022 armv7l GNU/Linux)
    The values registers have been set using the A20 Allwinner User Manual and by reverse engineering
    the previous OS images released by the pcDuino 3 teams in order to find a correct configuration.
    (pcDuino3 OS iso used: pcduino3_dd_sdbootable_20141110)
    
    You can list them:
    $ sudo lsmod | grep adc
    axp20x_adc             16384  0
    sun4i_gpadc_iio        16384  0
    industrialio           57344  2 sun4i_gpadc_iio,axp20x_adc
    sun4i_gpadc            16384  0
    
    And remove them temporaly from the kernel using:
    $ sudo rmmod sun4i_gpadc_iio
    $ sudo rmmod sun4i_gpadc
    
    Blacklisting the modules would remove the need of runing the rmmod.
    WARNING: Note that removing the access to this ADC is probably removing the
    access to the temperature sensor.
    """
    from time import sleep
    import os
    import mmap
    
    # Activate or deactivate the debug print
    DEBUG = False
    
    # Check CPU
    a20 = "sun7i" in open("/proc/cpuinfo", "r").read()
    
    if not a20:
        exit("This program only works on Allwinner sun7i (A20) cpus.")
    
    # TP register base address = 0x01C25000 (cfr. A20 manual page 205)
    TP_BASE_ADDRESS = 0x01C25000
    # TP Control Register0
    TP_CTRL0 = TP_BASE_ADDRESS + 0x00
    # TP Control Register1
    TP_CTRL1 = TP_BASE_ADDRESS + 0x04
    # TP Pressure Measurement and touch sensitive Control Register
    TP_CTRL2 = TP_BASE_ADDRESS + 0x08
    # Median and averaging filter Controller Register
    TP_CTRL3 = TP_BASE_ADDRESS + 0x0c
    # TP Interrupt FIFO Control Reg
    TP_INT_FIFOC = TP_BASE_ADDRESS + 0x10
    # TP Interrupt FIFO Status Register
    TP_INT_FIFOS = TP_BASE_ADDRESS + 0x14
    # TP Temperature Period Register
    TP_TPR = TP_BASE_ADDRESS + 0x18
    # TP Common Data
    TP_CDAT = TP_BASE_ADDRESS + 0x1c
    # Temperature Data Register
    TEMP_DATA = TP_BASE_ADDRESS + 0x20
    # TP Data Register
    TP_DATA = TP_BASE_ADDRESS + 0x24
    # TP IO Configuration
    TP_IO_CONFIG = TP_BASE_ADDRESS + 0x28
    # TP IO Port Data
    TP_PORT_DATA = TP_BASE_ADDRESS + 0x2c
    
    MAP_MASK = mmap.PAGESIZE - 1
    
    
    def write(file, address, content: bytearray):
        """Write the content at a specific address and check success.
        Return the success of the operation.
        """
        file.seek(address)
        file.write(content)
        file.seek(address)
        return file.read(len(content)) == content
    
    
    def print_all(mem):
        """Print the binary value of all the registers related to the ADC."""
        mem.seek(TP_CTRL0 & MAP_MASK)
        print("TP_CTRL0:", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_CTRL1 & MAP_MASK)
        print("TP_CTRL1:", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_CTRL2 & MAP_MASK)
        print("TP_CTRL2:", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_CTRL3 & MAP_MASK)
        print("TP_CTRL3:", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_INT_FIFOC & MAP_MASK)
        print("TP_INT_FIFOC", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_INT_FIFOS & MAP_MASK)
        print("TP_INT_FIFOS", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_TPR & MAP_MASK)
        print("TP_TPR :", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_CDAT & MAP_MASK)
        print("TP_CDAT:", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TEMP_DATA & MAP_MASK)
        print("TEMP_DATA:", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_DATA & MAP_MASK)
        print("TP_DATA:", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_IO_CONFIG & MAP_MASK)
        print("TP_IO_CONFIG:", [bin(d) for d in list(mem.read(4))])
    
        mem.seek(TP_PORT_DATA & MAP_MASK)
        print("TP_PORT_DATA:", [bin(d) for d in list(mem.read(4))])
    
    
    try:
        mem = mmap.mmap(os.open("/dev/mem", os.O_RDWR | os.O_SYNC), mmap.PAGESIZE,
                        mmap.MAP_SHARED, offset=TP_BASE_ADDRESS & ~MAP_MASK)
    
        if DEBUG:
            print("Register initial")
            print_all(mem)
    
        # The correct way to do it is to add the 4 bytes together as follow :
        REGISTERS_CONFIG0 = bytearray((
            # Bit 15:0 TACQ.
            #  Touch panel ADC acquire time CLK_IN/(16*(N+1))
            0b00111111,
            0b00000000,
            # Bit 23 ADC_FIRST_DLY_MODE.
            #  ADC First Convert Delay Mode Select:
            #  0: CLK_IN/16, and 1: CLK_IN/16*256
            # Bit 22 ADC_CLK_SELECT.
            #  ADC Clock Source Select:
            #  0: HOSC(24MHZ), and 1: Audio PLL
            # Bit 21:20 ADC_CLK_DIVIDER.
            #  ADC Clock Divider(CLK_IN)
            #  00: CLK/2, 01: CLK/3, 10: CLK/6, and 11: CLK/1
            # Bit 19:16 FS_DIV.
            #  ADC Sample Frequency Divider
            #  xxxx: CLK_IN/2^(20-xxxx)
            0b00111111,
            # Bit 31:24 ADC_FIRST_DLY.
            #  ADC First Convert Delay Time(T_FCDT)setting
            #  Based on ADC First Convert Delay Mode select (Bit 23)
            #  T_FCDT = ADC_FIRST_DLY * ADC_FIRST_DLY_MODE
            0b0
        )
        )
        write(mem, TP_CTRL0 & MAP_MASK, REGISTERS_CONFIG0)
    
        REGISTERS_CONFIG1 = bytearray((
            # Bit 7 CHOP_TEMP_EN
            #  Chop temperature calibration enable
            #  0: Disable, and 1: Enable
            # Bit 6 TOUCH_PAN_CALI_EN.
            #  Touch Panel Calibration
            #  1: start Calibration, it is clear to 0 after calibration
            # Bit 5 TP_DUAL_EN.
            #  Touch Panel Double Point Enable
            #  0: Disable, and 1: Enable
            # Bit 4 TP_MODE_EN.
            #  Tp Mode Function Enable
            #  0: Disable, and 1: Enable
            # Bit 3 TP_ADC_SELECT.
            #  Touch Panel and ADC Select:
            #  0: TP, and 1: ADC
            # Bit 2:0 ADC_CHAN_SELECT.
            #  Analog input channel Select In Normal mode:
            #  000: X1 channel, 001: X2 Channel
            #  010: Y1 Channel, 011: Y2 Channel
            #  1xx : 4-channel robin-round
            #  FIFO Access Mode, based on this setting
            #  Selecting one channel, FIFO will access that channel data;
            #  Selecting four channels FIFO will access each channel data
            #  in successive turn, first is X1 data.
            0b00011000,
            # Bit 19(15):12 STYLUS_UP_DEBOUNCE.
            #  See under
            # Bit 11:10
            # Bit 9 STYLUS_UP_DEBOUCE_EN.
            #  Stylus Up De-bounce Function Select
            #  0: Disable, and 1: Enable
            # Bit 8 /
            0b00000000,
            # Bit 23:20 /
            # Bit 19:(16)12 STYLUS_UP_DEBOUNCE.
            #  Stylus Up De-bounce Time setting
            #  0x00: 0
            #  ….
            #  0xff: 2N*(CLK_IN/16*256)
            0b00000000,
            # Bit 31:24 /
            0b00000000
        )
        )
        write(mem, TP_CTRL1 & MAP_MASK, REGISTERS_CONFIG1)
    
        REGISTERS_CONFIG2 = bytearray((
            0b00000000,
            0b00000000,
            0b00000000,
            0b11110100
        )
        )
        write(mem, TP_CTRL2 & MAP_MASK, REGISTERS_CONFIG2)
    
        REGISTERS_CONFIG3 = bytearray((
            # Bit 31(7):3 /
            # Bit 2 FILTER_EN.
            #  Filter Enable
            #  0: Disable, and 1: Enable
            # Bit 1:0 FILTER_TYPE.
            #  Filter Type:
            #  00: 4/2, 01: 5/3, 10: 8/4, and 11: 16/8
            0b00000101,
            # Bit 31:(8)3 /
            0b00000000,
            0b00000000,
            0b00000000
        )
        )
        write(mem, TP_CTRL3 & MAP_MASK, REGISTERS_CONFIG3)
    
        REGISTERS_CONFIGFIFOC = bytearray((
            0b00000000,
            0b00000000,
            0b00000001,
            0b00000000
        )
        )
        write(mem, TP_INT_FIFOC & MAP_MASK, REGISTERS_CONFIGFIFOC)
    
        REGISTERS_TPR = bytearray((
            0b00000000,
            0b00000000,
            0b00000000,
            0b00000000
        )
        )
        write(mem, TP_TPR & MAP_MASK, REGISTERS_TPR)
    
        REGISTERS_CONFIGIO = bytearray((
            # Bit 7 /
            # Bit 6:4 TX_N_SELECT
            #  TX_N Port Function Select:
            #  000:Input,  001:Output, and 010: TP_XN
            # Bit 3 /
            # Bit 2:0 TX_P_SELECT
            #  TX_P Port Function Select:
            #  000:Input,  001:Output, and 010: TP_XP
            0b00100010,
            # 0b00000000, # All inputs
            # 0b00010001, # All inputs
            # Bit 14:12 TY_N_SELECT
            #  TY_N Port Function Select:
            #  000:Input,  001:Output, and 010: TP_YN
            # Bit 11 /
            # Bit 10:8 TY_P_SELECT
            #  TY_P Port Function Select:
            #  000:Input,  001:Output, and 010: TP_YP
            0b00100010,
            # 0b00000000, # All inputs
            # 0b00010001, # All inputs
            # Bit 31:15 /
            0b00000000,
            0b00000000
        )
        )
        write(mem, TP_IO_CONFIG & MAP_MASK, REGISTERS_CONFIGIO)
    
        if DEBUG:
            print("Final config")
            print_all(mem)
    
        while True:
            for adc in (0, 1, 2, 3):
                if DEBUG:
                    print(f"\nA{adc+2} {45 * '='}")
                    print_all(mem)
                    print()
    
                # Select the ADC
                write(mem, TP_CTRL1 & MAP_MASK, bytearray(
                    (0b00011000 | adc, 0b00000000, 0b00000000, 0b00000000)))
    
                # LED some times to the ADC logic to set the value
                sleep(0.01)
                mem.seek(TP_DATA & MAP_MASK)
                LSB, MSB = mem.read(2)
                print(f"A{adc+2}: {(MSB << 8) + LSB}", sep=" ")
    
            print()
    
    finally:
        mem.close()

     

  2. Hello,

     

    Interesting topic. I played with a pcDuino3 and the pin LRADC0 and 1 are mapped to A0 and A1. There are other ADCs mapped to the A2 to A5 pins (XP_TP, XN_TP, YP_TP and YN_TP), but I did not play with them yet.

     

    I slightly changed your code to port it to Python 3:

     

    #!/usr/bin/env python3
    from time import sleep
    import os
    import mmap
    
    # Check CPU
    a20 = "Allwinner sun7i (A20)" in open("/proc/cpuinfo", "r").read()
    
    if not a20:
        exit("This program only works on Allwinner sun7i (A20) cpus.")
    
    # LRADC register base address = 0x01C22800 (cfr. A20 manual page 193)
    LRADC_BASE_ADDRESS = 0x01C22800
    """
    # LRADC Control Register
    LRADC_CTRL = LRADC_BASE_ADDRESS + 0x00
    # LRADC Interrupt Control Register
    LRADC_INTC = LRADC_BASE_ADDRESS + 0x04
    # LRADC Interrupt Status Register
    LRADC_INTS = LRADC_BASE_ADDRESS + 0x08
    """
    # LRADC Data Register 0
    LRADC_DATA0 = LRADC_BASE_ADDRESS + 0x0c
    # LRADC Data Register 1
    LRADC_DATA1 = LRADC_BASE_ADDRESS + 0x10
    
    MAP_MASK = mmap.PAGESIZE - 1
    
    try:
        mem = mmap.mmap(os.open("/dev/mem", os.O_RDWR | os.O_SYNC), mmap.PAGESIZE,
                        mmap.MAP_SHARED, offset=LRADC_BASE_ADDRESS & ~MAP_MASK)
    
        # The correct way to do it is to add the 4 bytes together as follow :
        mem.seek(LRADC_BASE_ADDRESS & MAP_MASK)
        REGISTERS_CONFIG = bytearray((
            # 0111 1001 is for bit 7 to 0 of the first byte (0x01C22800).
            # Bit 7 /
            # Bit 6 is LRADC sample hold
            # Bit 5:4 '11' are for reference voltage to use, where:
            #  00 ≈ 1.9 V, 01 ≈ 1.8V, 10 ≈ 1.7V, and 11 ≈ 1.6V
            # Bit 3:2 is the sampling rate, where:
            #  00 = 250 Hz, 01 = 125 Hz, 10 = 62.5 Hz, and 11 = 32.25 Hz.
            # Bit 1 /
            # Bit 0 is to activate the LRADC.
            0b01000001,
            # 0010 0000 is for bit 7 to 0 of the 2nd byte (0x01C22801).
            # Bit 13 and 12 '10' are for continuous mode.
            # 1100 0000 is for bit 7 to 0 of the 3rd byte (0x01C22802).
            0b00100000,
            # Bit 23 and 22 '11' are to enable both LRADC0 and LRADC1.
            0b11000000,
            # 1 = 0000 0001 is default value.
            0b00000001
        )
        )
        mem.write(REGISTERS_CONFIG)
    
        mem.seek(LRADC_BASE_ADDRESS & MAP_MASK)
        check_register_config = mem.read(4) == REGISTERS_CONFIG
        print(f"Check LRADC config: {'ok' if check_register_config else 'failed'}")
    
        while True:
            mem.seek(LRADC_DATA0 & MAP_MASK)
            # should be between 0 and 63 (6 bits)
            print(f"LRADC0 (A0): {mem.read_byte()}")
    
            mem.seek(LRADC_DATA1 & MAP_MASK)
            # should be between 0 and 63 (6 bits)
            print(f"LRADC1 (A1): {mem.read_byte()}")
    
            sleep(0.2)
    
    finally:
        mem.close()
×
×
  • Create New...

Important Information

Terms of Use - Privacy Policy - Guidelines