EFM32片内外设--DMA之LeUart Tx Rx

本文介绍了一种利用EFM32微控制器的LeUart模块结合DMA进行高效、低功耗串行通信的方法。文章详细展示了如何配置LeUart模块及其与DMA的交互,并通过具体代码实例解释了数据收发过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一直没有具体的自己写过和测试过DMA+LeUart的功能。只是知道EFM32针对LeUart做了很多的优化。例如在EM2的情况下,无论是发送还是接收,都可以自动唤醒DMA,进行传输,大大的降低了整个板子运行时候的功耗。

硬件准备:

TG STK, LEUart0, Tx:PD4, Rx:PD5.  TG STK的20pin扩展口上已经有这两个IO口,分别是12Pin(PD4,Tx),14Pin(PD5,Rx)。

软件准备:

1.使用CMSIS3.0的库了

2.DMA channel 0 作为Rx接收通道,接受数据的长度为1字节。。呵呵。

3.DMA channel 1 作为Tx发送通道,发送数据的长度为10字节。

4.平时进入EM2模式,在接受完毕之后,发送一次10字节的数据。

5.自己犯过一次无限Tx发送的错误,后来发现是逻辑错误。前提是我将Tx发送放在了主循环里面。当Rx接收到数据之后,会触发DMA中断。中断退出之后,在主循环中会触发Tx发送。然后继续进入EM2.而此时Tx发送完毕,又会触发DMA中断,然后又退出EM2,继续运行主程序中的Tx触发函数,周而复始,无限循环。

6.如果要使用call back函数,则必须使能该通道上的DMA中断。每个通道都有一个中断标志。CH0DONE~CH7DONE,外加一个ERROR标志

例程:这次例程会比较长。呵呵

#include "em_chip.h"
#include "efm32.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_leuart.h"
#include "em_dma.h"
#include "em_gpio.h"

unsigned char ucRx_Ok = 0;

/* DEFINES */
#define DMA_RX_CHANNEL    0
#define RX_BUF_MAX        1
#define DMA_TX_CHANNEL    1
#define TX_BUF_MAX        10

unsigned char ucRx_Buffer[RX_BUF_MAX];
unsigned char ucTx_Buffer[TX_BUF_MAX] = {'G','e','c','k','o',',','!','!','!','\0',};

/* DMA control block, must be aligned to 256. */
#if defined (__ICCARM__)
#pragma data_alignment=256
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2];
#elif defined (__CC_ARM)
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
#elif defined (__GNUC__)
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
#else
#error Undefined toolkit, need to define alignment
#endif

/* DMA init structure */
DMA_Init_TypeDef dmaInit;
/* DMA callback structure */
DMA_CB_TypeDef cb[DMA_CHAN_COUNT];


void LeUart_Initial_IO(void)
{
    CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);
   
  /* Reseting and initializing LEUART0 */
  LEUART_Reset(LEUART0);
 
  LEUART_Init_TypeDef leuart1Init =
  {
      .enable   = leuartEnable,     /* Activate data reception on LEUn_RX pin. */
      .refFreq  = 0,                  /* Inherit the clock frequenzy from the LEUART clock source */
      .baudrate = 9600,               /* Baudrate = 9600 bps */
      .databits = leuartDatabits8,    /* Each LEUART frame containes 8 databits */
      .parity   = leuartNoParity,     /* No parity bits in use */
      .stopbits = leuartStopbits1,    /* Setting the number of stop bits in a frame to 2 bitperiods */
  };
 
  LEUART_Init(LEUART0, &leuart1Init);

  /* Route LEUART0 RX pin to DMA location 0 */
  LEUART0->ROUTE = LEUART_ROUTE_RXPEN | LEUART_ROUTE_TXPEN | LEUART_ROUTE_LOCATION_LOC0;

  GPIO_PinModeSet(gpioPortD,            /* Port */
                  5,                    /* Port number */
                  gpioModeInputPull,    /* Pin mode is set to input only, with pull direction given bellow */
                  1);                   /* Pull direction is set to pull-up */
  GPIO_PinModeSet(gpioPortD,            /* Port */
                  4,                    /* Port number */
                  gpioModePushPull,     /* Pin mode is set to input only, with pull direction given bellow */
                  1);
}

void DMAInit(void)
{
    /* Initializing the DMA */
    dmaInit.hprot = 0;
    dmaInit.controlBlock = dmaControlBlock;
    DMA_Init(&dmaInit);
}

void LeUartRxReceiveComplete(unsigned int channel, bool primary, void *user)
{
    DMA_ActivateBasic(DMA_RX_CHANNEL,
                      true,
                      false,
                      NULL,
                      NULL,
                      RX_BUF_MAX - 1);
    GPIO_PinOutSet(gpioPortD, 7);
    ucRx_Ok = 1;
}

void LeUartTxTransmitComplete(unsigned int channel, bool primary, void *user)
{
    /* Disable DMA wake-up from LEUART1 TX */
    LEUART0->CTRL &= ~LEUART_CTRL_TXDMAWU;
    GPIO_PinOutClear(gpioPortD, 7);
}

void DMA_Initial_IO(void)
{
    //Setting up Rx DMA
    /* Setting up channel */
    /* Setting call-back function */ 
    cb[DMA_RX_CHANNEL].cbFunc  = LeUartRxReceiveComplete;
    cb[DMA_RX_CHANNEL].userPtr = NULL;
   
    DMA_CfgChannel_TypeDef chnlCfg =
    {
        .highPri   = false,                     /* Normal priority */
        .enableInt = true,                     /* No interupt enabled for callback functions */
        .select    = DMAREQ_LEUART0_RXDATAV,    /* Set LEUART0 RX data avalible as source of DMA signals */
        .cb        = &(cb[DMA_RX_CHANNEL]),
    };
    DMA_CfgChannel(DMA_RX_CHANNEL, &chnlCfg);
   
    /* Setting up channel descriptor */
    DMA_CfgDescr_TypeDef descrCfg =
    {
        .dstInc  = dmaDataInc1,       /* Increment destination address by one byte */
        .srcInc  = dmaDataIncNone,    /* Do no increment source address  */
        .size    = dmaDataSize1,      /* Data size is one byte */
        .arbRate = dmaArbitrate1,     /* Rearbitrate for each byte recieved*/
        .hprot   = 0,                 /* No read/write source protection */
    };
    DMA_CfgDescr(DMA_RX_CHANNEL, true, &descrCfg);
   
    /* Starting the transfer. Using Basic Mode */
    DMA_ActivateBasic(DMA_RX_CHANNEL,                /* Activate channel selected */
                      true,                       /* Use primary descriptor */
                      false,                      /* No DMA burst */
                      (void *) &ucRx_Buffer,            /* Destination address */
                      (void *) &LEUART0->RXDATA,  /* Source address*/
                      RX_BUF_MAX - 1);               /* Size of buffer minus1 */
   
    /* Make sure the LEUART wakes up the DMA on RX data */
    LEUART0->CTRL = LEUART_CTRL_RXDMAWU;
   
   
    //Setting up Tx DMA
    /* Setting up channel */
    /* Setting call-back function */ 
    cb[DMA_TX_CHANNEL].cbFunc  = LeUartTxTransmitComplete;
    cb[DMA_TX_CHANNEL].userPtr = NULL;
   
    DMA_CfgChannel_TypeDef chnlCfg1 =
    {
        .highPri   = false,                     /* Normal priority */
        .enableInt = true,                     /* No interupt enabled for callback functions */
        .select    = DMAREQ_LEUART0_TXEMPTY,    /* Set LEUART0 RX data avalible as source of DMA signals */
        .cb        = &(cb[DMA_TX_CHANNEL]),
    };
    DMA_CfgChannel(DMA_TX_CHANNEL, &chnlCfg1);
   
    /* Setting up channel descriptor */
    DMA_CfgDescr_TypeDef descrCfg1 =
    {
        .dstInc  = dmaDataIncNone,    /* Do no increment source address  */
        .srcInc  = dmaDataInc1,       /* Increment destination address by one byte */
        .size    = dmaDataSize1,      /* Data size is one byte */
        .arbRate = dmaArbitrate1,     /* Rearbitrate for each byte recieved*/
        .hprot   = 0,                 /* No read/write source protection */
    };
    DMA_CfgDescr(DMA_TX_CHANNEL, true, &descrCfg1);
   
    /* Set new DMA destination address directly in the DMA descriptor */
    dmaControlBlock[DMA_TX_CHANNEL].DSTEND = &LEUART0->TXDATA;
    dmaControlBlock[DMA_TX_CHANNEL].SRCEND = ucTx_Buffer + TX_BUF_MAX - 1;
}

void main(void)
{
    /* Initialize chip */
    CHIP_Init();
   
    /* Start LFXO, and use LFXO for low-energy modules */
    CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);
   
    /* Enabling clocks, all other remain disabled */
    CMU_ClockEnable(cmuClock_CORELE, true);     /* Enable CORELE clock */
    CMU_ClockEnable(cmuClock_DMA, true);        /* Enable DMA clock */
    CMU_ClockEnable(cmuClock_GPIO, true);       /* Enable GPIO clock */
    CMU_ClockEnable(cmuClock_LEUART0, true);    /* Enable LEUART0 clock */
   
    DMAInit();
    LeUart_Initial_IO();
    DMA_Initial_IO();
   
    GPIO_PinModeSet(gpioPortD, 7, gpioModePushPull, 0);
   
    while (1)
    {
        EMU_EnterEM2(true);
        if(ucRx_Ok)//判断是否有接收到数据,如果有,则启动一次发送。
        {
            LEUART0->CTRL = LEUART_CTRL_TXDMAWU;
            DMA_ActivateBasic(DMA_TX_CHANNEL,                  /* Activate channel selected */
                              true,                         /* Use primary descriptor */
                              false,                        /* No DMA burst */
                              NULL,                         /* Keep destination address */
                              NULL,                         /* Keep source address*/
                              TX_BUF_MAX - 1);                 /* Size of buffer minus1 */
            ucRx_Ok = 0;
        }
    }
}

<think>我们正在讨论MCU控制外设的方法或原理。根据引用内容,MCU作为系统的“大脑”,负责数据处理、电能计算、LCD显示控制、通信模块管理等(引用[1])。同时,引用[2]提到了SiliconLabs的EFM32和EFR32系列MCU的外设例子集合,这为我们提供了具体的代码示例参考。此外,引用[3]则强调了在电池供电场景下,通过多个电源域管理来优化功耗。MCU控制外设的基本原理:1.**外设接口**:MCU通过内置的外设接口(如GPIO、UART、SPI、I2C、ADC等)与外部设备连接。2.**寄存器编程**:MCU通过配置外设的寄存器来控制外设的工作模式、数据传送等。3.**中断机制**:外设可以通过中断通知MCU事件发生,实现实时响应。4.**直接内存访问(DMA)**:用于高速数据传输,减少CPU负担。代码示例(以STM32的GPIO控制LED为例):```c#include"stm32f10x.h"intmain(void){//使能GPIOA的时钟(必须步骤)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置GPIOA的Pin0为推挽输出GPIO_InitTypeDefGPIO_InitStructure;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);while(1){//点亮LED(假设LED阳极接PA0,阴极接地)GPIO_SetBits(GPIOA,GPIO_Pin_0);//延时for(volatileuint32_ti=0;i<500000;i++);//熄灭LEDGPIO_ResetBits(GPIOA,GPIO_Pin_0);for(volatileuint32_ti=0;i<500000;i++);}}```此示例展示了如何通过配置GPIO寄存器来控制一个LED灯闪烁。首先使能GPIOA的时钟,然后配置PA0为推挽输出模式,最后在循环中设置和复位该引脚。对于更复杂的外设(如UART、ADC等),原理类似,但配置更复杂。例如,使用UART通信需要配置波特率、数据位、停止位等。**低功耗考虑**:在电池供电的应用中(如引用[3]所述),MCU可以通过关闭未使用外设的时钟或进入低功耗模式来节省电能。例如,在EFM32系列中,可以使用低功耗外设接口(LEUART)在深度睡眠模式下进行串口通信。§§相关问题§§1.MCU控制外设时如何实现低功耗?2.如何通过中断方式处理外设事件?3.能否举例说明MCU通过SPI控制外设的代码?</think>### MCU控制外设的原理与方法 MCU(微控制器)通过其内置的外设接口模块与外部设备交互,核心原理是**寄存器编程**和**总线控制**。主要实现方式包括: 1. **GPIO控制** 最基本的数字信号控制,通过配置数据方向寄存器(DDR)和数据寄存器(DR)实现: ```c // STM32 GPIO控制LED示例 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 点亮PA5引脚LED HAL_Delay(500); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 熄灭 ``` 2. **定时器/PWM控制** 用于电机、背光等模拟控制,通过配置计数器/比较寄存器实现: ```c // STM32 PWM控制示例(50%占空比) TIM_OC_InitTypeDef sConfig = {0}; sConfig.OCMode = TIM_OCMODE_PWM1; sConfig.Pulse = 500; // 周期值=1000时占空比50% HAL_TIM_PWM_ConfigChannel(&htim2, &sConfig, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); ``` 3. **通信接口控制** - **UART串口通信**(配置波特率/数据位): ```c // 发送字符串 HAL_UART_Transmit(&huart2, (uint8_t*)"Hello", 5, 100); ``` - **SPI控制外设**(如传感器): ```c uint8_t tx_data[2] = {0x01, 0x00}; // 读取寄存器0x01 uint8_t rx_data[2]; HAL_SPI_TransmitReceive(&hspi1, tx_data, rx_data, 2, 100); ``` 4. **ADC采集模拟信号** 配置采样通道和分辨率: ```c // 读取ADC值(12位分辨率) HAL_ADC_Start(&hadc1); uint16_t adc_val = HAL_ADC_GetValue(&hadc1); ``` 5. **低功耗管理** 根据引用[3],多电源域管理是关键: ```c // EFM32进入EM2低功耗模式 EMU_EnterEM2(true); // 保留RAM内容,关闭高频时钟 ``` ### 核心控制原理 1. **寄存器映射** 外设通过内存映射寄存器控制,例如: - `USART->DR`:串口数据寄存器 - `TIM2->CCR1`:定时器比较寄存器 2. **中断驱动** 事件触发中断服务函数: ```c void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == BUTTON_PIN) { // 处理按钮按下事件 } } ``` 3. **DMA传输** 高效数据搬运(如ADC连续采样): ```c HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, 256); ``` ### 实际应用场景 - **电能表控制**(引用[1]):MCU通过SPI读取电能计量芯,GPIO驱动LCD显示 - **物联网传感器**(引用[2]):EFM32系列通过I2C采集温湿度,BLE模块无线传输 - **电池管理**(引用[3]):多电源域动态关闭未使用外设时钟 > 提示:现代MCU如STM32CubeMX或EFM32 Simplicity Studio提供图形化配置工具,可自动生成外设初始化代码[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值