Add ADC driver
Editor: Since the kernel does not support the ADC driver of S3C2440, the ADC driver is added here. The friendly manual introduces this in quite detail, so just follow the manual. The code inside also has detailed comments. The ADC driver belongs to the character type device, and is implemented here as a miscellaneous device.
1. About the ADC and touch screen interface of S3C2440
The Linux-2.6.32.2 kernel does not provide an ADC driver that supports S3C2440, so we designed one ourselves. This driver is relatively simple and belongs to a character device. It is located in the drivers/char directory. The driver file name is: mini2440_adc.c. In the S3C2440 chip, the AD input and touch screen interface use a common A/D converter, see Chapter 16 of the 2440 chip manual, as shown in the figure.
2 Add ADC driver in the kernel
If the ADC driver and the touch screen driver want to coexist, the problem of sharing the "A/D converter" resources must be solved. Therefore, a global "ADC_LOCK" semaphore is declared in the ADC driver. The content and annotations of the ADC driver are as follows:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
; Self-defined header files, because the native kernel does not include
#include "s3c24xx-adc.h"
#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk(__FUNCTION__"(%d): ",__LINE__);printk(##x);}
#else
#define DPRINTK(x...) (void)(0)
#endif
;Define the ADC conversion device name, which will appear in /dev/adc
#define DEVICE_NAME "adc"
static void __iomem *base_addr;
;Define ADC device structure
typedef struct {
wait_queue_head_t wait;
int channel;
int prescale;
}ADC_DEV;
;Declare a global semaphore to share the A/D converter with the touch screen driver
DECLARE_MUTEX(ADC_LOCK);
;Status variable for whether the ADC driver owns the A/D converter resource
static int OwnADC = 0;
static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;
static struct clk *adc_clock;
;Define ADC related registers
#define ADCCON (*(volatile unsigned long *)(base_addr + S3C2410_ADCCON)) //ADC control
#define ADCTSC (*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC)) //ADC touch screen
control
#define ADCDLY (*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY)) //ADC start or Interval
Delay
#define ADCDAT0 (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT0)) //ADC conversion
data
0
#define ADCDAT1 (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT1)) //ADC conversion
data 1
#define ADCUPDN (*(volatile unsigned long *)(base_addr + 0x14)) //Stylus Up/Down interrupt status
#define PRESCALE_DIS (0 << 14)
#define PRESCALE_EN (1 << 14)
#define PRSCVL(x) ((x) << 6)
#define ADC_INPUT(x) ((x) << 3)
#define ADC_START (1 << 0)
#define ADC_ENDCVT (1 << 15)
; Define the “turn on AD input” macro. It is not made into a function because it is relatively simple.
#define START_ADC_AIN(ch, prescale)
do{
ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ;
ADCCON |= ADC_START;
}while(0)
;ADC interrupt handler function
static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
{
;If the ADC driver owns the "A/D converter" resource, read the conversion result from the ADC registerif
(OwnADC) {
adc_data = ADCDAT0 & 0x3ff;
ev_adc = 1;
wake_up_interruptible(&adcdev.wait);
}
return IRQ_HANDLED;
}
;ADC read function, generally corresponds to the device read function (read) of the user layer/application
layerstatic ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
char str[20];
int value;
size_t len;
;Judge whether the "A/D converter" resource is availableif
(down_trylock(&ADC_LOCK) == 0) {
OwnADC = 1; //Mark the "A/D converter" resource status as availableSTART_ADC_AIN
(adcdev.channel, adcdev.prescale); //Start conversion
wait_event_interruptible(adcdev.wait, ev_adc); //Wait for conversion result through terminal
ev_adc = 0;
DPRINTK("AIN[%d] = 0x%04x, %dn", adcdev.channel, adc_data, ADCCON & 0x80 ? 1:0);
;Assign the conversion result to value so as to pass it to the user layer/application layer
value = adc_data;
;Release "A/D converter" resources
OwnADC = 0;
up(&ADC_LOCK);
} else {
;There is no "A/D converter" resource, assign it to "-1"
value = -1;
}
len = sprintf(str, "%dn", value);
if (count >= len) {
;Pass the conversion result to the user layer/application layer
int r = copy_to_user(buffer, str, len);
return r ? r : len;
} else {
return -EINVAL;
}
}
;The function of opening the ADC device generally corresponds to the open of the user mode program
static int s3c2410_adc_open(struct inode *inode, struct file *filp)
{
;Initialize the interrupt queue
init_waitqueue_head(&(adcdev.wait));
;The default channel is "0"
adcdev.channel=0;
adcdev.prescale=0xff;
DPRINTK( "adc openedn");
return 0;
}
static int s3c2410_adc_release(struct inode *inode, struct file *filp)
{
DPRINTK( "adc closedn");
return 0;
}
static struct file_operations dev_fops = {
owner: THIS_MODULE,
open: s3c2410_adc_open,
read: s3c2410_adc_read,
release: s3c2410_adc_release,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
base_addr=ioremap(S3C2410_PA_ADC,0x20);
if (base_addr == NULL) {
printk(KERN_ERR "Failed to remap register blockn");
return -ENOMEM;
}
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_ERR "failed to get adc clock sourcen");
return -ENOENT;
}
clk_enable(adc_clock);
/* normal ADC */
ADCTSC = 0;
;注册中断
ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);
if (ret) {
iounmap(base_addr);
return ret;
}
;注册设备
ret = misc_register(&misc);
printk (DEVICE_NAME"tinitializedn");
return ret;
}
static void __exit dev_exit(void)
{
;Release interrupt
free_irq(IRQ_ADC, &adcdev);
iounmap(base_addr);
if (adc_clock) {
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
}
misc_deregister(&misc);
}
;Export semaphore "ADC_LOCK" for touch screen driver to use
EXPORT_SYMBOL(ADC_LOCK);
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
The above driver also includes a simple header file "s3c24xx-adc.h", which is also in the drivers/char directory. The content is:
#ifndef _S3C2410_ADC_H_
#define _S3C2410_ADC_H_
#define ADC_WRITE(ch, prescale) ((ch)<<16|(prescale))
#define ADC_WRITE_GETCH(data) (((data)>>16)&0x7)
#define ADC_WRITE_GETPRE(data) ((data)&0xff)
#endif /* _S3C2410_ADC_H_ */
The above driver also includes a simple header file "s3c24xx-adc.h", which is also in the drivers/char directory. The content is:
#ifndef _S3C2410_ADC_H_
#define _S3C2410_ADC_H_
#define ADC_WRITE(ch, prescale) ((ch)<<16|(prescale))
#define ADC_WRITE_GETCH(data) (((data)>>16)&0x7)
#define ADC_WRITE_GETPRE(data) ((data)&0xff)
#endif /* _S3C2410_ADC_H_ */
Then open the drivers/char/Makefile file and add the ADC driver target module at about line 114:
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
obj-$(CONFIG_MINI2440_ADC) += mini2440_adc.o
# Files generated that shall be removed upon make clean
clean-files := consolemap_deftbl.c defkeymap.c
and then open the drivers/char/Kconfig file and add the ADC driver configuration option:
config DEVKMEM
bool "/dev/kmem virtual device support"
default y
help
Say Y here if you want to support the /dev/kmem device. The
/dev/kmem device is rarely used, but can be used for certain
kind of kernel debugging operations.
When in doubt, say "N".
config MINI2440_ADC
bool "ADC driver for FriendlyARM Mini2440 development boards"
depends on MACH_MINI2440
default y if MACH_MINI2440
help
this is ADC driver for FriendlyARM Mini2440 development boards
Notes: the touch-screen-driver required this option
config BFIN_JTAG_COMM
tristate "Blackfin JTAG Communication"
depends on BLACKFIN
help
like this , we added the ADC to the kernel Driver, now execute the command line in the kernel source code directory: make menuconfig, select the following submenu items in turn, and find the ADC driver configuration option you just added:
Device Drivers --->
Character devices --->
Press the space bar to select the ADC configuration option , then exit to save the selected configuration, execute in the command line: make zImage, arch/arm/boot/zImage will be generated, use supervivi's "k" command to burn it to the development board.
3 ADC test program
Here we use the file system that comes with Friendly Arm, which contains an adc-test command. Its source code is as follows:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
Previous article:Linux-2.6.32 transplanted on mini2440 development board - modify the Linux boot logo
Next article:Linux-2.6.32 ported to add touch screen driver on mini2440 development board
Recommended ReadingLatest update time:2024-11-16 09:43
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- Recruiting hardware development engineers Annual salary: 120,000-300,000 | Experience: 3-8 years | Work location: Beijing, Chengdu, Wuhan
- Conformal Array for Radar Missile Seeker
- TMS570LS1224_GY30 light sensor driver
- Good information on clock division
- A question about the STM32L073 port A driver
- 【Gravity:AS7341 Review】Color Temperature Perception Measurement: Analysis of Three Chip Performance
- Problems with devices that measure temperature
- Huawei Science Comic: How do Bluetooth, Wi-Fi, and GNSS work together? Connectivity Chip
- InstaSPIN Brushless DC (BLDC) Lab
- [Synopsys IP Resources] How to cope with the challenges of HPC SoC development in the era of surging data volume