Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Last revision Both sides next revision | ||
en:xu3_hardware_adc [2016/01/21 17:49] john1117 [Using mmap] |
en:xu3_hardware_adc [2016/01/22 10:20] odroid [Using mmap] |
||
---|---|---|---|
Line 8: | Line 8: | ||
| 23 | ADC_0.AIN3 | XADC0AIN_3 | | | 23 | ADC_0.AIN3 | XADC0AIN_3 | | ||
- | ==== Using sysfs ==== | + | ===== Using sysfs ===== |
Read raw data.(ADC Channel 0) | Read raw data.(ADC Channel 0) | ||
Line 19: | Line 19: | ||
**Input voltage range is 0~1.8Volt. Otherwise your ODROID will be damaged permanently.** | **Input voltage range is 0~1.8Volt. Otherwise your ODROID will be damaged permanently.** | ||
- | ==== Using mmap ==== | + | ===== Using mmap ===== |
- | Example c source code | + | Example C source code to access the ADC. |
<file c mmap_adc.c> | <file c mmap_adc.c> | ||
- | #include <stdio.h> | + | #include <stdio.h> |
- | #include <fcntl.h> | + | #include <fcntl.h> |
- | #include <sys/mman.h> | + | #include <sys/mman.h> |
- | #include <stdint.h> | + | #include <stdint.h> |
- | | + | |
- | /* EXYNOS4412/5250 ADC_V1 registers definitions */ | + | /* EXYNOS ADC_V1 registers definitions */ |
- | #define ADC_V1_DATAX 0x0C | + | #define ADC_V1_DATAX 0x0C |
- | | + | |
- | /* Future ADC_V2 registers definitions */ | + | /* Future ADC_V2 registers definitions */ |
- | #define ADC_V2_CON1 0x00 | + | #define ADC_V2_CON1 0x00 |
- | #define ADC_V2_CON2 0x04 | + | #define ADC_V2_CON2 0x04 |
- | #define ADC_V2_STAT 0x08 | + | #define ADC_V2_STAT 0x08 |
- | #define ADC_V2_INT_EN 0x10 | + | |
- | #define ADC_V2_INT_ST 0x14 | + | /* Bit definitions for ADC_V2 */ |
- | + | #define ADC_V2_CON1_SOFT_RESET (1u << 2) | |
- | /* Bit definitions for ADC_V2 */ | + | |
- | #define ADC_V2_CON1_SOFT_RESET (1u << 2) | + | #define ADC_V2_CON2_OSEL (1u << 10) |
- | | + | #define ADC_V2_CON2_ESEL (1u << 9) |
- | #define ADC_V2_CON2_OSEL (1u << 10) | + | #define ADC_V2_CON2_HIGHF (1u << 8) |
- | #define ADC_V2_CON2_ESEL (1u << 9) | + | #define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4) |
- | #define ADC_V2_CON2_HIGHF (1u << 8) | + | #define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0) |
- | #define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4) | + | #define ADC_V2_CON2_ACH_MASK 0xF |
- | #define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0) | + | |
- | #define ADC_V2_CON2_ACH_MASK 0xF | + | /* Bit definitions common for ADC_V1 and ADC_V2 */ |
- | | + | #define ADC_CON_EN_START (1u << 0) |
- | /* Bit definitions common for ADC_V1 and ADC_V2 */ | + | #define ADC_DATX_MASK 0xFFF |
- | #define ADC_CON_EN_START (1u << 0) | + | |
- | #define ADC_DATX_MASK 0xFFF | + | static volatile uint32_t *adc; |
- | | + | static unsigned char channel; |
- | static volatile uint32_t *adc; | + | static uint32_t con1, con2; |
- | static uint32_t con1, con2; | + | static int val0; |
- | void exynos_read_raw(unsigned char channel); | + | static int val3; |
- | void exynos_adc_isr(); | + | |
- | static int val; | + | void exynos_read_raw(unsigned char channel); |
- | | + | |
- | int main(int argc, char **argv) | + | int main(int argc, char **argv) |
- | { | + | { |
- | int fd; | + | int fd; |
- | | + | |
- | if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { | + | if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { |
- | printf("Unable to open /dev/mem\n"); | + | printf("Unable to open /dev/mem\n"); |
- | return -1; | + | return -1; |
- | } | + | } |
- | | + | |
- | adc = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, | + | adc = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, |
- | MAP_SHARED, fd, 0x12D10000); | + | MAP_SHARED, fd, 0x12D10000); |
- | if (adc < 0) { | + | if (adc < 0) { |
- | printf("mmap failed.\n"); | + | printf("mmap failed.\n"); |
- | return -1; | + | return -1; |
- | } | + | } |
- | | + | |
- | // exynos_adc_hw_init | + | // exynos_adc_hw_init |
- | con1 = ADC_V2_CON1_SOFT_RESET; | + | con1 = ADC_V2_CON1_SOFT_RESET; |
- | *(adc + (ADC_V2_CON1 >> 2)) |= con1; | + | *(adc + (ADC_V2_CON1 >> 2)) |= con1; |
- | con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | | + | con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | |
- | ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); | + | ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); |
- | *(adc + (ADC_V2_CON2 >> 2)) |= con2; | + | *(adc + (ADC_V2_CON2 >> 2)) |= con2; |
- | | + | |
- | while (1) | + | while (1) |
- | { | + | { |
- | if (*(adc + (ADC_V2_STAT >> 2)) && 4) { | + | // End of A/D conversion |
- | /* start channel 0 conversion */ | + | if (*(adc + (ADC_V2_STAT >> 2)) && 4) { |
- | exynos_read_raw(0); | + | /* Read value */ |
- | /* Read value */ | + | if (channel == 0x0) { |
- | val = *(adc + (ADC_V1_DATAX >> 2)) & ADC_DATX_MASK; | + | val0 = *(adc + (ADC_V1_DATAX >> 2)) & ADC_DATX_MASK; |
- | printf("ADC Channel 0 : %d\n", val); | + | printf("ADC Channel 0 : %d\n", val0); |
- | } | + | } else if (channel == 0x3) { |
- | sleep(1); | + | val3 = *(adc + (ADC_V1_DATAX >> 2)) & ADC_DATX_MASK; |
- | } | + | printf("ADC Channel 3 : %d\n", val3); |
- | | + | } |
- | return 0; | + | |
- | } | + | if (channel == 0x0) { |
- | | + | /* start channel 0 conversion */ |
- | void exynos_read_raw(unsigned char channel) | + | exynos_read_raw(3); |
- | { | + | } |
- | con2 = *(adc + (ADC_V2_CON2 >> 2)); | + | else if (channel == 0x3) { |
- | con2 &= ~ADC_V2_CON2_ACH_MASK; | + | // start channel 3 conversion |
- | con2 |= ADC_V2_CON2_ACH_SEL(channel); | + | exynos_read_raw(0); |
- | *(adc + (ADC_V2_CON2 >> 2)) |= con2; | + | } |
- | | + | } |
- | con1 = *(adc + (ADC_V2_CON1 >> 2)); | + | channel = con2 & ADC_V2_CON2_ACH_MASK; |
- | *(adc +(ADC_V2_CON1 >> 2)) = con1 | ADC_CON_EN_START; | + | sleep(1); |
+ | } | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | void exynos_read_raw(unsigned char channel) | ||
+ | { | ||
+ | con2 = *(adc + (ADC_V2_CON2 >> 2)); | ||
+ | con2 &= ~ADC_V2_CON2_ACH_MASK; | ||
+ | *(adc + (ADC_V2_CON2 >> 2)) &= con2; | ||
+ | con2 |= ADC_V2_CON2_ACH_SEL(channel); | ||
+ | *(adc + (ADC_V2_CON2 >> 2)) |= con2; | ||
+ | |||
+ | con1 = *(adc + (ADC_V2_CON1 >> 2)); | ||
+ | *(adc +(ADC_V2_CON1 >> 2)) = con1 | ADC_CON_EN_START; | ||
} | } | ||
</file> | </file> | ||
- | Register Map Summary | + | ==== Register Map Summary ==== |
+ | |||
+ | * Base Address : 0x12D1_0000 | ||
^Name^Offset^Description^Reset Value^ | ^Name^Offset^Description^Reset Value^ | ||
|ADC_CON1|0x0000|ADC Control register|0x0000_0002| | |ADC_CON1|0x0000|ADC Control register|0x0000_0002| | ||
Line 116: | Line 134: | ||
|ADC_INT_STATUS|0x0014|ADC Interrupt Status register|0x0000_0000| | |ADC_INT_STATUS|0x0014|ADC Interrupt Status register|0x0000_0000| | ||
|ADC_VERSION|0x0020|ADC Version information register|0x8000_0008| | |ADC_VERSION|0x0020|ADC Version information register|0x8000_0008| | ||
+ | |||
+ | **ADC_CON1** | ||
+ | |||
+ | * Base Address : 0x12D1_0000 | ||
+ | * Address = Base Address + 0x0000, Reset Value = 0x0000_0002 | ||
+ | |||
+ | ^Name^Bit^Type^Description^Reset Value^ | ||
+ | |RSVD|[31:3]|R|Reserved(read as zero, do not modify)|0x0| | ||
+ | |SOFT_RESET|[2:1]|RW|Software Reset|0x1| | ||
+ | |:::|:::|:::|0x2 = Reset|:::| | ||
+ | |:::|:::|:::|Other = Non-reset|:::| | ||
+ | |STC_EN|[0]|RW|Enables ADC start conversion:|0x0| | ||
+ | |:::|:::|:::|0x0 = Disables|:::| | ||
+ | |:::|:::|:::|0x1 = Enables|:::| | ||
+ | |||
+ | **ADC_CON2** | ||
+ | |||
+ | * Base Address: 0x12D1_0000 | ||
+ | * Address = Base Address + 0x0004, Reset Value = 0x0000_0720 | ||
+ | |||
+ | ^Name^Bit^Type^Description^Reset Value^ | ||
+ | |RSVD|[31:11|R|Reserved(read as zero, do not modify)|0x0| | ||
+ | |OSEL|[10]|RW|Selection of the output format|0x1| | ||
+ | |:::|:::|:::|0x0 = 2's complement|:::| | ||
+ | |:::|:::|:::|0x1 = Offset binary(recommended)|:::| | ||
+ | |ESEL|[9]|RW|Selection of the ADC output timing|0x0| | ||
+ | |:::|:::|:::|0x0 = The first output data is evaluated after 40 ADC Clock|:::| | ||
+ | |:::|:::|:::|0x1 = The first output data is evaluated after 20 ADC Clock (recommended).|:::| | ||
+ | |HIGHF|[8]|RW|Selection of the ADC conversion rate|0x0| | ||
+ | |:::|:::|:::|0x0 = 30 KSPS conversion rate|:::| | ||
+ | |:::|:::|:::|0x1 = 600 KSPS conversion rate (recommended)|:::| | ||
+ | |RSVD|[7]|R|Reserved (read as zero, do not modify)|0x0| | ||
+ | |C_TIME|[6:4]|RW|Selection of the ADC conversion mode|0x2| | ||
+ | |:::|:::|:::|0x0 = 1 times conversion to get the data|:::| | ||
+ | |:::|:::|:::|0x1 = 2 times conversion|:::| | ||
+ | |:::|:::|:::|0x2 = 4 times conversion|:::| | ||
+ | |:::|:::|:::|0x3 = 8 times conversion|:::| | ||
+ | |:::|:::|:::|0x4 = 16 times conversion|:::| | ||
+ | |:::|:::|:::|0x5 = 32 times conversion|:::| | ||
+ | |:::|:::|:::|0x6 = 64 times conversion|:::| | ||
+ | |:::|:::|:::|NOTE: ADC_DAT register is updated on an average with a sum after 1, 2, 4, 8, 16, 32 or 64 times conversion.|:::| | ||
+ | |ACH_SEL|[3:0]|RW|Analog input channel selection|0x0| | ||
+ | |:::|:::|:::|0x0 = Channel 0|:::| | ||
+ | |:::|:::|:::|0x1 = Channel 1|:::| | ||
+ | |:::|:::|:::|0x2 = Channel 2|:::| | ||
+ | |:::|:::|:::|0x3 = Channel 3|:::| | ||
+ | |:::|:::|:::|0x4 = Channel 4|:::| | ||
+ | |:::|:::|:::|0x5 = Channel 5|:::| | ||
+ | |:::|:::|:::|0x6 = Channel 6|:::| | ||
+ | |:::|:::|:::|0x7 = Channel 7|:::| | ||
+ | |:::|:::|:::|0x8 = Channel 8|:::| | ||
+ | |:::|:::|:::|0x9 = Channel 9|:::| | ||
+ | |:::|:::|:::|0xA to 0xF = Reserved|:::| | ||
+ | |||
+ | **ADC_STATUS** | ||
+ | |||
+ | * Base Address: 0x12D1_0000 | ||
+ | * Address = Base Address + 0x00048, Reset Value = 0x0000_0000 | ||
+ | |||
+ | ^Name^Bit^Type^Description^Reset Value^ | ||
+ | |RSVD|[31:3]|R|Reserved (read as zero, do not modify)|0x0| | ||
+ | |FLAG|[2]|R|End of conversion flag|-| | ||
+ | |:::|:::|:::|0x0 = A/D conversion in process|:::| | ||
+ | |:::|:::|:::|0x1 = End of A/D conversion|:::| | ||
+ | |RSVD|[1:0]|R|Reserved (read as zero, do not modify)|0x0| | ||
+ | |||
+ | **ADC_DAT** | ||
+ | |||
+ | * Base Address: 0x12D1_0000 | ||
+ | * Address = Base Address + 0x000C, Reset Value = 0x0000_0000 | ||
+ | |||
+ | ^Name^Bit^Type^Description^Reset Value^ | ||
+ | |RSVD|[31:12]|R|Reserved (read as zero, do not modify)|0x0| | ||
+ | |ADCDAT|[11:0]|R|ADC conversion data value (0x000 to 0xFFF)|-| | ||
+ | |||
+ | **ADC_INT_EN** | ||
+ | |||
+ | * Base Address: 0x12D1_0000 | ||
+ | * Address = Base Address + 0x0010, Reset Value = 0x0000_0000 | ||
+ | |||
+ | ^Name^Bit^Type^Description^Reset Value^ | ||
+ | |RSVD|[31:1]|R|Reserved (read as zero, do not modify)|0x0| | ||
+ | |INT_EN|[0]|RW|0x0 = Disables interrupt|0x0| | ||
+ | |:::|:::|:::|0x1 = Enables interrupt|:::| | ||
+ | |||
+ | **ADC_INT_STATUS** | ||
+ | |||
+ | * Base Address: 0x12D1_0000 | ||
+ | * Address = Base Address + 0x0014, Reset Value = 0x0000_0000 | ||
+ | |||
+ | ^Name^Bit^Type^Description^Reset Value^ | ||
+ | |RSVD|[31:1]|R|Reserved (read as zero, do not modify)|0x0| | ||
+ | |INT_STATUS|[0]|RW|When Read|0x0| | ||
+ | |:::|:::|:::|0x0 = Interrupt is not generated|:::| | ||
+ | |:::|:::|:::|0x1 = Interrupt is generated|:::| | ||
+ | |:::|:::|:::|When Write|:::| | ||
+ | |:::|:::|:::|0x0 = No action|:::| | ||
+ | |:::|:::|:::|0x1 = This bit is cleared|:::| | ||
+ | |||
+ | |||
+ | **ADC_VERSION** | ||
+ | |||
+ | * Base Address: 0x12D1_0000 | ||
+ | * Address = Base Address + 0x0020, Reset Value = 0x8000_0008 | ||
+ | |||
+ | ^Name^Bit^Type^Description^Reset Value^ | ||
+ | |ADC_VERSION_INFO|[31:0]|R|ADC Version Information|0x8000_0008| |