CW32L052

BD网盘链接:

https://pan.baidu.com/s/10WHNgB_cicTP1SbdcI4AkQ?pwd=l1et 
提取码:l1et

概述

CW32L052支持DMA(Direct Memory Access),即直接内存访问,无需CPU干预,实现高速数据传输。数据的传输可以发生在:

• 外设和内存之间:例如ADC采集数据到内存,这种传输方式常见于需要将外设采集的数据快速传输到内存进行处理的应用。

• 内存和内存之间:例如在两个不同的数组之间传输数据,或者在不同的内存块之间进行数据拷贝。

• 外设和外设之间:例如从一个SPI主/从机传输数据到另一个SPI从/主机。

使用DMA能够有效减轻CPU的负担,特别是在大量数据需要高效传输的情况下,可以提高系统的整体性能。

框图
1.png

DMA功能框图

特性

使用DMA,最核心的就是配置要传输的数据,包括数据从哪里来,要到哪里去,传输的数据的单位是什么,要传多少数据,是一次传输还是连续传输等等。

2.png

4个DMA通道的优先级和通道号绑定,通道号越小优先级越高,通道号越大优先级越低。

  • 4种传输模式: 

硬件触发BULK传输模式、硬件触发BLOCK传输模式、软件触发BULK传输模式和软件触发BLOCK传输模式。

硬件

 BULK

 BLOCK

软件

 BULK

 BLOCK

BULK传输模式:

适用于小数据块的传输,通常涉及大量的数据点,但每个数据点的大小较小。

与BLOCK模式不同,BULK模式下DMA会更频繁地启动新的传输,因为每个数据点通常被视为单独的传输单元,所以DMA控制器需要在每个数据点传输完成后需要重新配置或者启动DMA。在BULK传输模式下,传输过程不可被打断。

BLOCK传输模式:

适用于大数据块的高速传输,通常用于需要连续传输大量数据的情况。BLOCK模式下,DMA会将数据分成较大的块,并在传输时以这些块为单位进行操作。DMA控制器在一次配置后,连续传输多个数据块,而无需额外的干预或重新配置。每传输完成1个数据块后就要进行一次传输优先级的仲裁,允许CPU或者更高优先级的DMA通道访问当前DMA通道所占用的外设。

• 硬件触发和软件触发:

要想通过DMA来传输数据,必须先给DMA控制器发送DMA请求。部分外设支持硬件触发启动DMA传输,如FLASH存储器、UART串口、TIM定时器、ADC数模转换器等被配置为DMA通道的触发源时,

 3.png

可以产生DMA请求(DMA request),硬件触发启动DMA传输,

 4.png

 而不支持硬件DMA的外设,只能配置为软件触发启动DMA传输。

 5.png

虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。

• DMA中断

DMA通道在传输工程中可产生2个中断标志:传输错误中断标志和传输完成中断标志

不同 DMA 通道的中断各自独立,通过中断标志寄存器 DMA_ISR 可以获取各通道的中断标志。标志对应多个可能的产生原因,具体产生原因需查询 DMA_CSRy.STATUS 状态位,如下表所示

6.png

• 数据宽度:

数据位宽可以设置为8bit、16bit和32bit,DMA通道的源地址和目的地址的位宽必须完全一致

• 数据块数量:

传输的数据块数量可以设置为1 ~ 65535

• 数据传输优先级

当CPU和DMA访问不同的外设时,数据的传输可以同时进行;

当CPU和DMA同时访问同一个外设时,CPU的优先级高于DMA。

从外设到内存BD网盘链接:

https://pan.baidu.com/s/1RlVVPt72fJrnBBUEHf3iJQ?pwd=oklw 
提取码:oklw

通过ADC转换完成标志触发(硬件触发)DMA方式实现外设到内存的DMA传输

核心代码:
#include "main.h" 
#include "delay.h" 
#include "gpio.h" 
#include "cw32l052_lcd.h" 
#include "cw32l052_adc.h" 
#include "cw32l052_dma.h" 
 
#define NUM0 0x070d  //段式LCD数字段码 
#define NUM1 0x0600 
#define NUM2 0x030e 
#define NUM3 0x070a 
#define NUM4 0x0603 
#define NUM5 0x050b 
#define NUM6 0x050f 
#define NUM7 0x0700 
#define NUM8 0x070f 
#define NUM9 0x070b  

void ADC_Configuration(void);      //ADC配置函数 
void DMA_Configuration(void);      //DMA配置函数 
void LCD_Configuration(void);      //LCD配置函数 
void LCD_Proc(uint16_t dispdata);  //LCD子程序函数  

/* 
    **功能说明:
    **ADC采集数据触发DMA,将采集到的数据(1.2V内核电压基准源)存储在内存value中,并显示在LCD屏上 
*/ 

int main(void) 
{   
    LED_Init();   
    LCD_Configuration();   
    ADC_Configuration();   
    DMA_Configuration();   
    while (1)   
    {     
        LCD_Proc(value);  //显示采集到的ADC     
        PA15_TOG();     
        Delay_ms(500);   
    } 
}  

void ADC_Configuration(void) 
{   
    ADC_InitTypeDef   ADC_InitStruct = {0};    
    
    __RCC_ADC_CLK_ENABLE();   
    __RCC_GPIOA_CLK_ENABLE();    
    
    PA00_ANALOG_ENABLE(); //PA00 (AIN0)    
    ADC_InitStruct.ADC_OpMode = ADC_SingleChOneMode;   //单通道单次转换模式   
    ADC_InitStruct.ADC_ClkDiv = ADC_Clk_Div128;       //PCLK   A
    DC_InitStruct.ADC_SampleTime = ADC_SampTime5Clk; //5个ADC时钟周期   
    ADC_InitStruct.ADC_VrefSel = ADC_Vref_VDDA;       //VDDA参考电压(3.3V)   
    ADC_InitStruct.ADC_InBufEn = ADC_BufEnable;       //开启跟随器   
    ADC_InitStruct.ADC_TsEn = ADC_TsDisable;           //内置温度传感器失能   
    ADC_InitStruct.ADC_DMASOCEn = ADC_DMASOCEnable;   //ADC转换完成触发DMA传输   
    ADC_InitStruct.ADC_Align = ADC_AlignRight;         //ADC转换结果右对齐   
    ADC_InitStruct.ADC_AccEn = ADC_AccDisable;         //转换结果累加不使能   
    ADC_Init(&ADC_InitStruct);                        //初始化ADC配置      CW_ADC->CR1_f.DISCARD = FALSE;                    //ADC转换结果保存策略配置:新数据覆盖未被读取的旧数据          CW_ADC->CR1_f.CHMUX = ADC_Vref1P2Input;           //待转换通道配置:1.2V内核电压基准源    
    
    ADC_ClearITPendingBit(ADC_IT_EOC);   
    ADC_ITConfig(ADC_IT_EOC, ENABLE);   
    ADC_EnableNvic(ADC_INT_PRIORITY);    
    ADC_Enable();   
    ADC_SoftwareStartConvCmd(ENABLE);                  //开始转换 
}  

void ADC_IRQHandler(void) 
{     
    /* USER CODE BEGIN */   
    if(ADC_GetITStatus(ADC_IT_EOC) != RESET)   
    {       
        ADC_ClearITPendingBit(ADC_IT_EOC);       
        ADC_SoftwareStartConvCmd(ENABLE);              //开始转换   
    }     
    /* USER CODE END */ 
}  

void NVIC_Configuration(void) 
{     
    __disable_irq();      
    NVIC_ClearPendingIRQ(DMACH1_IRQn);      
    NVIC_EnableIRQ(DMACH1_IRQn);      
    __enable_irq(); 
}  

void DMA_Configuration(void) 
{   
    DMA_InitTypeDef DMA_InitStruct = {0};    
    
    __RCC_DMA_CLK_ENABLE();    
    
    DMA_StructInit(&DMA_InitStruct);      
    DMA_InitStruct.DMA_Mode = DMA_MODE_BLOCK;     //BLOCK模式       
    DMA_InitStruct.DMA_TransferWidth = DMA_TRANSFER_WIDTH_32BIT; //数据宽度32bit   
    DMA_InitStruct.DMA_SrcInc = DMA_SrcAddress_Fix; //源地址固定   
    DMA_InitStruct.DMA_DstInc = DMA_DstAddress_Fix;  //目标地址固定   
    DMA_InitStruct.DMA_TransferCnt = 1;  //数据块数量1   
    DMA_InitStruct.DMA_SrcAddress = (uint32_t)&(CW_ADC->RESULT0);  //数据源地址 (外设)   
    DMA_InitStruct.DMA_DstAddress = (uint32_t)&value;   //传输目标地址   (内存)   
    DMA_InitStruct.TrigMode = DMA_HardTrig;  //硬件触发DMA传输   
    DMA_InitStruct.HardTrigSource = DMA_HardTrig_ADC_SINGLETRANSCOM;  //硬件触发源:ADC单次转换完成标志   
    DMA_Init(CW_DMACHANNEL1,&DMA_InitStruct); //DMA通道1   
    DMA_ClearITPendingBit(DMA_IT_ALL);  //清除DMA中断标志位   
    DMA_ITConfig(CW_DMACHANNEL1, DMA_IT_TC, ENABLE);   //使能DMA通道1中断   
    NVIC_Configuration();   
    DMA_Cmd(CW_DMACHANNEL1, ENABLE); //启动DMA通道1进行传输 
}  

void DMACH1_IRQHandler(void) 
{    
     /* USER CODE BEGIN */   
     if( DMA_GetITStatus(DMA_IT_TC1) )  //DMA通道1传输完成标志     
     {         
         DMA_ClearITPendingBit(DMA_IT_TC1);          
         CW_DMACHANNEL1->CNT = 0x10001;     //REPEAT写1,传输数量为1                                
         DMA_Cmd(CW_DMACHANNEL1, ENABLE);      
     }     
     /* USER CODE END */ 
}

演示:ADC转换结果为1580左右,换算成电压:1580/4096*3.3=1.27V

7.png

从内存到内存

BD网盘链接:

https://pan.baidu.com/s/1ScCk-UqHBjJA5PxwSqMhkg?pwd=4701 
提取码:4701

通过软件触发DMA方式实现内存(FLASH)到内存(SRAM)的DMA传输

核心代码:
//单片机头文件
#include "main.h"
#include "cw32l052_lcd.h"
#include "cw32l052_dma.h"//硬件外设
#include "delay.h"#include "gpio.h"//C库
#include <string.h>

#define NUM0 0x070d  //段式LCD数字段码
#define NUM1 0x0600#define NUM2 0x030e
#define NUM3 0x070a#define NUM4 0x0603
#define NUM5 0x050b#define NUM6 0x050f
#define NUM7 0x0700#define NUM8 0x070f
#define NUM9 0x070b

#define DATASIZE 10

uint16_t const srcBuf[DATASIZE] =   //源内存(FLASH)数据
{  
    9999,8888,7777,6666,5555,  
    4444,3333,2222,1111,0
};

uint16_t dstBuf[DATASIZE]={0};      //目标内存(SRAM)数据

void DMA_Configuration(void);      //DMA配置函数
void LCD_Configuration(void);      //LCD配置函数
void LCD_Proc(uint16_t dispdata);  //LCD子程序函数

uint32_t value=0;  //ADC数值

/*
    **功能说明:
    **将srcBuf数组中的数据通过DMA传送到dstBuf数组中,
    **srcBuf数组中的数据通过const关键词存储到FLASH中,
    **dstBuf数组存储在SRAM程序运行过程中
    **传输完成后比较两数组内容,相同则打开LED1和LED1,LCD上循环显示dstBuf数据;
    **不同则进入死循环,两指示灯闪烁
*/

int main(void)
{  
    LED_Init();  
    LCD_Configuration();  
    DMA_Configuration();  
    DMA_SWTrigCmd(CW_DMACHANNEL1);   //使能通道1软件触发  
    while(DMA_GetFlagStatus(CW_DMACHANNEL1)!=DMA_CHANNEL_STATUS_TRANSCOMPLETE); //等待传输完成  
    if(memcmp(srcBuf,dstBuf,DATASIZE)==0)  //如果srcBuf和dstBuf相同  
    {      
        LED1_ON(); //指示灯      
        LED2_ON();      
        for(int i=0;i<10;i++)  //LCD屏显示dstBuf数据      
        {        
            LCD_Proc(dstBuf[i]);        
            Delay_ms(500);      
        }  
    }  
    else  //如果不相同  
    {    
        while(1)  //进入while死循环    
        {      
            PA15_TOG();  //指示灯      
            PC10_TOG();      
            Delay_ms(500);    
        }  
    }  
     
    while (1)  
    {   
    }
}

void DMA_Configuration(void)
{  
    DMA_InitTypeDef DMA_InitStruct = {0};   
    
    __RCC_DMA_CLK_ENABLE();   
    
    DMA_StructInit(&DMA_InitStruct);   
    DMA_InitStruct.DMA_Mode = DMA_MODE_BLOCK;     //BLOCK模式  
    DMA_InitStruct.DMA_TransferWidth = DMA_TRANSFER_WIDTH_16BIT; //数据宽度16bit  
    DMA_InitStruct.DMA_SrcInc = DMA_SrcAddress_Increase; //源地址固定  
    DMA_InitStruct.DMA_DstInc = DMA_DstAddress_Increase;  //目标地址递增  
    DMA_InitStruct.DMA_TransferCnt = DATASIZE;  //数据块数量  
    DMA_InitStruct.DMA_SrcAddress = (uint32_t)&srcBuf[0];  //数据源地址 (内存)  
    DMA_InitStruct.DMA_DstAddress = (uint32_t)&dstBuf[0];   //传输目标地址 (内存)  
    DMA_InitStruct.TrigMode = DMA_SWTrig;  //软件触发DMA传输
  
    DMA_Init(CW_DMACHANNEL1,&DMA_InitStruct); //DMA通道1  
    DMA_Cmd(CW_DMACHANNEL1, ENABLE); //启动DMA通道1进行传输
}

演示:LCD屏上显示通过DMA传输的dstBuf的数据

8.png

从外设到外设

BD网盘链接:

https://pan.baidu.com/s/1fSyMPAapft2a_Vy_d3F9vw?pwd=dw6x   
提取码:dw6x

通过硬件触发DMA方式实现外设(SPI)到外设(SPI)的DMA传输

核心代码:
/*单片机头文件*/
#include "main.h"
/*硬件驱动*/
#include "delay.h"
#include "gpio.h"
#include "cw32l052_dma.h"
#include "cw32l052_spi.h"
/*C库*/
#include <string.h>

//硬件连接//SPIY_SCK  (PA10) -- SPIX_SCK  (PB13)
//SPIY_MISO (PA11) -- SPIX_MISO (PB14)
//SPIY_MOSI (PA12) -- SPIX_MOSI (PB15)

//SPI2相关定义(Master)
#define    SPIX                     CW_SPI2               
#define   SPIX_GPIO          CW_GPIOB
#define    SPIX_SCK_PIN             GPIO_PIN_13
#define   SPIX_MISO_PIN            GPIO_PIN_14
#define    SPIX_MOSI_PIN            GPIO_PIN_15
#define    SPIX_AF_SCK              PB13_AFx_SPI2SCK()
#define    SPIX_AF_MISO             PB14_AFx_SPI2MISO()
#define    SPIX_AF_MOSI             PB15_AFx_SPI2MOSI()
#define    SPIX_RX_DMACHANNEL       CW_DMACHANNEL1
#define    SPIX_TX_DMACHANNEL       CW_DMACHANNEL2  
#define     SPIX_DMA_RXTRIGSOURCE    DMA_HardTrig_SPI2_RXBufferNE
#define     SPIX_DMA_TXTRIGSOURCE    DMA_HardTrig_SPI2_TXBufferE

//SPI1相关定义(Slave)
#define    SPIY                     CW_SPI1
#define   SPIY_GPIO          CW_GPIOA
#define    SPIY_SCK_PIN             GPIO_PIN_10
#define    SPIY_MISO_PIN            GPIO_PIN_11
#define    SPIY_MOSI_PIN            GPIO_PIN_12
#define    SPIY_AF_SCK              PA10_AFx_SPI1SCK()
#define    SPIY_AF_MISO             PA11_AFx_SPI1MISO()
#define    SPIY_AF_MOSI             PA12_AFx_SPI1MOSI()

//数组长度
#define   BUFFERSIZE                 ARRAY_SZ(TxBuffer1)

//发送内容1
uint8_t TxBuffer1[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,                       
                       0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,                       
                       0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,                       
                       0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,                       
                       0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23                      
                      };

//发送内容2
uint8_t TxBuffer2[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,                       
                       0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,                       
                       0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,                       
                       0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,                       
                       0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83                      
                      }; 
                      
uint8_t RxBuffer1[BUFFERSIZE];  //接收数组1
uint8_t RxBuffer2[BUFFERSIZE];  //接收数组2
uint8_t TxCounter = 0;  //发送计数
uint8_t RxCounter = 0;  //接收计数 

uint8_t TransferStatus1 = 1;  //DMA传输状态标志1
uint8_t TransferStatus2 = 1;  //DMA传输状态标志2 

void DMA_Configuration(void);      //DMA配置函数
void SPI_Configuration(void);      //SPI配置函数
void SPI_GPIO_Configuration(void); //SPI相关GPIO口配置 

/*
    **功能说明:
    **主机SPIY发送TxBuffer1中的数据,从机SPIX通过DMA接收数据并存储到RxBuffer1
  **主机SPIY发送无效数据,启动SPI通信,同时SPIX从机通过DMA发送TxBuffer2中的数据,SIPY接收数据并存储到RxBuffer2
  **单独比较TxBuffer1与RxBuffer1、TxBuffer2与RxBuffer2中的内容,比较结果通过LED灯指示
*/

int main(void)
{  
  LED_Init();               //初始化LED指示灯  
  SPI_GPIO_Configuration(); //配置PI相关GPIO口  
  DMA_Configuration();      //配置DMA传输  
  SPI_Configuration();      //配置SPI传输  
  SPI_DMACmd(SPIX, SPI_DMAReq_Rx, ENABLE); //使能SPIX DMA RX  
  SPI_NSSInternalSoftwareConfig(SPIX, SPI_NSSInternalSoft_Reset);//位选CS选中从机SPIX,起始信号  
  while(TxCounter < BUFFERSIZE)  
  {    
    while(SPI_GetFlagStatus(SPIY,SPI_FLAG_TXE) == RESET);//等待发送缓冲空(为空后硬件自动置1)    
    SPI_SendData(SPIY,TxBuffer1[TxCounter++]); //发送TxBuffer1中的数据,通过数据寄存器DR把数据填充到发送缓冲区中  
  }  
    
    while(DMA_GetFlagStatus(SPIX_RX_DMACHANNEL) != DMA_CHANNEL_STATUS_TRANSCOMPLETE);//等待DMA接收完成
        
    SPI_NSSInternalSoftwareConfig(SPIX, SPI_NSSInternalSoft_Set); //释放从机SPIX,结束信号  
    TransferStatus1 = memcmp(TxBuffer1, RxBuffer1, BUFFERSIZE); //对比两数组数据  
    if(TransferStatus1==0)  //如果数据相同  
    {    
        LED1_ON();  //LED1指示  
    }  
    else  
    {    
       LED1_OFF();  
    }   
        
    TxCounter = 0;  
    SPI_ReceiveData(SPIY);//读DR以清除RXNE(接收非空)标志位  
    SPI_DMACmd(SPIX, SPI_DMAReq_Rx, DISABLE);//失能SPIX DMA RX  
    SPI_FlushSendBuff(SPIX);//清空发送缓冲区和移位寄存器  
    SPI_DMACmd(SPIX, SPI_DMAReq_Tx, ENABLE);//使能SPIX DMA TX  
    SPI_NSSInternalSoftwareConfig(SPIX, SPI_NSSInternalSoft_Reset);  
        
    while(TxCounter < BUFFERSIZE)  
    {          
        while(SPI_GetFlagStatus(SPIY, SPI_FLAG_TXE) == RESET){;} //主机发送数据以启动SPI通信    
        SPI_SendData(SPIY, TxBuffer1[TxCounter++]);     
           
        while(SPI_GetFlagStatus(SPIY, SPI_FLAG_RXNE) == RESET){;}      
        RxBuffer2[RxCounter++] = SPI_ReceiveData(SPIY);  //获取接收缓冲区中的内容    
    }  
        
    while(SPI_GetFlagStatus(SPIY,SPI_FLAG_BUSY) == SET); //检查数据是否已经全部通过SPI发送完毕    
    SPI_NSSInternalSoftwareConfig(SPIX, SPI_NSSInternalSoft_Set); //释放  
    TransferStatus2 = memcmp(TxBuffer2, RxBuffer2, BUFFERSIZE);  //检查  
    if(TransferStatus2 == 0)  
    {    
        LED2_ON();  
    }  
    else  
    {    
        LED2_OFF();  
    }  
    while (1)  
    {   
    }
}

void SPI_GPIO_Configuration(void)
{    
    GPIO_InitTypeDef GPIO_InitStructure = {0};     
    //打开GPIO时钟    
    __RCC_GPIOA_CLK_ENABLE();    
    __RCC_GPIOB_CLK_ENABLE();
    
  //SPI SCK MOSI MISO 复用    
  SPIY_AF_SCK;    
  SPIY_AF_MISO;    
  SPIY_AF_MOSI;    
  SPIX_AF_SCK;    
  SPIX_AF_MISO;    
  SPIX_AF_MOSI;
  
  //推挽输出    
  GPIO_InitStructure.Pins = SPIY_SCK_PIN | SPIY_MOSI_PIN;    
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;    
  GPIO_Init(SPIY_GPIO, &GPIO_InitStructure);
   
   GPIO_InitStructure.Pins = SPIX_MISO_PIN;    
  GPIO_Init(SPIX_GPIO, &GPIO_InitStructure);
  
  //浮空输入    
  GPIO_InitStructure.Pins = SPIX_SCK_PIN | SPIX_MOSI_PIN;    
  GPIO_InitStructure.Mode = GPIO_MODE_INPUT;    
  GPIO_Init(SPIX_GPIO, &GPIO_InitStructure);
  
  GPIO_InitStructure.Pins = SPIY_MISO_PIN;    
  GPIO_Init(SPIY_GPIO, &GPIO_InitStructure);}
void SPI_Configuration(void)
{  
    SPI_InitTypeDef SPI_InitStructure = {0};   
    
    __RCC_SPI1_CLK_ENABLE();  
    __RCC_SPI2_CLK_ENABLE();   
    
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线全双工模式  
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式  
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据位宽8bit  
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;  //时钟极性  
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//时钟相位,奇数边缘采样  
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //配置NSS引脚(片选信号线)的使用模式,软件控制  
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //波特率:PCLK8分频  
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //MSB先行模式  
    SPI_InitStructure.SPI_Speed = SPI_Speed_Low; //低速  
    SPI_Init(SPIY,&SPI_InitStructure);   
    
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; //从机模式  
    SPI_Init(SPIX,&SPI_InitStructure); 
     
    SPI_Cmd(SPIX,ENABLE);  
    SPI_Cmd(SPIY,ENABLE);
}

void DMA_Configuration(void)
{  
    DMA_InitTypeDef DMA_InitStructure = {0};  
     
    __RCC_DMA_CLK_ENABLE();   //DMA TX 
     
    DMA_InitStructure.DMA_Mode = DMA_MODE_BLOCK; //BLOCK模式  
    DMA_InitStructure.DMA_TransferWidth = DMA_TRANSFER_WIDTH_8BIT;  
    DMA_InitStructure.DMA_SrcInc = DMA_SrcAddress_Increase;  
    DMA_InitStructure.DMA_DstInc = DMA_DstAddress_Fix;  
    DMA_InitStructure.TrigMode = DMA_HardTrig;  
    DMA_InitStructure.HardTrigSource = SPIX_DMA_TXTRIGSOURCE;  
    DMA_InitStructure.DMA_TransferCnt = BUFFERSIZE;  
    DMA_InitStructure.DMA_SrcAddress = (uint32_t)&TxBuffer2[0];  
    DMA_InitStructure.DMA_DstAddress = (uint32_t)&SPIX->DR; //数据寄存器  
    DMA_Init(SPIX_TX_DMACHANNEL,&DMA_InitStructure);  
    DMA_Cmd(SPIX_TX_DMACHANNEL,ENABLE);   
    
    //DMA RX      
    DMA_InitStructure.DMA_Mode = DMA_MODE_BLOCK;  
    DMA_InitStructure.DMA_TransferWidth = DMA_TRANSFER_WIDTH_8BIT;  
    DMA_InitStructure.DMA_SrcInc = DMA_SrcAddress_Fix;  
    DMA_InitStructure.DMA_DstInc = DMA_DstAddress_Increase;  
    DMA_InitStructure.TrigMode = DMA_HardTrig;  
    DMA_InitStructure.HardTrigSource = SPIX_DMA_RXTRIGSOURCE;  
    DMA_InitStructure.DMA_TransferCnt = BUFFERSIZE;  
    DMA_InitStructure.DMA_SrcAddress = (uint32_t)&SPIX->DR;  
    DMA_InitStructure.DMA_DstAddress = (uint32_t)&RxBuffer1[0];  
    DMA_Init(SPIX_RX_DMACHANNEL,&DMA_InitStructure);  
    DMA_Cmd(SPIX_RX_DMACHANNEL,ENABLE);
}

来源:CW32生态社区

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

围观 7

BD网盘链接:

https://pan.baidu.com/s/1dmtMWcK1TII-vutsS8X0Og?pwd=5wwy 
提取码:5wwy

概述

CW32L052内部集成了64KB嵌入式FLASH供用户使用,可用来存储应用程序和用户数据。

芯片支持对 FLASH 存储器的读、擦除和写操作,支持擦写保护和读保护。

芯片内置 FLASH 编程所需的高压 BOOST 电路,无须额外提供编程电压。

FLASH存储器组织

  • 总容量64KB,分页管理

  • 每页 512 字节

  • 共 128 页

FLASH存储器保护

FLASH 存储器具有擦写保护和读保护功能。

  • 擦写保护

包括锁定页擦写保护和PC 地址页擦写保护,处于保护状态的页面不能被擦写,可避免 FLASH 内容被意外改写。

  • 读保护

  • 以整片 FLASH 为保护对象,不支持单页保护,可避免用户代码被非法读取。

FLASH存储器操作

FLASH 存储器操作包括:读操作、擦除、写(编程)操作。

页擦除

FLASH 的页擦除操作的最小单位为 1 页,即 512 字节。页擦除操作完成后,该页所有地址空间的数据内容均为 0xFF。

如果对未解锁的 FLASH 页面进行页擦除操作,或者对*正在运行的程序[^1]*进行擦除操作,会操作失败,产生错误中断标志。

CW32L052 内部 FLASH 存储器被划分为 128 页,每 8 页对应擦写锁定寄存器的1 个锁定位。擦写锁定寄存器的各位域与 FLASH 锁定页面的对应关系如下表所示:

1.png

写操作

基于嵌入式 FLASH 的特性,写操作只能将 FLASH 存储器中位数据由‘1’改写为‘0’,不能由‘0’改写为‘1’, 因此在写数据之前先要对对应地址所在页进行擦除操作。

基于以上陈述,总结出以下三个原则:

  • 不可对数据位内容为‘0’的地址写入

  • 不可对锁定区域内的地址写入

  • 不可对 PC(程序指针)所在的页的地址写入

读操作

CW32L052 对 FLASH 的读操作支持 3 种不同位宽,可采用直接访问绝对地址方式读取,读取的数据位宽必 须和对应地址边界对齐。

核心代码

//单片机头文件
#include "main.h"
//硬件驱动
#include "gpio.h"
#include "delay.h"

//子程序
void LCD_Configuration(void);       //段式LCD配置函数
void LCD_Display(uint16_t dispdata);     //段式LCD显示函数
uint8_t FLASH_Erase(void);         //FLASH页擦除函数
uint8_t FLASH_Write(uint8_t *ByteData,uint16_t amount); //FLASH写操作函数

int main(void)
{
 int i;
 int temp8;
 uint8_t cnt=0;
 uint8_t WriteBuf[256];
 
    LED_Init();    //初始化程序运行情况指示灯
 LCD_Configuration();    //配置LCD液晶显示屏
 
 FLASH_Erase();          //页擦除操作
 for(i=0;i<256;i++)      //验证是否擦除成功
 {
  temp8=*((volatile uint8_t*)(512*127+i));
  if(temp8!=0xff)
  {
   while(1)
   {
    LED2_ON();       //LED2闪烁
    Delay_ms(300);
    LED2_OFF();
    Delay_ms(300);
   }
  }
 }
 
 for(i=0;i<256;i++)      //准备写入FLASH存储器的数据
 {
  WriteBuf[i]=i;
 }
 FLASH_Write(WriteBuf,256); //写操作
 for(i=0;i<255;i++)           //验证是否写入正确
 {
  temp8=*((volatile uint8_t*)(512*127+i));
  if(temp8!=i)
  {
   while(1)
   {
    LED1_ON();     //LED1、LED2同时闪烁指示写入失败
    LED2_ON();
    Delay_ms(300);
    LED1_OFF();
    LED2_OFF();
    Delay_ms(300);
   }
  }
 }
 
 LED1_ON();       //指示擦除、读、写均成功
 LED2_ON();
    while(1)
    {
  LCD_Display(*((volatile uint8_t*)(512*127+cnt)));  //LCD上依次显示写入的数据
  Delay_ms(500);
  cnt++;
    }
}

uint8_t FLASH_Erase(void)    //页擦除
{
 int flag=1;
 
 FLASH_UnlockPages(512*127,512*127);
 flag=FLASH_ErasePages(512*127,512*127);
 FLASH_LockAllPages();
 if(flag!=0)
 {
  while(1)
  {
   LED1_ON();
   Delay_ms(300);
   LED1_OFF();
   Delay_ms(300);
  }
 }
 
 return 0;
}

uint8_t FLASH_Write(uint8_t *ByteData,uint16_t amount)  //写操作
{
 int flag=1;
 
 FLASH_UnlockPages(512*127,512*127);
 flag=FLASH_WriteBytes(512*127,ByteData,amount);
 FLASH_LockAllPages();
 if(flag!=0)
 {
  while(1)
  {
   LED2_ON();
   Delay_ms(300);
   LED2_OFF();
   Delay_ms(300);
  }
 }
 
 return 0;
}

void LCD_Configuration(void)      //段式LCD配置
{
    __RCC_LCD_CLK_ENABLE();
 RCC_LSI_Enable();
 
    LCD_InitTypeDef LCD_InitStruct = {0};

    LCD_InitStruct.LCD_Bias = LCD_Bias_1_3;
    LCD_InitStruct.LCD_ClockSource = LCD_CLOCK_SOURCE_LSI;
    LCD_InitStruct.LCD_Duty = LCD_Duty_1_4;
    LCD_InitStruct.LCD_ScanFreq = LCD_SCAN_FREQ_256HZ;
    LCD_InitStruct.LCD_VoltageSource = LCD_VoltageSource_Internal;

    LCD_Init(&LCD_InitStruct); 
 LCD_COMConfig(LCD_COM0 | LCD_COM1 | LCD_COM2 | LCD_COM3, ENABLE);
    LCD_SEG0to23Config(LCD_SEG0|LCD_SEG1|LCD_SEG2|LCD_SEG3|LCD_SEG4|LCD_SEG5|LCD_SEG6|LCD_SEG7, ENABLE);
   
 LCD_Cmd(ENABLE);
}

void LCD_Display(uint16_t dispdata)   //LCD显示
{
 uint16_t DisBuf[10]={NUM0,NUM1,NUM2,NUM3,NUM4,NUM5,NUM6,NUM7,NUM8,NUM9};
 
 LCD_Write(LCD_RAMRegister_0,0x00000000);
 LCD_Write(LCD_RAMRegister_1,0x00000000);
 
 if(dispdata<10)
  LCD_Write(LCD_RAMRegister_0,DisBuf[dispdata]);
 else if(dispdata<100)
  LCD_Write(LCD_RAMRegister_0,DisBuf[dispdata/10]|DisBuf[dispdata%10]<<16);
 else if(dispdata<1000)
 {
  LCD_Write(LCD_RAMRegister_0,DisBuf[dispdata/100]|DisBuf[dispdata/10%10]<<16);
  LCD_Write(LCD_RAMRegister_1,DisBuf[dispdata%10]);
 }
 else
 {
  LCD_Write(LCD_RAMRegister_0,0xffffffff);
  LCD_Write(LCD_RAMRegister_1,0xffffffff);
 }
 
}

视频演示

2.gif

补充

FLASH存储器和EEPROM存储器对比

一般性的总结: 

3.png

使用场景侧重:

  • EEPROM:频繁的擦写操作,如存储计数器、传感器数据等

  • FLASH:大容量、高速读写,如存储程序代码和固件等

来源:CW32生态社区

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

围观 5

2023年2月,武汉芯源半导体超低功耗家族再添新成员——32位M0+内核MCU CW32L052系列芯片。

CW32L052 是基于 eFlash 的单芯片低功耗微控制器,集成了主频高达 48MHz 的 ARM® Cortex®-M0+ 内核、高速嵌入式存储器(最大64K字节FLASH、最大8K字节RAM),支持最多55路I/O接口。

1.png

CW32L052内部框图

外设包括:1路12位ADC,3路UART(支持LIN通信接口)、2路SPI、2路IIC、模拟电压比较器、低电压检测器、多路定时器以及最多8×36段LCD驱动器等功能模块。

CW32L052工作温度为-40℃~85℃,宽工作电压1.65V~5.5V,支持Sleep和DeepSleep两种低功耗工作模式,在DeepSleep模式下工作电流仅为500nA,从这一模式下唤醒时间仅为4us。

Sleep 模式

在 Sleep 模式下,CPU 停止运行,所有外设保持工作,并且可以在发生中断或事件的时候唤醒 CPU。

DeepSleep 模式

DeepSleep 用于实现最低功耗,CPU 停止运行,高速时钟模块(HSE、HSIOSC)自动关闭,低速时钟(LSE、LSI、RC10K、RC150K)保持原状态不变。

当发生外部复位,或 IWDT 复位,或部分外设中断发生,或 RTC 事件发生时,芯片退出 DeepSleep 模式。

CW32L052作为广受市场欢迎的低功耗MCU的补充,继承了CW32L083优秀的设计理念与框架,并在CW32L083的基础上进行了部分精简与优化,降低成本但不妥协性能。目前已推出LQFP64、LQFP48两种封装产品,可广泛应用于物联网、便携医疗、智能家居等领域。

CW32L052 家族产品功能列表

2.png

武汉芯源半导体官方网站已经发布了CW32L052系列MCU的数据手册、用户手册、应用笔记等技术文档,以及配套的StartKit开发板资料、底层外设驱动软件库和应用例程等SDK资料。

同时,可免费提供样品,欢迎选型进行测试。

关于CW32L052更多详细信息,请访问官方网站www.whxy.com

来源:武汉芯源半导体

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

围观 15
订阅 RSS - CW32L052