Browse Source

add serial DMA transmit function(beta)

git-svn-id: https://rt-thread.googlecode.com/svn/trunk@2382 bbd45198-f89e-11dd-88c7-29a3b14d5316
dzzxzz@gmail.com 12 years ago
parent
commit
db58e3350b

+ 16 - 6
components/drivers/include/drivers/serial.h

@@ -57,18 +57,21 @@
 #define RT_SERIAL_ERR_OVERRUN           0x01
 #define RT_SERIAL_ERR_FRAMING           0x02
 #define RT_SERIAL_ERR_PARITY            0x03
+
+#define RT_SERIAL_TX_DATAQUEUE_SIZE     40
+#define RT_SERIAL_TX_DATAQUEUE_LWM      30
 
 /* Default config for serial_configure structure */
 #define RT_SERIAL_CONFIG_DEFAULT           \
 {                                          \
     BAUD_RATE_115200, /* 115200 bits/s */  \
     DATA_BITS_8,      /* 8 databits */     \
-	STOP_BITS_1,      /* 1 stopbit */      \
-	PARITY_NONE,      /* No parity  */     \
-	BIT_ORDER_LSB,    /* LSB first sent */ \
-	NRZ_NORMAL,       /* Normal mode */    \
-	0                                      \
-}                                                                                 
+    STOP_BITS_1,      /* 1 stopbit */      \
+    PARITY_NONE,      /* No parity  */     \
+    BIT_ORDER_LSB,    /* LSB first sent */ \
+    NRZ_NORMAL,       /* Normal mode */    \
+    0                                      \
+}
 
 struct serial_ringbuffer
 {
@@ -98,6 +101,10 @@ struct rt_serial_device
     struct serial_ringbuffer *int_rx;
     /* tx structure */
     struct serial_ringbuffer *int_tx;
+
+    struct rt_data_queue      tx_dq;              /* tx dataqueue */
+    
+    volatile rt_bool_t        dma_flag;           /* dma transfer flag */
 };
 typedef struct rt_serial_device rt_serial_t;
 
@@ -111,9 +118,12 @@ struct rt_uart_ops
 
     int (*putc)(struct rt_serial_device *serial, char c);
     int (*getc)(struct rt_serial_device *serial);
+
+    rt_size_t (*dma_transmit)(struct rt_serial_device *serial, const char *buf, rt_size_t size);
 };
 
 void rt_hw_serial_isr(struct rt_serial_device *serial);
+void rt_hw_serial_dma_tx_isr(struct rt_serial_device *serial);
 rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
                                const char              *name,
                                rt_uint32_t              flag,

+ 3 - 3
components/drivers/include/rtdevice.h

@@ -125,15 +125,15 @@ rt_err_t rt_data_queue_init(struct rt_data_queue *queue,
                             rt_uint16_t           lwm,
                             void (*evt_notify)(struct rt_data_queue *queue, rt_uint32_t event));
 rt_err_t rt_data_queue_push(struct rt_data_queue *queue,
-                            void                 *data_ptr,
+                            const void           *data_ptr,
                             rt_size_t             data_size,
                             rt_int32_t            timeout);
 rt_err_t rt_data_queue_pop(struct rt_data_queue *queue,
-                           void                **data_ptr,
+                           const void          **data_ptr,
                            rt_size_t            *size,
                            rt_int32_t            timeout);
 rt_err_t rt_data_queue_peak(struct rt_data_queue *queue,
-                            void                **data_ptr,
+                            const void          **data_ptr,
                             rt_size_t            *size);
 void rt_data_queue_reset(struct rt_data_queue *queue);
 

+ 64 - 1
components/drivers/serial/serial.c

@@ -132,7 +132,16 @@ static rt_err_t rt_serial_init(struct rt_device *dev)
             serial_ringbuffer_init(serial->int_rx);
 
         if (dev->flag & RT_DEVICE_FLAG_INT_TX)
-            serial_ringbuffer_init(serial->int_tx);      
+            serial_ringbuffer_init(serial->int_tx);
+
+        if (dev->flag & RT_DEVICE_FLAG_DMA_TX)
+        {
+            serial->dma_flag = RT_FALSE;
+            
+            /* init data queue */
+            rt_data_queue_init(&(serial->tx_dq), RT_SERIAL_TX_DATAQUEUE_SIZE,
+                               RT_SERIAL_TX_DATAQUEUE_LWM, RT_NULL);
+        }
 
         /* set activated */
         dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
@@ -261,6 +270,41 @@ static rt_size_t rt_serial_write(struct rt_device *dev,
                 break;
         }
     }
+    else if (dev->flag & RT_DEVICE_FLAG_DMA_TX)
+    {
+        const void *data_ptr = RT_NULL;
+        rt_size_t data_size = 0;
+        rt_base_t level;
+        rt_err_t result;
+        
+        RT_ASSERT(0 == (dev->flag & RT_DEVICE_FLAG_STREAM));
+
+        result = rt_data_queue_push(&(serial->tx_dq), buffer, size, 20); 
+        if (result == RT_EOK)
+        {
+            level = rt_hw_interrupt_disable();
+            if (serial->dma_flag == RT_FALSE)
+            {
+                serial->dma_flag = RT_TRUE;
+                rt_hw_interrupt_enable(level);
+            
+                if (RT_EOK == rt_data_queue_pop(&(serial->tx_dq), &data_ptr, &data_size, 0))
+                {
+                    serial->ops->dma_transmit(serial, data_ptr, data_size);
+                }
+            }
+            else
+                rt_hw_interrupt_enable(level);
+
+            return size;
+        }
+        else
+        {
+            rt_set_errno(result);
+
+            return 0;
+        }
+    }
     else
     {
         /* polling mode */
@@ -377,3 +421,22 @@ void rt_hw_serial_isr(struct rt_serial_device *serial)
         serial->parent.rx_indicate(&serial->parent, rx_length);
     }
 }
+
+/*
+ * ISR for DMA mode Tx
+ */
+void rt_hw_serial_dma_tx_isr(struct rt_serial_device *serial)
+{
+    void *data_ptr;
+    rt_size_t data_size;
+
+    if (RT_EOK == rt_data_queue_pop(&(serial->tx_dq), &data_ptr, &data_size, 0))
+    {
+        /* transmit next data node */
+        serial->ops->dma_transmit(serial, data_ptr, data_size);
+    }
+    else
+    {
+        serial->dma_flag = RT_FALSE;
+    }
+}

+ 225 - 213
components/drivers/src/dataqueue.c

@@ -11,18 +11,22 @@
  * Date           Author       Notes
  * 2012-09-30     Bernard      first version.
  */
+
 #include <rtthread.h>
 #include <rtdevice.h>
 #include <rthw.h>
 
 struct rt_data_item
 {
-	void* data_ptr;
-	rt_size_t data_size;
+    const void *data_ptr;
+    rt_size_t data_size;
 };
 
-rt_err_t rt_data_queue_init(struct rt_data_queue* queue, rt_uint16_t size, rt_uint16_t lwm,
-	void (*evt_notify)(struct rt_data_queue* queue, rt_uint32_t event))
+rt_err_t
+rt_data_queue_init(struct rt_data_queue *queue,
+                   rt_uint16_t size,
+                   rt_uint16_t lwm,
+                   void (*evt_notify)(struct rt_data_queue *queue, rt_uint32_t event))
 {
     RT_ASSERT(queue != RT_NULL);
 
@@ -30,7 +34,7 @@ rt_err_t rt_data_queue_init(struct rt_data_queue* queue, rt_uint16_t size, rt_ui
 
     queue->size = size;
     queue->lwm = lwm;
-	queue->waiting_lwm = RT_FALSE;
+    queue->waiting_lwm = RT_FALSE;
 
     queue->get_index = 0;
     queue->put_index = 0;
@@ -38,7 +42,7 @@ rt_err_t rt_data_queue_init(struct rt_data_queue* queue, rt_uint16_t size, rt_ui
     rt_list_init(&(queue->suspended_push_list));
     rt_list_init(&(queue->suspended_pop_list));
 
-    queue->queue = (struct rt_data_item*) rt_malloc(sizeof(struct rt_data_item) * size);
+    queue->queue = (struct rt_data_item *)rt_malloc(sizeof(struct rt_data_item) * size);
     if (queue->queue == RT_NULL)
     {
         return -RT_ENOMEM;
@@ -48,268 +52,276 @@ rt_err_t rt_data_queue_init(struct rt_data_queue* queue, rt_uint16_t size, rt_ui
 }
 RTM_EXPORT(rt_data_queue_init);
 
-rt_err_t rt_data_queue_push(struct rt_data_queue* queue, void* data_ptr, rt_size_t data_size, rt_int32_t timeout)
+rt_err_t rt_data_queue_push(struct rt_data_queue *queue,
+                            const void *data_ptr,
+                            rt_size_t data_size,
+                            rt_int32_t timeout)
 {
     rt_uint16_t mask;
     rt_ubase_t  level;
     rt_thread_t thread;
-	rt_err_t    result;
-	
-	RT_ASSERT(queue != RT_NULL);
+    rt_err_t    result;
+    
+    RT_ASSERT(queue != RT_NULL);
 
-	result = RT_EOK;
+    result = RT_EOK;
     thread = rt_thread_self();
-	mask = queue->size - 1;
+    mask = queue->size - 1;
 
     level = rt_hw_interrupt_disable();
     while (queue->put_index - queue->get_index == queue->size)
     {
-		queue->waiting_lwm = RT_TRUE;
+        queue->waiting_lwm = RT_TRUE;
 
         /* queue is full */
         if (timeout == 0)
         {
-        	result = -RT_ETIMEOUT;
+            result = -RT_ETIMEOUT;
+
+            goto __exit;
+        }
 
-			goto __exit;
+        /* current context checking */
+        RT_DEBUG_NOT_IN_INTERRUPT;
+
+        /* reset thread error number */
+        thread->error = RT_EOK;
+        
+        /* suspend thread on the push list */
+        rt_thread_suspend(thread);
+        rt_list_insert_before(&(queue->suspended_push_list), &(thread->tlist));
+        /* start timer */
+        if (timeout > 0)
+        {
+            /* reset the timeout of thread timer and start it */
+            rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout);
+            rt_timer_start(&(thread->thread_timer));
         }
-		
-		/* current context checking */
-		RT_DEBUG_NOT_IN_INTERRUPT;
-
-		/* reset thread error number */
-		thread->error = RT_EOK;
-		
-		/* suspend thread on the push list */
-		rt_thread_suspend(thread);
-		rt_list_insert_before(&(queue->suspended_push_list), &(thread->tlist));
-		/* start timer */
-		if (timeout > 0)
-		{
-			/* reset the timeout of thread timer and start it */
-			rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout);
-			rt_timer_start(&(thread->thread_timer));
-		}
-
-		/* enable interrupt */
-		rt_hw_interrupt_enable(level);
-
-		/* do schedule */
-		rt_schedule();
-		
-		/* thread is waked up */
-		result = thread->error;
-		level = rt_hw_interrupt_disable();
-		if (result != RT_EOK) goto __exit;
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(level);
+
+        /* do schedule */
+        rt_schedule();
+
+        /* thread is waked up */
+        result = thread->error;
+        level = rt_hw_interrupt_disable();
+        if (result != RT_EOK) goto __exit;
     }
 
-	queue->queue[queue->put_index & mask].data_ptr  = data_ptr;
-	queue->queue[queue->put_index & mask].data_size = data_size;
-	queue->put_index += 1;
+    queue->queue[queue->put_index & mask].data_ptr  = data_ptr;
+    queue->queue[queue->put_index & mask].data_size = data_size;
+    queue->put_index += 1;
 
-	if (!rt_list_isempty(&(queue->suspended_pop_list)))
-	{
-		/* there is at least one thread in suspended list */
+    if (!rt_list_isempty(&(queue->suspended_pop_list)))
+    {
+        /* there is at least one thread in suspended list */
+
+        /* get thread entry */
+        thread = rt_list_entry(queue->suspended_pop_list.next, struct rt_thread, tlist);
 
-		/* get thread entry */
-		thread = rt_list_entry(queue->suspended_pop_list.next, struct rt_thread, tlist);
-		
-		/* resume it */
-		rt_thread_resume(thread);
-		rt_hw_interrupt_enable(level);
+        /* resume it */
+        rt_thread_resume(thread);
+        rt_hw_interrupt_enable(level);
 
-		/* perform a schedule */
-		rt_schedule();
+        /* perform a schedule */
+        rt_schedule();
 
-		return result;
-	}
+        return result;
+    }
 
 __exit:
     rt_hw_interrupt_enable(level);
-	if ((result == RT_EOK) && queue->evt_notify != RT_NULL)
-	{
-		queue->evt_notify(queue, RT_DATAQUEUE_EVENT_PUSH);
-	}
+    if ((result == RT_EOK) && queue->evt_notify != RT_NULL)
+    {
+        queue->evt_notify(queue, RT_DATAQUEUE_EVENT_PUSH);
+    }
 
     return result;
 }
 RTM_EXPORT(rt_data_queue_push);
 
-rt_err_t rt_data_queue_pop(struct rt_data_queue* queue, void** data_ptr, rt_size_t *size, 
-	rt_int32_t timeout)
+rt_err_t rt_data_queue_pop(struct rt_data_queue *queue,
+                           const void** data_ptr,
+                           rt_size_t *size, 
+                           rt_int32_t timeout)
 {
     rt_ubase_t  level;
     rt_thread_t thread;
-	rt_err_t    result;
-	rt_uint16_t mask;
-	
-	RT_ASSERT(queue != RT_NULL);
-	RT_ASSERT(data_ptr != RT_NULL);
-	RT_ASSERT(size != RT_NULL);
-
-	result = RT_EOK;
+    rt_err_t    result;
+    rt_uint16_t mask;
+
+    RT_ASSERT(queue != RT_NULL);
+    RT_ASSERT(data_ptr != RT_NULL);
+    RT_ASSERT(size != RT_NULL);
+
+    result = RT_EOK;
     thread = rt_thread_self();
-	mask = queue->size - 1;
+    mask = queue->size - 1;
 
     level = rt_hw_interrupt_disable();
-	while (queue->get_index == queue->put_index)
-	{
-		/* queue is empty */
-		if (timeout == 0)
-		{
-			result = -RT_ETIMEOUT;
-			goto __exit;
-		}
-
-		/* current context checking */
-		RT_DEBUG_NOT_IN_INTERRUPT;
-
-		/* reset thread error number */
-		thread->error = RT_EOK;
-		
-		/* suspend thread on the pop list */
-		rt_thread_suspend(thread);
-		rt_list_insert_before(&(queue->suspended_pop_list), &(thread->tlist));
-		/* start timer */
-		if (timeout > 0)
-		{
-			/* reset the timeout of thread timer and start it */
-			rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout);
-			rt_timer_start(&(thread->thread_timer));
-		}
-
-		/* enable interrupt */
-		rt_hw_interrupt_enable(level);
-
-		/* do schedule */
-		rt_schedule();
-
-		/* thread is waked up */
-		result = thread->error;
-		level = rt_hw_interrupt_disable();
-		if (result != RT_EOK) goto __exit;
-	}
-
-	*data_ptr = queue->queue[queue->get_index & mask].data_ptr;
-	*size     = queue->queue[queue->get_index & mask].data_size;
-
-	queue->get_index += 1;
-
-	if ((queue->waiting_lwm == RT_TRUE) && 
-		(queue->put_index - queue->get_index) <= queue->lwm)
-	{
-		queue->waiting_lwm = RT_FALSE;
-
-		/* there is at least one thread in suspended list and less than low water mark */
-		if (!rt_list_isempty(&(queue->suspended_push_list)))
-		{
-			/* get thread entry */
-			thread = rt_list_entry(queue->suspended_push_list.next, struct rt_thread, tlist);
-
-			/* resume it */
-			rt_thread_resume(thread);
-			rt_hw_interrupt_enable(level);
-
-			/* perform a schedule */
-			rt_schedule();
-		}
-
-		if (queue->evt_notify != RT_NULL)
-			queue->evt_notify(queue, RT_DATAQUEUE_EVENT_LWM);
-
-		return result;
-	}
+    while (queue->get_index == queue->put_index)
+    {
+        /* queue is empty */
+        if (timeout == 0)
+        {
+            result = -RT_ETIMEOUT;
+            goto __exit;
+        }
+
+        /* current context checking */
+        RT_DEBUG_NOT_IN_INTERRUPT;
+
+        /* reset thread error number */
+        thread->error = RT_EOK;
+        
+        /* suspend thread on the pop list */
+        rt_thread_suspend(thread);
+        rt_list_insert_before(&(queue->suspended_pop_list), &(thread->tlist));
+        /* start timer */
+        if (timeout > 0)
+        {
+            /* reset the timeout of thread timer and start it */
+            rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout);
+            rt_timer_start(&(thread->thread_timer));
+        }
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(level);
+
+        /* do schedule */
+        rt_schedule();
+
+        /* thread is waked up */
+        result = thread->error;
+        level = rt_hw_interrupt_disable();
+        if (result != RT_EOK) goto __exit;
+    }
+
+    *data_ptr = queue->queue[queue->get_index & mask].data_ptr;
+    *size     = queue->queue[queue->get_index & mask].data_size;
+
+    queue->get_index += 1;
+
+    if ((queue->waiting_lwm == RT_TRUE) && 
+        (queue->put_index - queue->get_index) <= queue->lwm)
+    {
+        queue->waiting_lwm = RT_FALSE;
+
+        /* there is at least one thread in suspended list and less than low water mark */
+        if (!rt_list_isempty(&(queue->suspended_push_list)))
+        {
+            /* get thread entry */
+            thread = rt_list_entry(queue->suspended_push_list.next, struct rt_thread, tlist);
+
+            /* resume it */
+            rt_thread_resume(thread);
+            rt_hw_interrupt_enable(level);
+
+            /* perform a schedule */
+            rt_schedule();
+        }
+
+        if (queue->evt_notify != RT_NULL)
+            queue->evt_notify(queue, RT_DATAQUEUE_EVENT_LWM);
+
+        return result;
+    }
 
 __exit:
     rt_hw_interrupt_enable(level);
-	if ((result == RT_EOK) && (queue->evt_notify != RT_NULL))
-	{
-		queue->evt_notify(queue, RT_DATAQUEUE_EVENT_POP);
-	}
+    if ((result == RT_EOK) && (queue->evt_notify != RT_NULL))
+    {
+        queue->evt_notify(queue, RT_DATAQUEUE_EVENT_POP);
+    }
 
-	return result;
+    return result;
 }
 RTM_EXPORT(rt_data_queue_pop);
 
-rt_err_t rt_data_queue_peak(struct rt_data_queue* queue, void** data_ptr, rt_size_t *size)
+rt_err_t rt_data_queue_peak(struct rt_data_queue *queue,
+                            const void** data_ptr,
+                            rt_size_t *size)
 {
     rt_ubase_t  level;
-	rt_uint16_t mask;
+    rt_uint16_t mask;
 
-	RT_ASSERT(queue != RT_NULL);
+    RT_ASSERT(queue != RT_NULL);
 
-	mask = queue->size - 1;
+    mask = queue->size - 1;
 
     level = rt_hw_interrupt_disable();
 
-	if (queue->get_index == queue->put_index) 
-	{
-	    rt_hw_interrupt_enable(level);
-		return -RT_EEMPTY;
-	}
-	
-	*data_ptr = queue->queue[queue->get_index & mask].data_ptr;
-	*size	  = queue->queue[queue->get_index & mask].data_size;
+    if (queue->get_index == queue->put_index) 
+    {
+        rt_hw_interrupt_enable(level);
+        
+        return -RT_EEMPTY;
+    }
+
+    *data_ptr = queue->queue[queue->get_index & mask].data_ptr;
+    *size     = queue->queue[queue->get_index & mask].data_size;
 
     rt_hw_interrupt_enable(level);
-	return RT_EOK;
+
+    return RT_EOK;
 }
 RTM_EXPORT(rt_data_queue_peak);
 
-void rt_data_queue_reset(struct rt_data_queue* queue)
+void rt_data_queue_reset(struct rt_data_queue *queue)
 {
-	struct rt_thread *thread;
-	register rt_ubase_t temp;
-
-	rt_enter_critical();
-	/* wakeup all suspend threads */
-
-	/* resume on pop list */
-	while (!rt_list_isempty(&(queue->suspended_pop_list)))
-	{
-		/* disable interrupt */
-		temp = rt_hw_interrupt_disable();
-
-		/* get next suspend thread */
-		thread = rt_list_entry(queue->suspended_pop_list.next, struct rt_thread, tlist);
-		/* set error code to RT_ERROR */
-		thread->error = -RT_ERROR;
-
-		/*
-		 * resume thread
-		 * In rt_thread_resume function, it will remove current thread from
-		 * suspend list
-		 */
-		rt_thread_resume(thread);
-
-		/* enable interrupt */
-		rt_hw_interrupt_enable(temp);
-	}
-
-	/* resume on push list */
-	while (!rt_list_isempty(&(queue->suspended_push_list)))
-	{
-		/* disable interrupt */
-		temp = rt_hw_interrupt_disable();
-
-		/* get next suspend thread */
-		thread = rt_list_entry(queue->suspended_push_list.next, struct rt_thread, tlist);
-		/* set error code to RT_ERROR */
-		thread->error = -RT_ERROR;
-
-		/*
-		 * resume thread
-		 * In rt_thread_resume function, it will remove current thread from
-		 * suspend list
-		 */
-		rt_thread_resume(thread);
-
-		/* enable interrupt */
-		rt_hw_interrupt_enable(temp);
-	}
-	rt_exit_critical();
-
-	rt_schedule();
+    struct rt_thread *thread;
+    register rt_ubase_t temp;
+
+    rt_enter_critical();
+    /* wakeup all suspend threads */
+
+    /* resume on pop list */
+    while (!rt_list_isempty(&(queue->suspended_pop_list)))
+    {
+        /* disable interrupt */
+        temp = rt_hw_interrupt_disable();
+
+        /* get next suspend thread */
+        thread = rt_list_entry(queue->suspended_pop_list.next, struct rt_thread, tlist);
+        /* set error code to RT_ERROR */
+        thread->error = -RT_ERROR;
+
+        /*
+         * resume thread
+         * In rt_thread_resume function, it will remove current thread from
+         * suspend list
+         */
+        rt_thread_resume(thread);
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(temp);
+    }
+
+    /* resume on push list */
+    while (!rt_list_isempty(&(queue->suspended_push_list)))
+    {
+        /* disable interrupt */
+        temp = rt_hw_interrupt_disable();
+
+        /* get next suspend thread */
+        thread = rt_list_entry(queue->suspended_push_list.next, struct rt_thread, tlist);
+        /* set error code to RT_ERROR */
+        thread->error = -RT_ERROR;
+
+        /*
+         * resume thread
+         * In rt_thread_resume function, it will remove current thread from
+         * suspend list
+         */
+        rt_thread_resume(thread);
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(temp);
+    }
+    rt_exit_critical();
+
+    rt_schedule();
 }
 RTM_EXPORT(rt_data_queue_reset);
-