DMA (Direct Memory Access) is a data exchange mode that directly accesses data from memory without going through the CPU. In situations where a large amount of data needs to be exchanged, using DMA well can greatly improve system performance because DMA operations hardly occupy CPU resources. S3C2440 provides 4 channels of DMA
Each DMA channel can handle data transfer in the following four situations:
(1) Both the source device and the destination device are on the system bus APB
(2) The source device is on the system bus and the destination device is on the peripheral bus
(3) The source device is on the peripheral bus and the destination device is on the system bus
(4) Both the source device and the destination device are on the peripheral bus AHB
The following DMA driver uses the fourth type. The memory belongs to the AHB bus. We plan to open two continuous spaces in the memory as the source and the destination. We use two methods to write the data in the source to the destination. One method is to let the CPU do it, and the other method is to let DMA do it:
#include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_CPY_NO_DMA 0 #define MEM_CPY_DMA 1 #define BUF_SIZE (512*1024) #define DMA0_BASE_ADDR 0x4B000000 #define DMA1_BASE_ADDR 0x4B000040 #define DMA2_BASE_ADDR 0x4B000080 #define DMA3_BASE_ADDR 0x4B0000C0 struct s3c_dma_regs { unsigned long disrc; unsigned long disrcc; unsigned long didst; unsigned long didstc; unsigned long dcon; unsigned long dstat; unsigned long dcsrc; unsigned long dcdst; unsigned long dmasktrig; }; static int major = 0; static char *src; static u32 src_phys; static char *dst; static u32 dst_phys; static struct class *cls; static volatile struct s3c_dma_regs *dma_regs; static DECLARE_WAIT_QUEUE_HEAD(dma_waitq); /* Interrupt event flag, the interrupt service routine sets it to 1, and ioctl clears it to 0 */ static volatile int ev_dma = 0; static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int i; memset(src, 0xAA, BUF_SIZE); memset(dst, 0x55, BUF_SIZE); switch (cmd) { //This is non-DMA mode case MEM_CPY_NO_DMA : { for (i = 0; i < BUF_SIZE; i++) dst[i] = src[i]; //CPU directly copies the source to the destination if (memcmp(src, dst, BUF_SIZE) == 0) { printk("MEM_CPY_NO_DMA OKn"); } else { printk("MEM_CPY_DMA ERRORn"); } break; } //This is DMA mode case MEM_CPY_DMA : { ev_dma = 0; /* Tell DMA the source, destination, and length */ /* Regarding the specific situation of the following registers, we will explain it in detail in Note 3*/ dma_regs->disrc = src_phys; /* physical address of source*/ dma_regs->disrcc = (0<<1) | (0<<0); /* Source is on AHB bus, source address increments*/ dma_regs->didst = dst_phys; /* Physical address of destination*/ dma_regs->didstc = (0<<2) | (0<<1) | (0<<0); /* Destination is on AHB bus, destination address increments*/ dma_regs->dcon = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0); /* AHP bus synchronization, enable interrupt, full speed transmission (BIT27), software trigger (BIT23), */ /* Start DMA */ dma_regs->dmasktrig = (1<<1) | (1<<0); //Open the channel and start the software /* How do I know when DMA is complete? */ /* Sleep */ wait_event_interruptible(dma_waitq, ev_dma); if (memcmp(src, dst, BUF_SIZE) == 0) { printk("MEM_CPY_DMA OKn"); } else { printk("MEM_CPY_DMA ERRORn"); } break; } } return 0; } static struct file_operations dma_fops = { .owner = THIS_MODULE, .ioctl = s3c_dma_ioctl, }; static irqreturn_t s3c_dma_irq(int irq, void *devid) { /* wake up */ ev_dma = 1; wake_up_interruptible(&dma_waitq); /* wake up the sleeping process*/ return IRQ_HANDLED; } static int s3c_dma_init(void) { /* Register an interrupt here, this interrupt will occur when DMA data transfer is completed*/ if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", 1)) { printk("can't request_irq for DMAn"); return -EBUSY; } /* Allocate the buffers corresponding to SRC and DST: We know that kmalloc function can be used to open up space in the kernel, but dma_alloc_writecombine is used here. Why? This is because the logical address of the space opened by kmalloc is continuous, but its actual physical address may not be continuous. When DMA transfers data, the physical address is required to be continuous, and dma_alloc_writecombine meets this requirement. The prototype of this function is: dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) where size represents the size of the opened space, handle represents the physical address of the opened space, and the return value is the logical address of the opened space*/ src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL); // source if (NULL == src) { printk("can't alloc buffer for srcn"); free_irq(IRQ_DMA3, 1); return -ENOMEM; } dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL); //Purpose if (NULL == dst) { free_irq(IRQ_DMA3, 1); dma_free_writecombine(NULL, BUF_SIZE, src, src_phys); printk("can't alloc buffer for dstn"); return -ENOMEM; } major = register_chrdev(0, "s3c_dma", &dma_fops); //Register character device /* To automatically create device nodes */ cls = class_create(THIS_MODULE, "s3c_dma"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); /* /dev/dma */ dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs)); //This is to map the DMA control register to kernel space return 0; } static void s3c_dma_exit(void) { iounmap(dma_regs); class_device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "s3c_dma"); dma_free_writecombine(NULL, BUF_SIZE, src, src_phys); dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys); free_irq(IRQ_DMA3, 1); } module_init(s3c_dma_init); module_exit(s3c_dma_exit); MODULE_LICENSE("GPL"); test program: #include #include #include #include #include #include /* ./dma_test nodma * ./dma_test dma */ #define MEM_CPY_NO_DMA 0 #define MEM_CPY_DMA 1 void print_usage(char *name) { printf("Usage:n"); printf("%s } int main(int argc, char **argv) { int fd; if (argc != 2) { print_usage(argv[0]); return -1; } fd = open("/dev/dma", O_RDWR); if (fd < 0) { printf("can't open /dev/dman"); return -1; } if (strcmp(argv[1], "nodma") == 0) { while (1) { ioctl(fd,MEM_CPY_NO_DMA); } } else if (strcmp(argv[1], "dma") == 0) { while (1) { ioctl(fd,MEM_CPY_DMA); } } else { print_usage(argv[0]); return -1; } return 0; } Test Methods: # insmod dma.ko //Load driver # cat /proc/interrupts //View interrupts CPU0 30: 52318 s3c S3C2410 Timer Tick 33: 0 s3c s3c-mci 34: 0 s3c I2SSDI 35: 0 s3c I2SSDO 36: 0 s3c s3c_dma 37: 12 s3c s3c-mci 42: 0 s3c ohci_hcd:usb1 43: 0 s3c s3c2440-i2c 51: 2725 s3c-ext eth0 60: 0 s3c-ext s3c-mci 70: 97 s3c-uart0 s3c2440-uart 71: 100 s3c-uart0 s3c2440-uart 83: 0 - s3c2410-wdt Err: 0 # ls /dev/dma //View device /dev/dma # ./dmatest //This will print the usage Usage: ./dmatest # ./dmatest dma //Copy via DMA, CPU can do other things MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK # ./dmatest nodma //CPU copy, various competing CPUs MEM_CPY_NO_DMA MEM_CPY_NO_DMA MEM_CPY_NO_DMA ———————————————————————————————————————————————————————————————————————————— After getting familiar with DMA and register configuration, let's see how to use DMA to play audio when running S3C2440: Reprinted from: http://blog.csdn.net/zhaocj/article/details/5583935 Next, we will use DMA to play audio. Since DMA is used, system resources are not occupied during playback. We can easily implement various sound operations without affecting the playback effect, such as volume increase and decrease, mute, pause, etc. Here, it is also necessary to emphasize that when using DMA to transfer data, the maximum byte size that can be transferred at one time is: DSZ×TSZ×TC, DSZ represents the data size (byte, half word or word, that is, 1, 2 or 4), TSZ represents the transfer size (unit transfer or burst transfer, that is, 1 or 4), and TC represents the transfer count value (that is, the data stored in the lower 20 bits of register DCONn). Therefore, if the byte size to be transferred exceeds the size of the product of these three parameters, further processing is required. In the program we give, we have considered this aspect. The following is a specific program, in which we use UART to play, stop, pause, mute, increase and decrease the volume of audio signals. …… …… //A pure audio data array unsigned char music[] = { 0xF9, 0xFF, 0xF5, 0xFF, 0xF8, 0xFF, 0xF8, 0xFF, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0xFF, 0xF9, 0xFF, 0xF6, 0xFF, 0xF6, 0xFF, 0xFA, 0xFF, 0xFD, 0xFF, 0xFA, 0xFF, 0xFA, 0xFF, 0xFF, 0xF7, 0xFF, 0xF6, 0xFF, …… …… }; int result; int remainder; char flag; char cmd; char play_state; void __irq uartISR(void) { char ch; rSUBSRCPND |= 0x1; rSRCPND |= 0x1<<28; rINTPND |= 0x1<<28; ch=rURXH0; switch(ch) { case 0x55: //play cmd = 1; break; case 0x1: //Mute cmd = 0x11; break; case 0x2: //volume up cmd = 0x12; break; case 0x3: //Volume down cmd = 0x13;
Previous article:Study Notes --- S3C2440 NANDFLASH operation principle and test code analysis
Next article:S3C2440's RAM and boot process!
Recommended ReadingLatest update time:2024-11-23 15:23
- Naxin Micro and Xinxian jointly launched the NS800RT series of real-time control MCUs
- How to learn embedded systems based on ARM platform
- Summary of jffs2_scan_eraseblock issues
- Application of SPCOMM Control in Serial Communication of Delphi7.0
- Using TComm component to realize serial communication in Delphi environment
- Bar chart code for embedded development practices
- Embedded Development Learning (10)
- Embedded Development Learning (8)
- Embedded Development Learning (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Intel promotes AI with multi-dimensional efforts in technology, application, and ecology
- ChinaJoy Qualcomm Snapdragon Theme Pavilion takes you to experience the new changes in digital entertainment in the 5G era
- Infineon's latest generation IGBT technology platform enables precise control of speed and position
- Two test methods for LED lighting life
- Don't Let Lightning Induced Surges Scare You
- Application of brushless motor controller ML4425/4426
- Easy identification of LED power supply quality
- World's first integrated photovoltaic solar system completed in Israel
- Sliding window mean filter for avr microcontroller AD conversion
- What does call mean in the detailed explanation of ABB robot programming instructions?
- STMicroelectronics discloses its 2027-2028 financial model and path to achieve its 2030 goals
- 2024 China Automotive Charging and Battery Swapping Ecosystem Conference held in Taiyuan
- State-owned enterprises team up to invest in solid-state battery giant
- The evolution of electronic and electrical architecture is accelerating
- The first! National Automotive Chip Quality Inspection Center established
- BYD releases self-developed automotive chip using 4nm process, with a running score of up to 1.15 million
- GEODNET launches GEO-PULSE, a car GPS navigation device
- Should Chinese car companies develop their own high-computing chips?
- Infineon and Siemens combine embedded automotive software platform with microcontrollers to provide the necessary functions for next-generation SDVs
- Continental launches invisible biometric sensor display to monitor passengers' vital signs
- i.MX6ULL Embedded Linux Development 3-Kernel Porting
- Summary of the project "Automatic Control of Small Hydropower Stations"
- [RISC-V MCU CH32V103 Review] + Use of TIM timer
- National Technology General MCU BOOT Interface Basic Instruction Usage Guide
- "【TGF4042 Signal Generator】" Signal Output Accuracy Measurement
- Xinyuan Electronics Large Screen LED Display Design/Manufacture MCU and Embedded System
- Core Route FPGA Learning Kit [ADV7123/GM7123 VGA Video Output Module] Schematic Diagram
- Thanksgiving is here. Who do you want to thank the most today?
- Live broadcast has ended | Littelfuse [How to improve the safety and reliability of electronic equipment in smart buildings]
- Circuit board manufacturing experience