sd卡 polling和ma ema sma dma的区别别

热门搜索:
当前位置:&&>&&>&&>&商品信息
EM-LPC1788评估板
工具型号:EM-LPC1788
生 产 商:
点 击 数:
分享与收藏
工具简介:EM-LPC1788评估板
详细说明:&基于NXP LPC1788芯片(Cortex-M3内核)
&&&&&& EM-LPC1788是一款基于恩智浦LPC1788(Cortex-M3内核)处理器的全功能评估套件。开发板板载USB转串口电路,用户只需一根USB线即可完成ISP烧写过程,同时开发板支持多种供电方式。LPC1788是NXP半导体针对各种高级通讯,高质量图像显示等应用场合而设计的一款具有高集成度,以Cortex-M3为内核的微控制器,该微控制器包含有LCD控制器,10/100的以太网EMAC,USB全速Device/Host/OTG控制器, CAN总线控制器,SPI,SSP,IIC,IIS以及外部存储控制器EMC等资源,特适用于工业控制和医疗系统的应用场合。
NXP LPC1788,Cortex-M3内核,主频120MHz.
512KB内部Flash96KB 内部SRAM
128MB外扩Nandflash4MB 外扩Norflash32MB外扩SDRAM1MB 外扩SRAM
20Pin JTAG调试接口,支持JTAG模式,SWD模式
USB转RS232接口 1个3线RS232串行口 1个9线RS232串行口 2路CAN总线接口 SD Card接口 I2S音频输入/接口 8个用户LED灯 USB Host *1,USB Device*110/100M Ethernet 接口 TFT- LCD屏接口(标配4.3寸屏)四线电阻式触摸 ADC可调电位器 RTC(带后备电池)64个I/O扩展 (CPU资源) 20PIN 标准JTAG调试接口
4个用户按键
支持USB2UART mini USB/USB1/USB2/EXT 5V供电方式 .
01 ADC_Interrupt
描述了在中断模式下如何使用ADC转换功能
02 ADC_Polling
描述了在轮询模式下如何使用ADC转换功能
03 Can_Selftest
用来测试CAN的自测模式
04 Crc_Demo
描述了如何在LPC1788上应用CRC
05 Dac_Dma
描述了怎样通过DMA和DAC转换器进行发送和接收数据
06 Dac_SineWave
描述了怎样通过DMA产生正弦波
07 DMA_Flash2Ram
描述了怎样通过从Flash向Ram传输数据来测试GPDMA功能
08 EEprom_Demo
本例程描述了LPC1788开发板上EEPROM的工作机制
09 GPIO_Interrupt
测试GPIO中断功能
10 GPIO_LedBlinky
用来测试GPIO中断功能驱动LED
11 Nvic_VectorTableRelocation
描述了如何重定位向量表
12 NandFlash
测试了外扩NandFlash芯片
13 NorFlash
测试了外扩NorFlash芯片
测试了外扩SRAM芯片
测试了外扩SDRAM芯片
16 Pwm_SingleEdge
描述了如何在单边缘模式下使用PWM信号
17 Pwm_DualEdge
描述了怎样在双边缘模式下产生PWM信号
18 Pwm_MatchInterrupt
描述了怎样在中断模式下使用PWM匹配功能
19 Rtc_Alarm
描述了怎样在秒计数器增量中断中产生中断,并每10秒钟产生一次报警中断
20 Systick_10msBase
描述了怎样配置系统时钟计时器达到每10ms产生一次中断
21 TIMER_MatchInterrupt
描述了在中断模式下如何让利用定时器匹配来产生具体的时间
22 PWR_Sleep
描述了怎样使系统进入睡眠模式并通过外部中断唤醒
23 PWR_DeepSleep
描述了如何使系统进入深度睡眠模式并通过外部中断唤醒
24 Emac_EasyWeb
测试了网口,实现了一个简单的WEB应用
25 Lcd_LQ043T3DX0A
为在LCD上显示特定画面,测试LCD
26 Lcd_touchscreen
触摸屏测试
TouchScreen
27 SSP_Touchscreen
描述了如何通过SSP捕捉触摸屏坐标
28 Wdt_Interrupt
描述了怎样在一个具体时间后利用WDT产生中断
29 Wdt_Reset
描述了怎样用WDT早某一具体时间后产生芯片重启
30 Mci_CidCard
测试了LPC1788的SD卡模块
31 Usb_MassStorage
描述了开发板在PC上映射为一个可移动磁盘
32 Usb_VirtualCom
描述了怎样配置USB设备为一个虚拟的COM端口
33 IIS_Audio
测试开发板的音频模块
34_IIC_EEPROM
通过IIC读写外部EEPROM
35_UCOSII_V2.54
Ucos实时系统例程测试
地址:深圳市福田区深南中路3018号中航广场世纪汇3116室 电话:86-755- 传真:86-755-
深圳市鼎森电子科技有限公司 All Rights Reserved 403 Forbidden
403 Forbidden1097人阅读
CC2541、CC2640(41)
uart dma实现方式分析(转载)
在做uart DMA驱动期间,前后加起来也有1个月左右的时间,总的来说比较全面的了解了uart,DMA的工作原理。
在调试中,遇到了最大问题就是关于DMA操作这快的不熟悉,导致浪费了很多的时间和精力。对UART,DMA的工作原理可以看LDD3,或者设备驱动一书中也有
详细介绍,uart-dma驱动的移植可以参好drivers/serial/bfin_5xx.c
&&& &UART-DMA总体思路如下:
1.本UART-DMA采用的是,DMA+POLLING(轮询)的方式,其中轮询采用的是定时器。
2.在驱动中发送DMA需要用户层主动发起;
&&& 接收DMA:在UART open操作时enable_dma(rx),等待接收数据,当到达DMA counter值时,产生DMA中断,
&&&&&&&&&&&&&&&&& 在DMA中断处理中 做如下处理 disable_dma(rx),CPU取走数据,enable_dma(rx),以便接收下次数据;
3.POLLING(轮询)在这里的作用,如果在接收DMA过程中,还没有达到DMA counter值时,不会产生中断,这样接收到的数据
& 就不能及时的被CPU取走,为了解决这个问题,采用轮询--定时器方式,通过每50ms 读取DMA counter寄存器一次看是否有数据上的
& 变化,如果有这CPU把数据取走,没有则不做处理。
4.错误处理:如果有错误(溢出、校验……),产生错误中断,在错误处理中断中有相应的处理。
&&& & 现分析如下:
1.内核起来之后,最开始经过& setup_arch() /* init/main.c */--&arch_mem_init()--&plat_mem_setup()--&clx_serial_setup()
在clx_serial_setup()函数中
void __init clx_serial_setup(void)
*******************
&&& REG8(UART0_FCR) |= UARTFCR_UUE;&& //设置uart0的 FIFO 控制寄存器,disable UART
&&& REG8(UART1_FCR) |= UARTFCR_UUE;&& //设置uart1的 FIFO 控制寄存器,disable UART
&&& s.type = PORT_16550A;&&& &&& //设置uart的其它属性
&&& s.iotype = UPIO_MEM;
&&& s.regshift = 2;
&&& s.fifosize = 1;
&&& s.uartclk= clx_clocks.
&&& s.flags = STD_COM_FLAGS;
#if !defined(CONFIG_CLX_UART0_REMR)
&&& s.line =
&&& s.irq = IRQ_UART0;
&&& s.membase = (unsigned char __iomem *)UART0_BASE;&
&&& if (early_serial_setup(&s) != 0)&&&&&&&& //调用early_serial_setup()来初始化串口0
&&&&&&& printk(KERN_ERR &Serial ttyS0 setup failed!\n&);
&&& line++;
#if !defined(CONFIG_CLX_UART1_REMR)
&&& s.line =
&&& s.line = 1;
&&& s.irq = IRQ_UART1;
&&& s.membase = (unsigned char __iomem *)UART1_BASE;
&&& if (early_serial_setup(&s) != 0)&&& &&& //调用early_serial_setup()来初始化串口1
&&&&&&& printk(KERN_ERR &Serial ttyS1 setup failed!\n&);
2.early_serial_setup()函数是在你自己写的驱动里边定义的
static struct uart_8250_port cq8401_serial_ports[UART_NR];
int __init early_serial_setup(struct uart_port *port)
&&& if (port-&line &= ARRAY_SIZE(cq8401_serial_ports))
&&&&&&& return -ENODEV;
&&& cq8401_isa_init_ports();&& //初始化相应的UART
&&& cq8401_serial_ports[port-&line].port&&& = *& //将传过来的参数port,赋值给cq8401_serial_ports[port-&line].port
&&& cq8401_serial_ports[port-&line].port.ops&&& = &cq8401_serial8250_
&&& &&& &&& &&& &&& &&& &&& //同样,相应的OPS操作赋值
&&& return 0;
到这里,setup_arch()里面UART的初始化到这里就结束了。
3.接着是内核里面
console_init() /* init/main.c */ 的初始化
void __init console_init(void)
&&& initcall_t *
&&& /* Setup the default TTY line discipline. */
&&& (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
&&&& * set up the console device so that later boot sequences can&
&&&& * inform about problems etc..
&&& call = __con_initcall_
&&& while (call & __con_initcall_end) {
&&&&&&& (*call)();
&&&&&&& call++;
关于console 初始化,在网上找了一段描述,就直接套用过来了。
网址:http://blog.csdn.net/lights_joy/archive//3855530.aspx
************************************************
&&& 在linux初始化过程中,除非启用了early console,否则直到console_init调用之前是没有任何输出的,它们的输出都放在__log_buf这个缓冲内的,在console_init调用时再将这个缓冲区内的数据一次性输出。
&&& console和串口的初始化操作应该是由__con_initcall_start到__con_initcall_end之间的函数调用来完成的
&&& 在linux的头文件中搜索initcall,发现了这样一个定义(include\linux\init.h):
#define console_initcall(fn) \
&&&& static initcall_t __initcall_##fn \
&&&& __attribute_used__ __attribute__((__section__(&.con_initcall.init&)))=fn
在此函数中的所有操作都是与硬件无关的,据此可以猜测,应该还有一个与硬件相关的文件,且在此文件中应该使用console_initcall这个宏。
&&& 在内核源码中搜索console_initcall,会找到在clx_uart_dma.c中有& console_initcall(cq8401_console_init);这样一句话。
&&& 关于具体console的初始化在这里不必多说,具体的可以看下别人的分析。
**************************************************
4. console初始化好了以后,后面就是UART驱动初始化的时候了
static int __init cq8401_serial8250_init(void)
&&& int ret,
**************
&&& cq8401_isa_init_ports();& //相应串口的初始化
&&& ret = uart_register_driver(&cq8401_serial_reg);& //串口驱动的注册
&&& if (ret)
&&&&&&&&&&&
cq8401_serial_init_ports();&&&& //端口的添加,定义如下
**************
static void __exit cq8401_serial8250_exit(void)
&&&&&&& struct uart_8250_port *
&&&&&&& for (i = 0; i & nr_ i++)
&&&&&&&&&&& up= &cq8401_serial_ports[i];
&&&&&&&&&&& uart_remove_one_port (&cq8401_serial_reg,&up-&port );
&&& uart_unregister_driver(&cq8401_serial_reg);
module_init(cq8401_serial8250_init);
module_exit(cq8401_serial8250_exit);
*******************************************
cq8401_serial_init_ports()函数分析
static void& cq8401_serial_init_ports(void)
&&& for (i = 0; i & nr_ i++) {
&&&&&&& struct uart_8250_port *up = &cq8401_serial_ports[i];&& //前面early_serial_setup()中以对cq8401_serial_ports[i]赋值
&&&&&&& up-&port.line&&& =
&&&&&&& uart_add_one_port (&cq8401_serial_reg, &up-&port);&&& //端口的添加
&&& 在这里uart驱动的初始化到这里就结束了,下面重点分析 uart对应的OPS操作。
5. UART OPS操作分析
static struct uart_ops cq8401_serial8250_pops = {
&&& .tx_empty&& = cq8401_serial8250_tx_empty,
&&& .set_mctrl& = cq8401_serial8250_set_mctrl,
&&& .get_mctrl& = cq8401_serial8250_get_mctrl,
&&& .stop_tx&&& = cq8401_serial8250_stop_tx,
&&& .start_tx&& = cq8401_serial8250_start_tx,
&&& .stop_rx&&& = cq8401_serial8250_stop_rx,
&&& .enable_ms& = cq8401_serial8250_enable_ms,
&&& .break_ctl& = cq8401_serial8250_break_ctl,
&&& .startup&&& = cq8401_serial8250_startup,
&&& .shutdown&& = cq8401_serial8250_shutdown,
&&& .set_termios&&& = cq8401_serial8250_set_termios,
//& .pm&&&& = cq8401_serial8250_pm,
&&& .type&&&&&& = cq8401_serial8250_type,
&&& .release_port&& = cq8401_serial8250_release_port,
&&& .request_port&& = cq8401_serial8250_request_port,
&&& .config_port&&& = cq8401_serial8250_config_port,
&&& .verify_port&&& = cq8401_serial8250_verify_port,
& 在分析之前,先看看uart_8250_port 结构体 的定义
struct uart_8250_port {
.............
#ifdef CONFIG_SERIAL_CQ8401_DMA
&&& struct tx_buffer&&&&& tx_&&&&&&&&&&
&&& int&&&&&&&&&&&&&&&&&&&& tx_
&&& int&&&&&&&&&&&&&&&&&&&& tx_
&&& wait_queue_head_t&&&&&& tx_wait_& //UART发送队列
&&& unsigned int&&&&&&& rx_dma_&& //UART接收DMA地址
//&&& struct circ_buf&&&& rx_dma_&&&
&&& struct timer_list&&&&&& rx_dma_& //接收定时器
&&& unsigned int&&&&&&&&&&& tx_dma_& //发送DMA通道
&&& unsigned int&&&&&&&&&&& rx_dma_& //接收DMA通道
&&& unsigned int&&&&&&&&&&& tx_&&&&& //接收 DMA SOC ID&&
&&& unsigned int&&&&&&&&&&& rx_&&&&& //发送 DMA SOC ID
& 5.1& .startup&&& = cq8401_serial8250_startup& //UART的OPEN操作
static int cq8401_serial8250_startup(struct uart_port *port)
&&& ...................
&&& if(up-&port.type==PORT_16550A){
&&&&&&& printk(&***************init cq8401& dma**************\n&);
&&&&&&& char *
&&&&&&& init_waitqueue_head(&up-&tx_wait_queue);
&&&&&&& serial_outp(up, UART_LCR, serial_in(up,UART_LCR) | UART_LCR_WLEN8); //8位数据位
&&&&&&& udelay (10);
&&&&&&& serial_outp(up,UART_FCR,0x0f);& //reset tx, disable uart
&&&&&&& udelay (10);
&&&&&&& printk(&irq=%d\n&,up-&port.irq);
&&&&&&& /*
&&&&&&&& * register error handle interrupt
&&&&&&&& */
&&&&&&& if(up-&port.line)
&&&&&&&&&&&& serial=&serial1&;
&&&&&&& else
&&&&&&&&&&&& serial=&serial0&;
&&& &&& //注册 UART 错误处理中断
&&&&&&& r = request_irq(up-&port.irq,serialirqxx,SA_INTERRUPT, serial, up);
&&&&&&& printk(&r=%d&,r);
&&&&&&&&&&&&& if (r&0)
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&& serial_outp(up,UART_IER,0x14); //禁止接收、发送 中断,允许超时、错误处理中断
&&&&&&& udelay (10);
&&&&&&& if( serial_init_dma(up,up-&port.line) & 1){&&& //serial_init_dma(),申请发送,接收DMA通道和 BUF
&&&&&&&&&&& return -EBUSY;
&& ......................
serial_init_dma(),申请发送,接收DMA通道和 BUF 如下:
up:对应串口结构体
line:串口号
static int serial_init_dma(struct uart_8250_port *up,unsigned int line)
&&& ......................
&&&&&&& /*
&&&&&&&& * request transmit dma channel and dma buffer for the corresponding& uart
&&&&&&&& */
&&&&&&& if(line){
&&&&&&&&&&& uart=&uart1_tx&;
&&&&&&& else
&&&&&&&&&&& uart=&uart0_tx&;
&&&&&&& //片上SOC设备 DMA通道申请,&&
&&& & up-&tx_channel:SOC 设备ID;
&&& & uart:申请的DMA设备名;
&&& & uart0_tx_dma_intr:DMA中断处理函数;&
&&& & SA_INTERRUPT:the irq handler flags
&&& & up:the irq handler device id for shared irq
&&&&&&& r=clx_request_dma(up-&tx_channel, uart , uart0_tx_dma_intr,
&&&&&&&&&&&&&&&&&&&&&&&&&& SA_INTERRUPT, up);
&&&&&&& if (r &0){
&&&&&&&&&&&&&&& printk(&Unable to APPLY UART%d TX DMA channel\n&,line);
&&&&&&&&&&&&&&& return -EBUSY;
&&&&&&&&&&&&&&&& }
&&&&&&& up-&tx_dma_channel=r;
&&&&&&& up-&tx_done&&&&&&& = 1;
&&&&&&& up-&tx_count&&&&&& = 0;
&&&&&&& /*
&&&&&&&& *request receive dma channel and dma buffer for the corresponding& uart
&&&&&&&& */
&&&&&&& if(line)
&&&&&&&&&&& uart=&uart1_rx&;
&&&&&&& else
&&&&&&&&&&& uart=&uart0_rx&;
&&& //接收DMA通道申请&&&&
&&&&&&& r = clx_request_dma(up-&rx_channel, uart , uart0_rx_dma_intr,
&&&&&&&&&&&&&&&&&& SA_INTERRUPT, up);
&&&&&&& if (r &0){
&&&&&&&&&&&&&&& printk(&Unable to APPLY UART%d RX DMA channel\n&,line);
&&&&&&&&&&&&&&& return -EBUSY;
&&&&&&&&&&&&&&& }
&&&&&&& up-&rx_dma_channel=r;
&&&&&&& up-&rx_dma_buf.head = 0;
&&&&&&& up-&rx_dma_buf.tail = 0;
&&&&&&//接收DMA& BUF 申请,这里需要说明一下,在DMA操作中,是操作的总线地址,而且为了解决 DMA 和 cache的一致性问题;
所以最好用 dma_alloc_coherent()函数
&&& void *dma_alloc_coherent(struct device *dev, size_t size,
&&& dma_addr_t * dma_handle, gfp_t gfp)
&&& dev:设备
&&&&&&& size:申请的大小
&&&&&&& dma_handle:申请后返回的总线地址,这里即是&up-&rx_dma_addr
&&&&&&& gfp:申请内存的标识(GFP_KERNEL,GFP_DMA)
&&& 该函数返回供驱动操作的虚拟地址,这里即是up-&rx_dma_buf.buf
当然也可以采用其它方式申请DMA BUF,如:
&&&& kmalloc(),get_free_pages();申请虚拟地址;
&&& 用virt_to_bus();将上面申请到的虚拟地址转化成总线地址。但是强调一点,如果用这种方式,在DMA操作开始之后,如果CPU在还没有到达
&&& DMA COUNTER值时就取数据的话,必须在每次CPU取数据之前做dma_cache_wback_inv()操作,来避免 cache中有 DMA BUF 的数据备份,&
&&& 也就是CPU每次取数据必须从 DMA BUF中取。&
&&&&&&& up-&rx_dma_buf.buf=(unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &up-&rx_dma_addr, GFP_DMA);;
&&&&&&& up-&rx_dma_addr -=CLXSOC_PCI_CORE_START;&& //这里
&&&&&&& /*
&&&&&&&& * enable receive dma channel
&&&&&&&& */
&&&&&&& spin_unlock(&up-&port.lock);
&&& //设置 DMA 相关寄存器;enable_dma(rx),enable 接收DMA通道,准备接收数据,分析如下
&&&&&&& uart_start_dma(up-&rx_dma_channel,up-&rx_dma_addr,DMA_RX_XCOUNT,DMA_MODE_READ);
&&&&&&& serial_outp(up,UART_FCR,0x19);
&&&&&&& udelay (10);
&&&&&&& printk(&enable rx dma\n&);
&&&&&&& return 1;
设置 DMA 相关寄存器, enable发送,接收DMA通道函数分析
chan:通道号
phyaddr:DMA BUF的总线地址
count:DMA COUNTER寄存器的值
mode:根据读,写的不同,来设置DMA DCCR寄存器的相关值,比如说:SAI,DAI,DRTR等值
static void uart_start_dma(int chan, unsigned long phyaddr,unsigned int count, int mode)
&&& if (count == 0) {
&&&&&&& count++;
&&&&&&& printk(KERN_DEBUG &%s: CLXSOC DMA controller can't set dma count zero!\n&,
&&&&&&&&&&&&&&& __FUNCTION__);
&&& flags = claim_dma_lock();
&&& disable_dma(chan);&&&&& //先disable_dma
&&& clear_dma_ff(chan);&&& &&&&
&&& set_dma_addr(chan, phyaddr);&&//设置DMA 源地址,目的地址;phyaddr:源地址,目的地址前面&clx_request_dma,中已经给结构体赋值,这里只需要赋值即可&
&&& set_dma_count(chan, count);& //DCCR,即DMA COUNTER值的设置
&&& set_dma_mode(chan, mode);&&& //根据DMA 接收,发送的不同来设置DCCR.SAI,DCCR.DAI位
&&& enable_dma(chan); //enable DMA
&&& release_dma_lock(flags);
&& 根据各个芯片的DMA控制器的不同,详细的可参考DATASHEET。
&5.2 .start_tx&& = cq8401_serial8250_start_tx& //UART 发送操作
发送操作流程如下:
&&& 用户在APP层,主动发起一次数据到外部设备的传输,先open UART,然后 write data 到这是write操作最终会调用到驱动的
start_tx操作,在UART-DMA驱动中,将用户保存在circ_buf中的数据 通过 DMA到 UART 发送寄存器中(FIFO 模式下);完成一次DMA操作,
这是DMA 产生 DMA中断,通知CPU数据已发送出去,CPU根据circ_buf的情况做相应的处理
static void cq8401_serial8250_start_tx(struct uart_port *port)
&&& struct uart_8250_port *up = (struct uart_8250_port *)
&&& serial_dma_tx_chars(up);
static void serial_dma_tx_chars(struct uart_8250_port *uart)
&&&&&&& struct circ_buf *xmit = &uart-&-&
&&&&&&& int flags = 0;
&&&&&&& if (!uart-&tx_done)
&&&&&&&&&&&&&&
&&&&&&& uart-&tx_done = 0;
#ifdef CONFIG_SERIAL_CQ8401_CTSRTS
&&&&&&& check_modem_status(uart); // 如果串口有RTS,CTS流控线的话,应在最开始做流控的相应操作,这里我们不做具体介绍
&&&& if (uart-&port.x_char) {
&&&&&&&&&&&&&&& serial_outp(uart, UART_TX, uart-&port.x_char);
&&&&&&&&&&&&&&& uart-&port.icount.tx++;
&&&&&&&&&&&&&&& uart-&port.x_char = 0;
&&&&&&&&&&&&&&& uart-&tx_done = 1;
&&&&&&&&&&&&&&&
&&& if (uart_circ_empty(xmit) || uart_tx_stopped(&uart-&port)) {
&&&&&&&&&&&&&&& cq8401_serial8250_stop_tx(&uart-&port);
&&&&&&&&&&&&&&& uart-&tx_done = 1;
&&&&&&&&&&&&&&&
&&& spin_lock_irqsave(&uart-&port.lock, flags);
&&& uart-&tx_count = CIRC_CNT(xmit-&head, xmit-&tail, UART_XMIT_SIZE);& //circ_buf中现在有多少数据就发送多少数据,好像circ_buf只有1K把,这个不是很清楚
&&& if (uart-&tx_count & (UART_XMIT_SIZE - xmit-&tail))
&&&&&&&&&&&&&&& uart-&tx_count = UART_XMIT_SIZE - xmit-&&
&&& serial_outp(uart,UART_FCR,0x19);& //enable dma and uart
&&& udelay (10);
&&&&& // 清空 DCache 数据缓存的数据
&&& dma_cache_wback_inv((unsigned int)(xmit-&buf+xmit-&tail),uart-&tx_count);
&&&&& //发送DMA寄存器设置并 enable_dma(tx)
&&& uart_start_dma(uart-&tx_dma_channel,virt_to_phys(xmit-&buf+xmit-&tail), uart-&tx_count,DMA_MODE_WRITE);
&&&& interruptible_sleep_on(&uart-&tx_wait_queue);//发送队列睡眠
&&&&&&&& spin_unlock_irqrestore(&uart-&port.lock, flags);
完成一次DMA操作后产生,会产生发送DMA中断,如下:
static irqreturn_t uart0_tx_dma_intr(int irq, void *dev_id)&
&&& struct uart_8250_port *uart = dev_
&&& struct circ_buf *xmit = &uart-&-&
&&& spin_lock(&uart-&port.lock);
&&& disable_dma(uart-&tx_dma_channel);& //disbale_dma(tx)
&&& if(__dmac_channel_transmit_end_detected(uart-&tx_dma_channel)){& //判断DCCR.CT是否等于1,1:DMA 操作结束,0:DMA 操作没结束
&&&&&&&&&&&&&&& xmit-&tail = (xmit-&tail+uart-&tx_count) &(UART_XMIT_SIZE -1);
&&&&&&&&&&&&&&& uart-&port.icount.tx+=uart-&tx_
&&&&&&&&&&&&&&& if (uart_circ_chars_pending(xmit) & WAKEUP_CHARS)& //如果circ_buf数据小于WAKEUP_CHARS,唤醒上层向circ_buf写数据
&&&&&&&&&&&&&&&&&&&&&&& uart_write_wakeup(&uart-&port);
&&&&&&&&&&&&&&& if (uart_circ_empty(xmit))&&&&&&&&&&&&&&&&&&&&&&&& //circ_buf为空,停止发送
&&&&&&&&&&&&&&&&&&&&&&& cq8401_serial8250_stop_tx(&uart-&port);
&&&&&&& uart-&tx_done = 1;
&&&&&&& __dmac_channel_clear_transmit_end(uart-&tx_dma_channel);& //dccr.ct=0,以便下此DMA操作,具体看DATASHEET
&&&&&&& wake_up_interruptible(&uart-&tx_wait_queue); //唤醒发送队列
&&&&&&& spin_unlock(&uart-&port.lock);
&&&&&&& return IRQ_HANDLED;
&&& 自此发送过程以分析完成。
& 5.3 接收流程
&&&&由于在UART的操作中,接收都是被动的,所以在UART OPEN的时候,就 enable_dma(rx),只要UART 接收寄存器(FIFO 模式下)
中有数据就会 通过 DMA 到 RX DMA BUF中,为了CPU能及时取走数据,才用定时器操作,每50ms 定时器去读取 DMA COUNTER寄存器,
看是否有数据接收到,如果有则 CPU 取走数据;当传输的数据到达 DAM COUNTER值时,DMA中断产生,CPU 接管工作。&
定时器函数如下:
void serial_rx_dma_timeout(struct uart_8250_port *uart)
&&& unsigned& int x_pos,pos=0;
&&& int flags=0;
&&& spin_lock_irqsave(&uart-&port.lock, flags);
//读取 DMA COUNTER寄存器看时候有数据过来
&&& x_pos =DMA_RX_XCOUNT - get_dma_residue(uart-&rx_dma_channel);
&&&&&&& if (x_pos == DMA_RX_XCOUNT)
&&&&&&&&&&&&&&& x_pos = 0;
&&& pos = x_
&&& if ( pos & uart-&rx_dma_buf.tail) {& //如果有数据过来,CPU 取走数据
&&&&&&&&&&& uart-&rx_dma_buf.head =
&&&&&&&&&&& serial_dma_rx_chars(uart);& //CPU 取走数据
&&&&&&&&&&& uart-&rx_dma_buf.tail = uart-&rx_dma_buf.
&&& spin_unlock_irqrestore(&uart-&port.lock, flags);
&&& mod_timer(&(uart-&rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
CPU 取走数据
static void serial_dma_rx_chars(struct uart_8250_port *uart)
............
&&&&&&&&//dma_cache_wback_inv((unsigned int)uart-&rx_dma_buf.buf, DMA_RX_XCOUNT);& //这就是前面强调的 用kmalloc(),get_free_pages()申请的DMA BUF在每次 CPU 取数据的时候要清空 Dcache,使cpu从RAM中取数据而不是从cache 中&
&&&&&&& uart-&port.icount.rx +=CIRC_CNT(uart-&rx_dma_buf.head, uart-&rx_dma_buf.tail, UART_XMIT_SIZE);
&&&&&&& if (status & UART_LSR_BI) {
&&&&&&&&&&&&&&& uart-&port.icount.brk++;
&&&&&&&&&&&&&&& if (uart_handle_break(&uart-&port))
&&&&&&&&&&&&&&&&&&&&&&& goto dma_ignore_
&&&&&&&&&&&&&&& status &= ~(UART_LSR_PE | UART_LSR_FE);
&&&&&&& if (status & UART_LSR_PE)
&&&&&&&&&&&&&&& uart-&port.icount.parity++;
&&&&&&& if (status & UART_LSR_OE)
&&&&&&&&&&&&&&& uart-&port.icount.overrun++;
&&&&&&& if (status & UART_LSR_FE)
&&&&&&&&&&&&&&& uart-&port.icount.frame++;
&&&&&&& status &= uart-&port.read_status_
&&&&&&& if (status & UART_LSR_BI)
&&&&&&&&&&&&&&& flg = TTY_BREAK;
&&&&&& else if (status & UART_LSR_PE)
&&&&&&&&&&&&&&& flg = TTY_PARITY;
&&&&&&& else if (status & UART_LSR_FE)
&&&&&&&&&&&&&&& flg = TTY_FRAME;
&&&&&&& else
&&&&&&&&&&&&&&& flg = TTY_NORMAL;
&&& //从DMA BUF中取走数据,insert到flip_buf中
&&&&&&& for (i = uart-&rx_dma_buf. i&uart-&rx_dma_buf.i++) {
&&&&&&&&&&&&&&& if (uart_handle_sysrq_char(&uart-&port, uart-&rx_dma_buf.buf[i]))
&&&&&&&&&&&&&&&&&&& goto dma_ignore_
&&&&&&&&&&&&&&& uart_insert_char(&uart-&port, status, UART_LSR_OE, uart-&rx_dma_buf.buf[i], flg);
&dma_ignore_char:
&&&&&&& tty_flip_buffer_push(tty);& //将flip_buf中的数据PUSH 到TTY 线路规程
接收DMA中断
static irqreturn_t uart0_rx_dma_intr(int irq, void *dev_id)
&&& struct uart_8250_port *uart = dev_
&&& disable_dma(uart-&rx_dma_channel);& //disable_dma(rx)
&&& spin_lock(&uart-&port.lock);
if(__dmac_channel_transmit_end_detected(uart-&rx_dma_channel)){
&&& uart-&rx_dma_buf.head = DMA_RX_XCOUNT;
&&& serial_dma_rx_chars(uart);&& //CPU将上次tail到 DMA COUNTER值之间的数据取走
&&& uart-&rx_dma_buf.tail = uart-&rx_dma_buf.head=0;
&&& memset(uart-&rx_dma_buf.buf, 0x00, DMA_RX_XCOUNT); //clear DMA BUF&&
&&& dma_cache_wback_inv((unsigned int)uart-&rx_dma_buf.buf, DMA_RX_XCOUNT);
&&& __dmac_channel_clear_transmit_end(uart-&rx_dma_channel);
&&& //enable_dma(rx),以便下次数据的接收
&&& uart_start_dma(uart-&rx_dma_channel,uart-&rx_dma_addr,DMA_RX_XCOUNT,DMA_MODE_READ);
&&& spin_unlock(&uart-&port.lock);
&&& mod_timer(&(uart-&rx_dma_timer), jiffies);
&&& return IRQ_HANDLED;
&&& 自此接收DMA过程分析完毕
&5.4& .shutdown&& = cq8401_serial8250_shutdown& //串口的关闭
&&& 主要做的就是释放DMA通道,DMA BUF,中断;删除定时器,
static void cq8401_serial8250_shutdown(struct uart_port *port)
&&& struct uart_8250_port *up = (struct uart_8250_port *)
&&& disable_dma(up-&tx_dma_channel);
&&& clx_free_dma(up-&tx_dma_channel);
&&& disable_dma(up-&rx_dma_channel);
&&& clx_free_dma(up-&rx_dma_channel);
&&& dma_free_coherent(NULL,DMA_RX_XCOUNT, up-&rx_dma_buf.buf,up-&rx_dma_addr);
&&& del_timer(&(up-&rx_dma_timer));
&&& free_irq(up-&port.irq, up);
& 5.5 UART错误中断处理,暂省略
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:107483次
积分:1955
积分:1955
排名:第17919名
原创:76篇
转载:62篇
评论:41条
(2)(1)(1)(3)(1)(5)(4)(2)(6)(6)(2)(3)(13)(23)(10)(12)(14)(32)

我要回帖

更多关于 sdhc卡和sd卡的区别 的文章

 

随机推荐