LPC5500

USB中的SOF(Start Of Frame)包是USB开发中,经常接触也是很简单的一个概念:SOF由USB主机每1ms定时发出(FS),作用很多,相当于是一个时钟节拍基准,如果暂时用不到,就忽略也没有关系。LPC5528的USB模块中,有一个FRAME_INT中断描述如下:

1.png

这个中断 ”感觉好像就是SOF中断”,似乎只是名字换成了FRAME_INT,在手册中的描述也和SOF中断几乎一样,但是手册里就是没有说它就是SOF中断。

经过小编实测和向同事确认,发现这个中断实际上并不完全等同于SOF中断,两者还是有一定区别的。本文就来探讨一下这个问题:

事情是这样的: 小编最近支持一个客户,客户的代码中涉及低功耗按键唤醒,USB Remote wakeup, 也需要用到SOF中断做为定时器来驱动上层app事件。

客户发现:当PC关机的时候,LPC5528被按键唤醒,唤醒后执行Remote Wakeup(实际就是MCU将USB总线设置为K states),因为这时候主机Host已经关机,USB总线上没有任何信号,永远维持的K states上,但是MCU却莫名其妙的进入了FRAME_INT中断,而且还是1ms的周期!

测试使用一个GPIO引脚——GPIO_SOF_EVT,每当进入FRAME_INT中断后,Toggle一次;测试波形如下图:

2.png

这有点奇怪了。。。USB总线上没有任何包发过来,但是还是会进FRAME_INT中断!难道FRAME_INT中断不是SOF中断?

结果确实是这样。。经过和同事确认,FRAME_INT中断确实不是SOF中断,它只是和SOF中断有点像而已。当VBUS没有掉电且MCU执行Remote wakeup(Resume)的时候, FEAME_INT还是会”如期而至”。。他和SOF包没有必然联系。。。 疑惑中

那么如果想用SOF中断咋办呢?有一个简单的办法:USB->INFO寄存器中的前11位为FRAME_NR,它记录了正确解码SOF的帧号,每当收到一个真正的SOF帧,FRAME_NR都会自加,所以在FRAME_INT中断中可以读入FRAME_NR来辅助判断,这次FRAME_INT到底是不是真正的SOF包到来:

3.png


代码如下所示:

4.png



补充:

  • 小编同时也测试了HS-USB的FRAME_INT中断,其结论和FS是一样的,只不过HS的FRAME_INT中断是微帧,125us一次。

  • FRAME_INT中断在SDK中默认是关闭的,需要在INTEN寄存器中打开对应的标志位才可以使用。

到此为止,有必要进一步地思考一下,为什么这个USB的内部模块中,没有专门的SOF中断,却出现了这个与SOF有关,又不来源于SOF的FRAME_INT中断。

尽管没有与芯片设计人员沟通,但可以合理地推论,这个FRAME_INT中断是为SOF相关应用而设计的,之所以没有直接使用SOF作为触发源,是因为在USB主机休眠时,不再有SOF信号,而对于在主机休眠时仍需要周期中断源的USB应用而言,则需要使用其它定时器资源来实现相应功能,这样就会占用其它片上资源,又会增加软件调度的负担。

而在片内USB模块中,设计这样的机制,即可以在没有外部SOF信号时,继续维持周期性的中断,也可以在SOF信号恢复后,保持这个周期中断与SOF同步,即实现了SOF中断的功能,又兼顾了软件的实现与层次划分。

最后,这样的设计并不增加硬件的成本。

来源:恩智浦MCU加油站

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 22

LPC5500的SDK中提供了非常丰富的串口例程(如下图所示):

“LPC5500_SDK例程:串口DMA发送+中断接收"

但是,偏偏没有串口DMA发送+中断接收这种组合。

实话说小编觉得这种组合才是大部分MCU场景中最常用的。尤其是对一些RX数据量不大(比如只是解析一些命令)但是TX数据量大的应用(比如定时,高频发送数据)再合适不过,该架构模型简单,非常容易理解。

本篇文章就手把手教大家分分钟撸一个DMA发送+中断接收的例程:

我们复制一个dma_transfer例程作为模板,重命名为dma_tx_int_rx(名字无所谓,自己起即可)。打开,编译下载运行一下,确保原版demo在自己的板子上可以顺利的运行,咱们把前期工作都铺垫好。

下面我们开始改代码:dma_transfer原版代码是TX和RX都是dma传输,我们只需要将RX改为中断接收数据即可。

首先将USART_TransferCreateHandleDMA函数修改下,将所有RX DMA有关的东西去掉,handler填NULL就可以。这样usart_dma driver就不会处理RX DMA有关的操作了:

USART_TransferCreateHandleDMA(DEMO_USART,  &g_uartDmaHandle, USART_UserCallback, NULL,&g_uartTxDmaHandle, NULL);

2. 开启usart RX中断,没啥可说的,常规操作:

/* Enable RX interrupt. */

USART_EnableInterrupts(DEMO_USART, kUSART_RxLevelInterruptEnable | kUSART_RxErrorInterruptEnable);
EnableIRQ(DEMO_USART_IRQn);

3. 在app层定义(接管)串口硬件中断,并在中断中处理串口RX接收到的数据:

#define DEMO_USART_IRQHandler FLEXCOMM0_IRQHandler
#define DEMO_USART_IRQn     FLEXCOMM0_IRQn



void DEMO_USART_IRQHandler(void)
{
   uint8_t data;
   /* If new data arrived. */
   if ((kUSART_RxFifoNotEmptyFlag | kUSART_RxError | kUSART_RxFifoFullFlag) & USART_GetStatusFlags(DEMO_USART))
   {
       data = USART_ReadByte(DEMO_USART);
       PRINTF("usart rx interrupt:%c\r\n", data);

       if(data == 's')

       {

          /* Send g_tipString out. */

          xfer.data     = g_tipString;

          xfer.dataSize = sizeof(g_tipString) - 1;


          g_uartDmaHandle.txState = kUSART_TxIdle;

          USART_TransferSendDMA(DEMO_USART, &g_uartDmaHandle, &xfer);

      }

   }

   if ((0U != (DEMO_USART->INTENSET &  USART_INTENSET_TXIDLEEN_MASK)) && (0U != (DEMO_USART->INTSTAT & USART_INTSTAT_TXIDLE_MASK)))

   {

      USART_TransferDMAHandleIRQ(DEMO_USART, &g_uartDmaHandle);

   }

}

注意,这里需要在硬件串口中断里判断TXIDLE中断,并调用USART_TransferDMAHandleIRQ。这是仿照fsl_usart_dma.c中的写法(fsl_usart_dma中开启了TXIDLE中断,并使用TXIDLE中断来调用dma handle用户回调函数,现在硬件串口中断已经被app层接管,所以我们同样要实现这部分功能)。

4. 测试:改好代码,下载运行:当串口敲入任意键时候,程序都会回显接收到数据,当按键”s”时,会调用USART_TransferSendDMA函数使用DMA发送一串数据,并进入DMA发送完成回调函数。

“LPC5500_SDK例程:串口DMA发送+中断接收"

代码清单

以下是完整代码清单(可以直接复制到usart_dma_transfer.c里运行):

/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.

* Copyright 2016-2017 NXP

* All rights reserved.

*

* SPDX-License-Identifier:  BSD-3-Clause

*/

#include "pin_mux.h"

#include "board.h"

#include "fsl_usart.h"

#include "fsl_usart_dma.h"

#include "fsl_dma.h"

#include "fsl_debug_console.h"


#include 

#include "fsl_power.h"


#define DEMO_USART                USART0

#define DEMO_USART_CLK_SRC        kCLOCK_Flexcomm0

#define DEMO_USART_CLK_FREQ       CLOCK_GetFlexCommClkFreq(0U)

#define USART_RX_DMA_CHANNEL      4

#define USART_TX_DMA_CHANNEL      5

#define EXAMPLE_UART_DMA_BASEADDR DMA0

#define DEMO_USART_IRQHandler FLEXCOMM0_IRQHandler

#define DEMO_USART_IRQn       FLEXCOMM0_IRQn

#define ECHO_BUFFER_LENGTH 8


usart_transfer_t xfer;

usart_dma_handle_t g_uartDmaHandle;

dma_handle_t g_uartTxDmaHandle;

uint8_t g_tipString[]                  = "This string is send from UART_DMA\r\n";

#define kUSART_TxIdle 0

void USART_UserCallback(USART_Type *base, usart_dma_handle_t *handle, status_t status, void *userData)

{

   userData = userData;

   if (kStatus_USART_TxIdle == status)

   {

       PRINTF("USART_UserCallback, status:0x%X\r\n", status);

   }

}

void DEMO_USART_IRQHandler(void)
{
   uint8_t data;
   /* If new data arrived. */
   if ((kUSART_RxFifoNotEmptyFlag | kUSART_RxError | kUSART_RxFifoFullFlag) & USART_GetStatusFlags(DEMO_USART))
   {
       data = USART_ReadByte(DEMO_USART);
       PRINTF("usart rx interrupt:%c\r\n", data);

       if(data == 's')

       {

          /* Send g_tipString out. */

          xfer.data     = g_tipString;

          xfer.dataSize = sizeof(g_tipString) - 1;


          g_uartDmaHandle.txState = kUSART_TxIdle;

          USART_TransferSendDMA(DEMO_USART, &g_uartDmaHandle, &xfer);

      }

   }

   if ((0U != (DEMO_USART->INTENSET &  USART_INTENSET_TXIDLEEN_MASK)) && (0U != (DEMO_USART->INTSTAT & USART_INTSTAT_TXIDLE_MASK)))

   {

      USART_TransferDMAHandleIRQ(DEMO_USART, &g_uartDmaHandle);

   }

}

int main(void)

{

   usart_config_t    config;

  

   /* set BOD VBAT level to 1.65V */

   POWER_SetBodVbatLevel(kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false);

   /* attach 12 MHz clock to FLEXCOMM0 (debug console) */

   CLOCK_AttachClk(kFRO12M_to_FLEXCOMM0);

  

   BOARD_InitBootPins();

   BOARD_InitBootClocks();

   BOARD_InitDebugConsole();

  

   PRINTF("USART: TX DMA, RX INTERRUPT\r\n");

   PRINTF("press 's' for DMA TX tranmsit\r\n");

  

   USART_GetDefaultConfig(&config);

   config.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE;

   config.enableTx     = true;

   config.enableRx     = true;

  

   USART_Init(DEMO_USART, &config, DEMO_USART_CLK_FREQ);

  

   /* Configure DMA. */

   DMA_Init(EXAMPLE_UART_DMA_BASEADDR);

   DMA_EnableChannel(EXAMPLE_UART_DMA_BASEADDR, USART_TX_DMA_CHANNEL);

   DMA_EnableChannel(EXAMPLE_UART_DMA_BASEADDR, USART_RX_DMA_CHANNEL);

   DMA_CreateHandle(&g_uartTxDmaHandle, EXAMPLE_UART_DMA_BASEADDR, USART_TX_DMA_CHANNEL);

   USART_TransferCreateHandleDMA(DEMO_USART, &g_uartDmaHandle, USART_UserCallback, NULL, &g_uartTxDmaHandle, NULL);

 

   /* Send g_tipString out. */

   xfer.data     = g_tipString;

   xfer.dataSize = sizeof(g_tipString) - 1;

   USART_TransferSendDMA(DEMO_USART, &g_uartDmaHandle, &xfer);

  

   /* Enable RX interrupt. */

   USART_EnableInterrupts(DEMO_USART, kUSART_RxLevelInterruptEnable | kUSART_RxErrorInterruptEnable);

   EnableIRQ(DEMO_USART_IRQn);

来源:恩智浦MCU加油站
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 185
订阅 RSS - LPC5500