Funpack11 LPC55S69 任务三——USB2Dual Uart
Funpack11期活动,基于LPC55S69实现了USB转双串口的程序
标签
嵌入式系统
USB
LPC55S69
funpack11
chsdzczp
更新2021-11-02
1391
  • 前言

这是我第一次参加Funpack的活动,之前各种单片机、DSP、FPGA用的的很多,但是NXP的单片机还没用过,看到这次的开发板是NXP双核M33的单片机,我果断去digi-key买了一块开发板,报名参加了这次活动。

  • LPC55S69介绍

LPC55S6x MCU家族是全球首款基于通用Cortex-M33的微控制器。

该高效率MCU家族采用Armv8-M架构,性能和高级安全功能达到新水平,包括TrustZone-M和协处理器扩展。LPC55S6x家族利用协处理器扩展型号,大幅提高信号处理效率,采用专有DSP加速器,使计算的时钟周期减少了10倍。还可选择使用第二个Cortex-M33内核,支持灵活地平衡高性能与功率效率。

此外,LPC55S6x MCU家族依托基于40nm NVM的处理技术,具备成本效益优势,提供广泛的可扩展封装和存储器选项,并提供强大的支持,包括MCUXpresso软件和工具生态系统及低成本开发板。

LPC55S6x Block Diagram

上图是LPC55S69的 Block Diagram,我们可以看出该芯片内部拥有2个M33的内核,其中一个比较精简,是辅助的CPU。存储方面最大可拥有640KB flash和320 KB RAM。各种外设也很丰富,16Bit ADC 、内置高速USB PHY、8个功能可配置的FlexComm、SDIO、各种定时器,该有的外设一应俱全。

  • LPC55S69-EVK

这次使用的板子是NXP官方的开发板,该电路板包括一个高性能板载调试器、音频子系统和加速度传感器,提供多个选项,可为网络、传感器、显示器和其他接口添加现成的附加板。

img

                                                            板卡技术和功能规格

img

  • 任务

实现USB-UART Hub,自己设计通信方式,实现USB到多路UART的收发。

我这次要做的任务是实现一个USB转双串口的功能。USB转UART在嵌入式开发中是必不可少的工具,我一直使用的FT4232转4路UART的板子,由于经常需要使用不止一个串口,使用起来相比CH340那种单路的方便很多。这次我就使用LPC55S69制作一个USB转双串口的调试工具。

  • 任务实现

NXP官方提供了很多的DEMO,其中有一个DEMO——dev_composite_cdc_vcom_cdc_vcom_bm,

从介绍中我们可以知道这个DEMO实现了USB转双串口的功能,但是并没有引出外部信号。

FjmX5_hq0FyDakFNA8XhI4wAK9ho

下面是这个demo的框图:

Fs3h-TSv3I-uKtVt6OfvUtd1iOsD

在这个demo中,CDC1和CDC2是两个虚拟的串口,但其并没有引出信号,PC经USB将数据发送至CDC后,CDC直接将数据传输回来。

这个demo已经很接近我们的目标了,我们只需要添加两个串口,在USB虚拟的串口收到PC发来的数据后通过串口发送至外界,同时在串口通过中断收到数据后,再将接受到的数据通过USB发送到PC,便实现了USB转串口的功能。下图是本项目的框图:

FvmRbZru0QTg8CfcTWwttf5InMgq

 

/*!
* @brief Application task function.
*
* This function runs the task for application.
*
* @return None.
*/
void USB_DeviceCdcVcomTask(void)
{
   usb_status_t error = kStatus_USB_Error;
   volatile usb_cdc_vcom_struct_t *vcomInstance;
​
   for (uint8_t i = 0; i < USB_DEVICE_CONFIG_CDC_ACM; i++)
  {
       vcomInstance = &g_deviceComposite->cdcVcom[i];
       if ((1 == vcomInstance->attach) && (1 == vcomInstance->startTransactions))
      {
           /* User Code */
           /* endpoint callback length is USB_CANCELLED_TRANSFER_LENGTH (0xFFFFFFFFU) when transfer is canceled */
           //判断接受区是否为空
           if ((0 != vcomInstance->recvSize) && (USB_CANCELLED_TRANSFER_LENGTH != vcomInstance->recvSize))
          {
               int32_t i;
​
               /* Copy Buffer to Send Buff */
               //将接收区数据拷贝至发送区
               for (i = 0; i < vcomInstance->recvSize; i++)
              {
                   vcomInstance->currSendBuf[vcomInstance->sendSize++] = vcomInstance->currRecvBuf[i];
              }
               vcomInstance->recvSize = 0;
          }
//判断发送区是否为空
           if (vcomInstance->sendSize)
          {
               uint32_t size          = vcomInstance->sendSize;
               vcomInstance->sendSize = 0;
//发送数据
               error = USB_DeviceCdcAcmSend(vcomInstance->cdcAcmHandle, vcomInstance->bulkInEndpoint,
                                            vcomInstance->currSendBuf, size);
​
               if (error != kStatus_USB_Success)
              {
                   /* Failure to send Data Handling code here */
              }
          }
      }
  }
}


这段程序是官方程序中USB的Task,下图是该程序流程图。这段程序主要功能是检测接受区数据是否为空,非空拷贝至发送区;然后检测发送区是否为空,非空便将数据发送出去。

FkTpWz8Rw_zFln85BM_Eq-1_absl

我们主要修改的程序就是这个Task,下面是我的程序:

void USB_DeviceCdcVcomTask(void)
{
    usb_status_t error = kStatus_USB_Error;
    volatile usb_cdc_vcom_struct_t *vcomInstance;
​
    for (uint8_t i = 0; i < USB_DEVICE_CONFIG_CDC_ACM; i++)
    {
        vcomInstance = &g_deviceComposite->cdcVcom[i];
        if ((1 == vcomInstance->attach) && (1 == vcomInstance->startTransactions))
        {
            /* User Code */
            /* endpoint callback length is USB_CANCELLED_TRANSFER_LENGTH (0xFFFFFFFFU) when transfer is canceled */
            if ((0 != vcomInstance->recvSize) && (USB_CANCELLED_TRANSFER_LENGTH != vcomInstance->recvSize))
            {
                int32_t i;
​
                /* Copy Buffer to Send Buff */
                for (i = 0; i < vcomInstance->recvSize; i++)
                {
                    vcomInstance->currSendBuf[vcomInstance->sendSize++] = vcomInstance->currRecvBuf[i];
                }
                vcomInstance->recvSize = 0;
            }

            if (vcomInstance->sendSize)
            {
                uint32_t size          = vcomInstance->sendSize;
                                    vcomInstance->sendSize = 0;
​
//                error = USB_DeviceCdcAcmSend(vcomInstance->cdcAcmHandle, vcomInstance->bulkInEndpoint,
//                                             NULL, size);
​
//                if (error != kStatus_USB_Success)
//                {
//                    /* Failure to send Data Handling code here */
//                }
                                if(i==0)
                                    USART_WriteBlocking(FLEXCOMM0_PERIPHERAL,vcomInstance->currSendBuf,size);
                                else
                                    USART_WriteBlocking(FLEXCOMM2_PERIPHERAL,vcomInstance->currSendBuf,size);

            }
        }
    }
}
 

这里源程序是将发送去程序调用USB_DeviceCdcAcmSend()将数据通过USB发送出去,这里我们借鸡下蛋,改用串口发送,便实现了数据通过USB发送至串口的功能。下面是我的程序的流程图,可以看出只是在发送部分不一致。

FiI1o78Uj1OgTvqE9T3jUCSgpfjj

 

到这里接收功能已经实现,下面实现发送功能。原程序是接收后直接发送,这里我是能了串口接收中断,直接在串口中断函数中接收1位数据,然受调用USB_DeviceCdcAcmSend()函数将其传送至USB。

下面是两个串口的中断函数:

/* FLEXCOMM0_IRQn interrupt handler */
void FLEXCOMM0_FLEXCOMM_IRQHANDLER(void) 
{
  /*  Place your code here */
  uint8_t rec;
    volatile usb_cdc_vcom_struct_t *vcom_1;
    vcom_1 = &g_deviceComposite->cdcVcom[0];
    /* If new data arrived. */
    if ((kUSART_RxFifoNotEmptyFlag | kUSART_RxError) & USART_GetStatusFlags(FLEXCOMM0_PERIPHERAL))
    {
        rec = USART_ReadByte(FLEXCOMM0_PERIPHERAL);
        USB_DeviceCdcAcmSend(g_deviceComposite->cdcVcom[0].cdcAcmHandle, vcom_1->bulkInEndpoint, &rec, 1);
    }
  /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
     Store immediate overlapping exception return operation might vector to incorrect interrupt. */
  #if defined __CORTEX_M && (__CORTEX_M == 4U)
    __DSB();
  #endif
}
​
/* FLEXCOMM2_IRQn interrupt handler */
void FLEXCOMM2_FLEXCOMM_IRQHANDLER(void) 
{
  /*  Place your code here */
  uint8_t rec;
    volatile usb_cdc_vcom_struct_t *vcom_2;
    vcom_2 = &g_deviceComposite->cdcVcom[1];
    /* If new data arrived. */
    if ((kUSART_RxFifoNotEmptyFlag | kUSART_RxError) & USART_GetStatusFlags(FLEXCOMM2_PERIPHERAL))
    {
        rec = USART_ReadByte(FLEXCOMM2_PERIPHERAL);
        USB_DeviceCdcAcmSend(g_deviceComposite->cdcVcom[1].cdcAcmHandle, vcom_2->bulkInEndpoint, &rec, 1);
    }
  /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
     Store immediate overlapping exception return operation might vector to incorrect interrupt. */
  #if defined __CORTEX_M && (__CORTEX_M == 4U)
    __DSB();
  #endif
}

自此我们便完成了数据从串口到USB的发送功能。

两个Flexcomm的初始化代码我这里就不放了,采用MCUXpresso Config Tools直接生成,配置正确即可。

  • 总结感悟

第一次使用NXP的处理器和IDE环境,对其有些不适应,不过研究了一下,还是很快就能上手的。这个小任务还是比较简单的,在demo上进行简单的修改便可实现功能。十分感谢硬禾学堂和digikey,让我有机会同NXP的人员、各位小伙伴进行学习交流,祝硬禾学堂越来越 “硬核” ,Funpack的活动能一直办下去

附件下载
my_dual_cdc.zip
项目代码
团队介绍
团队成员
ZP
搬砖程序猿
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号