10 串口三种发送方式下485收发引脚控制,轮询发送,中断发送,DMA发送

发布于 2020-11-06 14:34:24

基于STM32F429硬件的使用情况,

串口三种发送方式下485收发引脚控制
轮询发送,我可以直接在发送前发送后设置收发引脚,但是这样子浪费CPU时间

int uart_write(uint8_t *data,uint16_t length)
{
    if(config->rts_set)config->rts_set(1);
    rt_device_write(config->dev, 0, data, length);
    if(config->rts_set)config->rts_set(0);
    return 0;
}

中断发送,代码和流程跟轮询发送一样的,

int uart_write(uint8_t *data,uint16_t length)
{
    if(config->rts_set)config->rts_set(1);
    rt_device_write(config->dev, 0, data, length);
    if(config->rts_set)config->rts_set(0);
    return 0;
}

通过写数据触发中断,然后等待rt_completion_wait,这时候写线程被挂起
硬件发送完一个字节后中断触发rt_completion_done释放了写线程,接着发下一个字节,如此反复
但是这里我有一个问题,我并没有看到stm32驱动库中哪个函数去调用
rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE);中de rt_completion_done(&(tx_fifo->completion));去释放写线程。
检查了void uart_isr(struct rt_serial_device *serial)并没有找到,只有有关DMA的接收完成函数

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    struct stm32_uart *uart;
    RT_ASSERT(huart != NULL);
    uart = (struct stm32_uart *)huart;
    _dma_tx_complete(&uart->serial);
}

后面调用了
rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DMADONE);
这个是中断发送的问题,我现在暂时不敢确定这个到底会不会被调用释放

DMA发送
然后是DMA的发送方法比较复杂,我只能开始前切换到发送模式,通过发送完成回调去关闭发送引脚

int uart_write(uint8_t *data,uint16_t length)
{
    if(config->rts_set)config->rts_set(1);
    rt_device_write(config->dev, 0, data, length);
    return 0;
}


//设置发送回调函数
static rt_err_t uart_tx_complete(rt_device_t dev, void *buffer)
{
    if(config->rts_set)config->rts_set(0);
}

rt_device_set_tx_complete(uart_config->dev, uart_tx_complete);

但是这又一个新的问题,如果rt_data_queue_pop中还有数据,在DMA发送上一帧数据完后执行到rt_hw_serial_isr函数中下面这段时,从rt_data_queue_pop中拿出数据通过serial->ops->dma_transmit发送出去,在下面接着运行了tx_complete函数,我的RS485收发引脚已经被关了,处于接收状态,这个时候DMA中发的数据肯定发送不出去

        case RT_SERIAL_EVENT_TX_DMADONE:
        {
            const void *data_ptr;
            rt_size_t data_size;
            const void *last_data_ptr;
            struct rt_serial_tx_dma *tx_dma;

            tx_dma = (struct rt_serial_tx_dma*) serial->serial_tx;

            rt_data_queue_pop(&(tx_dma->data_queue), &last_data_ptr, &data_size, 0);
            if (rt_data_queue_peak(&(tx_dma->data_queue), &data_ptr, &data_size) == RT_EOK)
            {
                /* transmit next data node */
                tx_dma->activated = RT_TRUE;
                serial->ops->dma_transmit(serial, (rt_uint8_t *)data_ptr, data_size, RT_SERIAL_DMA_TX);
            }
            else
            {
                tx_dma->activated = RT_FALSE;
            }

            /* invoke callback */
            if (serial->parent.tx_complete != RT_NULL)
            {
                serial->parent.tx_complete(&serial->parent, (void*)last_data_ptr);
            }
            break;
        }

请问大家有什么办法不?

查看更多

关注者
2
被浏览
505
3 个回答
Acuity
Acuity 2020-11-08

1、中断发送不是这样使用的。中断发送是,线程将待发送数据塞入一个ringbuf里面,使能发送中断,然线程不用管,也不会阻塞;cpu会通过中断从ringbuf取数据,一字节一字节往外发数据。不高于115200波特率,建议用中断发送。
2、DMA发送,与中断发送类似,线程将发送数据塞入ringbuf,另一线程(或者中断)从ringbuf取出数据放入DMA发送buf,开启dma发送。高于115200波特率建议用DMA发送。
3、中断发送和DMA发送都可以通过判断ringbuf是否有数据来判断是否发送完成;中断发送,还可以开启“发送完成”中断,在发送完成中断里设置485方向引脚。
参考:串口DMA收&发

2pw
2pw 2020-11-10

使用DMA发送数据到485的正确姿势:

  1. 新建一个线程专门管理数据发送
  2. 使用发送双缓冲,当然不是必须的
  3. 做一个发送数据接口,需要发数据就push到发送线程
  4. 发送线程每次有数据前设置OE,串口完成中断自动拉回OE
  5. 发送线程在oe被拉回前不能再次发送,因此你需要缓存数据。或者使用上面的双缓冲可以是性能最大化

不管你什么协议都可以这样用。modbus极速收发,一晚上都不会错误一包

陈斌
陈斌 2020-11-12

中断发送的问题我修改了代码,实现了中断发送,效果并不理想,发送的时候有时候被高优先级的线程抢占了cpu,下一个字节没有写到寄存器去,发生断帧现象

https://club.rt-thread.org/ask/question/427309.html

所以并不是很看好中断发送,还是用轮询发送或者DMA好一些
而且由于中断优先级和线程优先级的问题,轮询发送时最好关闭调度器

rt_enter_critical();
if(config->rts_set)config->rts_set(1);
rt_device_write(config->dev, 0, data, length);
if(config->rts_set)config->rts_set(0);
rt_exit_critical();

DMA发送还没有测试,因为不好处理收发引脚的问题,

但是接收是用DMA接收并且屏蔽了DMNA半满和全满中断只用IDLE空闲中断

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览