From db58e3350bf17f0b6eab5e2518d6595d7eff59fb Mon Sep 17 00:00:00 2001 From: "dzzxzz@gmail.com" Date: Thu, 1 Nov 2012 07:17:54 +0000 Subject: [PATCH] add serial DMA transmit function(beta) git-svn-id: https://rt-thread.googlecode.com/svn/trunk@2382 bbd45198-f89e-11dd-88c7-29a3b14d5316 --- components/drivers/include/drivers/serial.h | 22 +- components/drivers/include/rtdevice.h | 6 +- components/drivers/serial/serial.c | 65 ++- components/drivers/src/dataqueue.c | 438 ++++++++++---------- 4 files changed, 308 insertions(+), 223 deletions(-) diff --git a/components/drivers/include/drivers/serial.h b/components/drivers/include/drivers/serial.h index 0084543a7..b84717ecc 100644 --- a/components/drivers/include/drivers/serial.h +++ b/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, diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index 09e969f1a..77e43f495 100644 --- a/components/drivers/include/rtdevice.h +++ b/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); diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c index 914054eb3..fae43dcc1 100644 --- a/components/drivers/serial/serial.c +++ b/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; + } +} diff --git a/components/drivers/src/dataqueue.c b/components/drivers/src/dataqueue.c index 353934e26..d96addeaf 100644 --- a/components/drivers/src/dataqueue.c +++ b/components/drivers/src/dataqueue.c @@ -11,18 +11,22 @@ * Date Author Notes * 2012-09-30 Bernard first version. */ + #include #include #include 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); - -- GitLab