当前位置: 首页 > news >正文

技巧小结:根据寄存器手册写常用外设的驱动程序

需求:根据STM32F103寄存器手册写DMA模块的驱动程序

一、分析标准库函数的写法:

  1. 各个外设的寄存器地址定义在stm32f10x.h文件中:此文件由芯片厂家提供;
  2. 内核的有关定义则定义在core_cm3.h文件中:ARM提供;
1、查看外设区域多级划分的基地址
一级地址划分

Flash地址、SRAM地址、外设地址、内核地址
查看数据手册的存储映像章节
在这里插入图片描述

在这里插入图片描述

二级地址划分

外设地址分为AHBAPB2APB1三个区域。

具体的对于APB1、APB2、AHB的划分则体现在参考手册存储器和总线架构章节的存储器映像小结

在这里插入图片描述

  • 除了SDIO外设的地址例外,AHB总线上的外设其余的都是以0x4000 0000 + 0x20000为基地址

在这里插入图片描述

  • APB2总线上的外设都是以0x4000 0000 + 0x10000为基地址

在这里插入图片描述
在这里插入图片描述

  • APB1总线上的外设都是以0x4000 0000 + 0x0为基地址
三级地址划分

外设各个模块的基地址,上图即数据手册中的存储器映像图。 列出了所用STM32F10xxx中内置外设的起始地址,比如
在这里插入图片描述

  • GPIOA外设模块挂在APB2总线,以APB2地址(0x4001 0000)为基地址,偏移地址则是0x0800,则得到GPIOA的基地址为0x4001 0000+0x0800=0x4001 0800GPIOA外设寄存器结束地址为0x4000 0BFF
2、代码实现上述外设区域的多级地址划分
一级地址:Flash、SRAM、外设
/*!< FLASH base address in the alias region */
#define FLASH_BASE            ((uint32_t)0x08000000) 
/*!< SRAM base address in the alias region */
#define SRAM_BASE             ((uint32_t)0x20000000) 
/*外设区域的基地址,地址需要定义为uint32_t */
#define PERIPH_BASE           ((uint32_t)0x40000000) 
/*!< 并不是所有F103都具有FSMC外设,这里是为了兼容有此模块的芯片 */
#define FSMC_R_BASE           ((uint32_t)0xA0000000) 
  • 总线划分成4个大的区域
二级地址:APB1、APB2、AHB
/*APB1外设区域的基地址,地址需要定义为uint32_t */
#define APB1PERIPH_BASE       PERIPH_BASE
/*APB2外设区域的基地址,地址需要定义为uint32_t */
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
/*AHB外设区域的基地址,地址需要定义为uint32_t */
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
  • 采用基地址+偏移地址的形式得到各个地址的具体值
三级地址:具体到某个模块的基地址,以GPIO为例
/*GPIOA挂在APB2外设区域,因此基于APB2PERIPH_BASE进行偏移 */
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE            (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE            (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE            (APB2PERIPH_BASE + 0x2000)
  • 得到每个外设模块的基地址
3、访问外设模块具体的每个寄存器地址

有一种方式是每个寄存器的地址都进行宏定义,然后使用指针访问地址,实现访问寄存器的功能,但这种调用时不够方便,因此标准库函数里采用了结构体的形式访问寄存器

3.1 查看GPIOA有哪些寄存器

参考手册的GPIO AFIO寄存器地址映象,说明GPIOA~GPIOG的寄存器完全一样。

在这里插入图片描述
在这里插入图片描述

  • 具体的每个寄存器的功能则见参考手册8.2GPIO寄存器描述和8.3复用功能I/O和调试配置(AFIO)。每个寄存器都是32位,一般都需要按字节或者按位访问
3.2 代码中按格式定义GPIOA的寄存器结构体

按照手册中的寄存器地址声明结构体模板

#define     __IO    volatile 
typedef struct
{__IO uint32_t CRL;__IO uint32_t CRH;__IO uint32_t IDR;__IO uint32_t ODR;__IO uint32_t BSRR;__IO uint32_t BRR;__IO uint32_t LCKR;
} GPIO_TypeDef;
3.3 代码中按格将GPIOA基地址强转成GPIOA的寄存器结构体类型
#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)

这里并没有实体化结构体,而是在直接访问地址,只是和直接指针访问地址有点区别,更简洁一些,比如

GPIOA.CRL = 0x1;
展开后表示
((GPIO_TypeDef *) GPIOA_BASE).CRL = 0x1;
  • 对于结构体而言,.运算就是表示在结构体变量的首地址基础上偏移一个偏移地址,从而到达成员变量的存放地址,这个偏移地址值则和结构体类型里的成员顺序有关。

  • 那么这里,GPIOA_BASE表示的是结构体类型指针,也就是这个地址后面的数据的访问按照GPIO_TypeDef结构体模板的形式即依次是成员变量CRL、CRH、IDR、ODR、BSRR、BRR、LCKR.CRL意味着偏移地址是0x1(由结构体模板GPIO_TypeDef决定),也就是GPIOA_BASE+0x1,这是CRL成员存放的地址。

两种访问方式的对比:
如果是直接的指针访问

#define GPIOA_CRL               (GPIOA_BASE + 0x1)
#define GPIOA_CRH               (GPIOA_BASE + 0x2)
#define GPIOA_IDR               (GPIOA_BASE + 0x3)
#define GPIOA_ODR               (GPIOA_BASE + 0x4)
#define GPIOA_BSRR              (GPIOA_BASE + 0x5)
#define GPIOA_BRR               (GPIOA_BASE + 0x6)
#define GPIOA_LCKR              (GPIOA_BASE + 0x7)
*(uint32_t*)GPIOA_CRL               = 0x1;
*(uint32_t*)GPIOA_CRH               = 0x1;
*(uint32_t*)GPIOA_IDR               = 0x1;
*(uint32_t*)GPIOA_ODR               = 0x1;
*(uint32_t*)GPIOA_BSRR              = 0x1;
*(uint32_t*)GPIOA_BRR               = 0x1;
*(uint32_t*)GPIOA_LCKR              = 0x1;

如果是结构体访问

#define GPIOA         ((GPIO_TypeDef *) GPIOA_BASE)
GPIOA.CRL    = 0x1;
GPIOA.CRH    = 0x1;
GPIOA.IDR    = 0x1;
GPIOA.ODR    = 0x1;
GPIOA.BSRR   = 0x1;
GPIOA.BRR    = 0x1;
GPIOA.LCKR   = 0x1;

而且一般IDE都有代码提示功能,会自动提示结构体有哪些成员,就不用再逐个查找具体的寄存器了,所以这种结构体访问方式更简洁。

4、位运算掩码

此外,一般还会宏定义一些掩码方便操作寄存器,类似于这种

1、宏定义
#define BIT1_MASK       ((uint32_t)(1<<1))
#define BITn_MASK(n)    ((uint32_t)(1<<n))
2、使用时
GPIOA.CRL |=  BIT1_MASK   ;把CRL寄存器的bit1位置的值置1       
GPIOA.CRL &= (~BIT1_MASK) ;把CRL寄存器的bit1位置的值置0  
GPIOA.CRL ^=  BIT1_MASK   ;把CRL寄存器的bit1位置的值翻转 

按位运算的口诀:
1、或1则置1或运算就是有一个为1就为1,因此用来置1
1、或0则不变1|0的结果是1,0|0的结果是0,因此此位保持不变)
2、与0则置0与运算就是有一个为0就为0,因此用来置0
2、与1则不变1&0的结果是1,0&0的结果是0,因此此位保持不变)
3、异或1则翻转异或运算就是1碰到1变成1,0碰到1变成1,因此用来翻转
3、异或0则不变(异或运算就是1碰到0变成1,0碰到0变成0,因此保持不变)

异或运算^为:相同为0,不同为1;


5、位运算掩码的使用

以控制GPIO电平输出的函数GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)为例:

/*** @brief  Sets the selected data port bits.* @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.* @param  GPIO_Pin: specifies the port bits to be written.*   This parameter can be any combination of GPIO_Pin_x where x can be (0..15).* @retval None*/
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{/* Check the parameters */assert_param(IS_GPIO_ALL_PERIPH(GPIOx));assert_param(IS_GPIO_PIN(GPIO_Pin));GPIOx->BSRR = GPIO_Pin;
}

分析:
(1)GPIOx:指定是哪一组GPIO

  • GPIO_TypeDef类型:GPIOA~GPIOG
  • 如果参数是GPIOA,也就是把GPIOA的基地址传递进来;
typedef struct
{__IO uint32_t CRL; //端口配置低寄存器(GPIOx_CRL) (x=A..E)__IO uint32_t CRH; //端口配置高寄存器(GPIOx_CRH) (x=A..E)__IO uint32_t IDR; //端口输入数据寄存器(GPIOx_IDR) (x=A..E)__IO uint32_t ODR; //端口输出数据寄存器(GPIOx_ODR) (x=A..E)__IO uint32_t BSRR;//端口位设置/清除寄存器(GPIOx_BSRR) (x=A..E)__IO uint32_t BRR; //端口位清除寄存器(GPIOx_BRR) (x=A..E)__IO uint32_t LCKR;//端口配置锁定寄存器(GPIOx_LCKR) (x=A..E)
} GPIO_TypeDef;

(2)GPIO_Pin:指定GPIO引脚号
uint16_t类型,此参数用于指定引脚号,组号和引脚号共同确定是哪个具体引脚。结合GPIO寄存器的特点,此参数需要赋值给对应低位GPIO寄存器从而控制寄存器的某一位的值,因此其需要设计成掩码,即:

 GPIOx->BSRR = GPIO_Pin;对应的使用例子:GPIO_SetBits(GPIOA,GPIO_Pin_5);						 //PB.5 输出高内部展开也就是:GPIOA->BSRR = GPIO_Pin_5;

那么GPIO_Pin_5就是赋值掩码,需要用户定义:

 /*!< Pin 5 selected */
#define GPIO_Pin_5                 ((uint16_t)0x0020) 

这个其实也就是选择bit5,等价于

#define GPIO_Pin_5                 ((uint16_t)(1<<5)) 

查看参考手册里BSRR寄存器的描述:如果想将GPIOA的5脚即PA5的输出电平设置为1,那么只需将GPIOABSRR寄存器的BS5位即bit5位设置为1,剩余的其他位写0即对其他引脚不产生影响。而函数GPIO_SetBits(GPIOA,GPIO_Pin_5); 就达到了这个目的:
(1)指定GPIOA
(2)指定BSRR寄存器
(3)指定引脚的掩码值GPIO_Pin_5
在这里插入图片描述

二、DMA外设的驱动程序

1、一级基地址
 /*!< Peripheral base address in the alias region */
#define PERIPH_BASE           ((uint32_t)0x40000000)
2、二级基地址
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
3、三级基地址

在这里插入图片描述

#define DMA1_BASE             (AHBPERIPH_BASE + 0x0000)
#define DMA1_Channel1_BASE    (AHBPERIPH_BASE + 0x0008)
#define DMA1_Channel2_BASE    (AHBPERIPH_BASE + 0x001C)
#define DMA1_Channel3_BASE    (AHBPERIPH_BASE + 0x0030)
#define DMA1_Channel4_BASE    (AHBPERIPH_BASE + 0x0044)
#define DMA1_Channel5_BASE    (AHBPERIPH_BASE + 0x0058)
#define DMA1_Channel6_BASE    (AHBPERIPH_BASE + 0x006C)
#define DMA1_Channel7_BASE    (AHBPERIPH_BASE + 0x0080)
4、外设的结构体定义

在这里插入图片描述
在这里插入图片描述

  • 前面的DMA_ISRDMA_IFCR寄存器是DMA模块的中断管理,适用于所有DMA通道。
  • 后面的DMA_CCR1、DMA_CCR2、DMA_CCR3、DMA_CCR4、DMA_CCR5、DMA_CCR6、DMA_CCR7则是对应到每个DMA通道的控制寄存器。
  • 因此为了使用方便,定义结构体时将两者分开
typedef struct
{__IO uint32_t ISR;  //DMA中断状态寄存器(DMA_ISR)__IO uint32_t IFCR; //DMA中断标志清除寄存器(DMA_IFCR)
} DMA_TypeDef;typedef struct
{__IO uint32_t CCR;  //DMA通道x配置寄存器(DMA_CCRx)(x = 1…7)__IO uint32_t CNDTR;//DMA通道x传输数量寄存器(DMA_CNDTRx)(x = 1…7)__IO uint32_t CPAR;//DMA通道x外设地址寄存器(DMA_CPARx)(x = 1…7)__IO uint32_t CMAR;//DMA通道x存储器地址寄存器(DMA_CMARx)(x = 1…7)
} DMA_Channel_TypeDef;
5、外设基地址强转为结构体类型
#define DMA1                ((DMA_TypeDef *) DMA1_BASE)
#define DMA1_Channel1       ((DMA_Channel_TypeDef *) DMA1_Channel1_BASE)
#define DMA1_Channel2       ((DMA_Channel_TypeDef *) DMA1_Channel2_BASE)
#define DMA1_Channel3       ((DMA_Channel_TypeDef *) DMA1_Channel3_BASE)
#define DMA1_Channel4       ((DMA_Channel_TypeDef *) DMA1_Channel4_BASE)
#define DMA1_Channel5       ((DMA_Channel_TypeDef *) DMA1_Channel5_BASE)
#define DMA1_Channel6       ((DMA_Channel_TypeDef *) DMA1_Channel6_BASE)
#define DMA1_Channel7       ((DMA_Channel_TypeDef *) DMA1_Channel7_BASE)
6、外设寄存器的访问
DMA1_Channel1.CCR |= (uint32_t)(1<<4); //DIR:数据传输方向
DMA1_Channel1.CCR |= (uint32_t)(1<<5); //CIRC:循环模式
DMA1_Channel1.CCR &= (uint32_t)(~(1<<6)); //PINC:外设地址增量模式
DMA1_Channel1.CCR |= (uint32_t)(1<<7); //MINC:存储器地址增量模式
DMA1_Channel1.CCR |= (uint32_t)(1<<8)); //PSIZE[1:0]:外设数据宽度
DMA1_Channel1.CCR |= (uint32_t)(1<<9)); 
DMA1_Channel1.CCR |= (uint32_t)(1<<14); //MEM2MEM:存储器到存储器模式 
...
  • 由于很多寄存器都是按位配置,如果不嫌麻烦,可以按照这种形式访问寄存器。
  • 为了访问方便,标准库函数里定义了用户层的DMA配置结构体,然后在通过定义的函数将这些参数组合成最终的uint32_t值写入到DMA寄存器中,更加方便。
7、DMA用户层结构体和寄存器掩码
7.1 DMA用户层结构体的定义
  • 这一层结构体DMA_InitTypeDef实际上已经是底层的上一层了即用户层,定义这个结构体是为了方便用户使用,因为很多寄存器都是按位操作的。

  • 那么如果想将用户层设置的参数传递到寄存器,显然需要写一个函数进行传递,这个函数就是void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct),这个函数里会组合结构体DMA_InitTypeDef的所有成员变量,得到一个可以直接写入到DMA_CCRx寄存器的uint32_t数,然后写入DMA_CCRx寄存器,完成参数的传递和写入。

typedef struct
{uint32_t DMA_PeripheralBaseAddr; /*!< Specifies the peripheral base address for DMAy Channelx. */uint32_t DMA_MemoryBaseAddr;     /*!< Specifies the memory base address for DMAy Channelx. */uint32_t DMA_DIR;                /*!< Specifies if the peripheral is the source or destination.This parameter can be a value of @ref DMA_data_transfer_direction */uint32_t DMA_BufferSize;         /*!< Specifies the buffer size, in data unit, of the specified Channel. The data unit is equal to the configuration set in DMA_PeripheralDataSizeor DMA_MemoryDataSize members depending in the transfer direction. */uint32_t DMA_PeripheralInc;      /*!< Specifies whether the Peripheral address register is incremented or not.This parameter can be a value of @ref DMA_peripheral_incremented_mode */uint32_t DMA_MemoryInc;          /*!< Specifies whether the memory address register is incremented or not.This parameter can be a value of @ref DMA_memory_incremented_mode */uint32_t DMA_PeripheralDataSize; /*!< Specifies the Peripheral data width.This parameter can be a value of @ref DMA_peripheral_data_size */uint32_t DMA_MemoryDataSize;     /*!< Specifies the Memory data width.This parameter can be a value of @ref DMA_memory_data_size */uint32_t DMA_Mode;               /*!< Specifies the operation mode of the DMAy Channelx.This parameter can be a value of @ref DMA_circular_normal_mode.@note: The circular buffer mode cannot be used if the memory-to-memorydata transfer is configured on the selected Channel */uint32_t DMA_Priority;           /*!< Specifies the software priority for the DMAy Channelx.This parameter can be a value of @ref DMA_priority_level */uint32_t DMA_M2M;                /*!< Specifies if the DMAy Channelx will be used in memory-to-memory transfer.This parameter can be a value of @ref DMA_memory_to_memory */
}DMA_InitTypeDef;
  • 可以先定一个一个DMA_InitTypeDef类型的结构体变量,即
    DMA_InitTypeDef ST_DMA_Init;
  • 然后这个结构体传给DMA_StructInit()函数,即
    DMA_StructInit(&ST_DMA_Init);
    通过此函数设置结构体的值即DMA参数配置,然后还要想办法把配置好的参数即结构体变量写入到DMA寄存器中。

void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct)
{
/*-------------- Reset DMA init structure parameters values ------------------*//* Initialize the DMA_PeripheralBaseAddr member */DMA_InitStruct->DMA_PeripheralBaseAddr = 0;/* Initialize the DMA_MemoryBaseAddr member */DMA_InitStruct->DMA_MemoryBaseAddr = 0;/* Initialize the DMA_DIR member */DMA_InitStruct->DMA_DIR = DMA_DIR_PeripheralSRC;/* Initialize the DMA_BufferSize member */DMA_InitStruct->DMA_BufferSize = 0;/* Initialize the DMA_PeripheralInc member */DMA_InitStruct->DMA_PeripheralInc = DMA_PeripheralInc_Disable;/* Initialize the DMA_MemoryInc member */DMA_InitStruct->DMA_MemoryInc = DMA_MemoryInc_Disable;/* Initialize the DMA_PeripheralDataSize member */DMA_InitStruct->DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;/* Initialize the DMA_MemoryDataSize member */DMA_InitStruct->DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;/* Initialize the DMA_Mode member */DMA_InitStruct->DMA_Mode = DMA_Mode_Normal;/* Initialize the DMA_Priority member */DMA_InitStruct->DMA_Priority = DMA_Priority_Low;/* Initialize the DMA_M2M member */DMA_InitStruct->DMA_M2M = DMA_M2M_Disable;
}
  • 以上赋值号的右边是用户定义的宏定义,表示操作掩码或者目标值。
7.2 DMA用户层结构体的值传递给DMA寄存器
/*** @brief  Initializes the DMAy Channelx according to the specified*         parameters in the DMA_InitStruct.* @param  DMAy_Channelx: where y can be 1 or 2 to select the DMA and *   x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel.* @param  DMA_InitStruct: pointer to a DMA_InitTypeDef structure that*         contains the configuration information for the specified DMA Channel.* @retval None*/
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
{uint32_t tmpreg = 0;/* Check the parameters */assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx));assert_param(IS_DMA_DIR(DMA_InitStruct->DMA_DIR));assert_param(IS_DMA_BUFFER_SIZE(DMA_InitStruct->DMA_BufferSize));assert_param(IS_DMA_PERIPHERAL_INC_STATE(DMA_InitStruct->DMA_PeripheralInc));assert_param(IS_DMA_MEMORY_INC_STATE(DMA_InitStruct->DMA_MemoryInc));   assert_param(IS_DMA_PERIPHERAL_DATA_SIZE(DMA_InitStruct->DMA_PeripheralDataSize));assert_param(IS_DMA_MEMORY_DATA_SIZE(DMA_InitStruct->DMA_MemoryDataSize));assert_param(IS_DMA_MODE(DMA_InitStruct->DMA_Mode));assert_param(IS_DMA_PRIORITY(DMA_InitStruct->DMA_Priority));assert_param(IS_DMA_M2M_STATE(DMA_InitStruct->DMA_M2M));/*--------------------------- DMAy Channelx CCR Configuration -----------------*//* Get the DMAy_Channelx CCR value */tmpreg = DMAy_Channelx->CCR; /* Clear MEM2MEM, PL, MSIZE, PSIZE, MINC, PINC, CIRC and DIR bits */tmpreg &= CCR_CLEAR_Mask;/* Configure DMAy Channelx: data transfer, data size, priority level and mode *//* Set DIR bit according to DMA_DIR value *//* Set CIRC bit according to DMA_Mode value *//* Set PINC bit according to DMA_PeripheralInc value *//* Set MINC bit according to DMA_MemoryInc value *//* Set PSIZE bits according to DMA_PeripheralDataSize value *//* Set MSIZE bits according to DMA_MemoryDataSize value *//* Set PL bits according to DMA_Priority value *//* Set the MEM2MEM bit according to DMA_M2M value */tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode |DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc |DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize |DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M; //按位组合参数得到最终参数/* Write to DMAy Channelx CCR */DMAy_Channelx->CCR = tmpreg;/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*//* Write to DMAy Channelx CNDTR */DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;/*--------------------------- DMAy Channelx CPAR Configuration ----------------*//* Write to DMAy Channelx CPAR */DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr;/*--------------------------- DMAy Channelx CMAR Configuration ----------------*//* Write to DMAy Channelx CMAR */DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr;
}
  • 用这个函数void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)传递上面设置的结构体参数,由于这个寄存器都是按位设置,因此上面结构体参数实际上要按位组合后才能得到完整的寄存器数据,也就是在这个函数里进行了组合,组合的代码语句:
tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode |DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc |DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize |DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M; //按位组合参数得到最终参数
  • 然后将组合结果赋值给寄存器DMA通道x配置寄存器(DMA_CCRx)(x = 1…7)
  /* Write to DMAy Channelx CCR */DMAy_Channelx->CCR = tmpreg;
7.3 外设寄存器掩码

(1)DMA通道x配置寄存器(DMA_CCRx)(x = 1…7)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/** @defgroup DMA_data_transfer_direction * @{*/
#define DMA_DIR_PeripheralDST              ((uint32_t)0x00000010)
#define DMA_DIR_PeripheralSRC              ((uint32_t)0x00000000)
  • DIR:数据传输方向,对应bit4,如果将bit41,等价于
    #define DMA_DIR_PeripheralDST ((uint32_t)(1<<4))
/** @defgroup DMA_peripheral_incremented_mode * @{*/
#define DMA_PeripheralInc_Enable           ((uint32_t)0x00000040)
#define DMA_PeripheralInc_Disable          ((uint32_t)0x00000000)
  • PINC:外设地址增量模式 (Peripheral increment mode),对应bit6,如果将bit61,等价于
    #define DMA_PeripheralInc_Enable ((uint32_t)(1<<6))
/** @defgroup DMA_memory_incremented_mode * @{*/
#define DMA_MemoryInc_Enable               ((uint32_t)0x00000080)
#define DMA_MemoryInc_Disable              ((uint32_t)0x00000000)
  • MINC:存储器地址增量模式 (Memory increment mode),对应bit7,如果将bit71,等价于
    #define DMA_MemoryInc_Enable ((uint32_t)(1<<7))
/** @defgroup DMA_peripheral_data_size * @{*/
#define DMA_PeripheralDataSize_Byte        ((uint32_t)0x00000000)
#define DMA_PeripheralDataSize_HalfWord    ((uint32_t)0x00000100)
#define DMA_PeripheralDataSize_Word        ((uint32_t)0x00000200)
  • PSIZE[1:0]:外设数据宽度 (Peripheral size),对应bit8-9,如果将bit8-9置位,这些位由软件设置和清除:
    00:8位
    01:16位
    10:32位
    11:保留
/** @defgroup DMA_memory_data_size * @{*/
#define DMA_MemoryDataSize_Byte            ((uint32_t)0x00000000)
#define DMA_MemoryDataSize_HalfWord        ((uint32_t)0x00000400)
#define DMA_MemoryDataSize_Word            ((uint32_t)0x00000800)
  • MSIZE[1:0]:存储器数据宽度 (Memory size),对应bit10-11,如果将bit10-11置位,这些位由软件设置和清除:
    00:8位
    01:16位
    10:32位
    11:保留
/** @defgroup DMA_circular_normal_mode * @{*/
#define DMA_Mode_Circular                  ((uint32_t)0x00000020)
#define DMA_Mode_Normal                    ((uint32_t)0x00000000)
  • CIRC:循环模式 (Circular mode),对应bit5,如果将bit51,等价于
    #define DMA_Mode_Circular ((uint32_t)(1<<5))
/** @defgroup DMA_priority_level * @{*/
#define DMA_Priority_VeryHigh              ((uint32_t)0x00003000)
#define DMA_Priority_High                  ((uint32_t)0x00002000)
#define DMA_Priority_Medium                ((uint32_t)0x00001000)
#define DMA_Priority_Low                   ((uint32_t)0x00000000)
  • PL[1:0]:通道优先级 (Channel priority level),对应bit12-13,如果将bit12-13置位,这些位由软件设置和清除:
    00:低
    01:中
    10:高
    11:最高
/** @defgroup DMA_memory_to_memory * @{*/
#define DMA_M2M_Enable                     ((uint32_t)0x00004000)
#define DMA_M2M_Disable                    ((uint32_t)0x00000000)
  • MEM2MEM:存储器到存储器模式 (Memory to memory mode),对应bit14,如果将bit141,等价于
    #define DMA_Mode_Circular ((uint32_t)(1<<14))
    (2)DMA通道x其他配置寄存器
    在这里插入图片描述
    在这里插入图片描述
    这部分都是按字节访问,也就无需定义用户结构体(只不过是把要设置的值都放到了用户结构体里,统一管理比较方便,而不是像CCR那样按位拆分后再组合成一个最终的uint32_t),而是直接调用底层的DMA结构体进行访问即可
/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*//* Write to DMAy Channelx CNDTR */DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;/*--------------------------- DMAy Channelx CPAR Configuration ----------------*//* Write to DMAy Channelx CPAR */DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr;/*--------------------------- DMAy Channelx CMAR Configuration ----------------*//* Write to DMAy Channelx CMAR */DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr;

三、总结

要分层写程序
  • 底层写一层结构体,确保能够直接访问寄存器
  • 用户层写一层结构体,只是为了方便使用和配置参数(尤其是按位使用的寄存器),最终还是要落到底层函数的调用和寄存器赋值
http://www.xdnf.cn/news/928603.html

相关文章:

  • 6.7-leetcodeT3170
  • 低成本嵌入式Linux开发方案:RV1106入门
  • 代码注释类型
  • 【win | 自动更新关闭】win11
  • 解决使用nvm安装node报错或者安装后有node没有npm
  • 基于投影寻踪博弈论-云模型的综合评价
  • 设计一套流程引擎队列分发器
  • 2025年AI编程工具推荐
  • 外部排序全解析:从基础到优化策略(王道)
  • go工具库:hertz api框架 hertz client的使用
  • 无线网络扫描与分析工具 LizardSystems Wi-Fi Scanner 25.05
  • 【python深度学习】Day 47 注意力热图可视化
  • 蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析
  • transformers 的Trainer的用法
  • Cloudflare 免费域名邮箱 支持 Catch-all 无限别名收件
  • JAVA理论第四战-线程池
  • 【AI论文】反思、重试、奖励:通过强化学习实现大型语言模型的自我提升
  • archlinux中使用 Emoji 字体
  • keil 5打开编译keil 4解决方案,兼容exe查找下载
  • 编程关键字
  • 【区块链基础】区块链的 Fork(分叉)深度解析:原理、类型、历史案例及共识机制的影响
  • 分类与扩展
  • 【推荐算法】推荐算法演进史:从协同过滤到深度强化学习
  • 「Java基本语法」代码格式与注释规范
  • 第二十七课:手搓梯度提升树
  • AI掘金时代:探讨如何用价值杠杆撬动付费用户增长
  • 记录下three.js学习过程中不理解问题①
  • 测试(面经 八股)
  • 《真假信号》速读笔记
  • Python爬虫实战:研究Unirest库相关技术