SPI初始化的寄存器操作

SPI初始化的寄存器操作 ST官方封装的标准库函数操作SPI初始化通常如下操作先定义结构体给结构体变量赋值然后再调用SPI初始化函数选定SPI2以及SPI结构体参数进行初始化。进入官方封装的SPI初始化函数可以看到函数内首先对SPI结构体参数的值进行了校验是否符合SPI初始换参数内容随后用中间变量取出的SPI_CR寄存器的值取出值后用掩码CR1_CLEAR_Mask对SPI_CR寄存器的值进行一次寄存器值初始化CR1_CLEAR_Mask对应的宏定义其值为0x3040转化为对应的CR1寄存器值则为0011 0000 0100 0000清除了CR1寄存器中的配置值只保留的I2S的两位值和SPE这一位使能SPI的值是因为标准库的设计假设是在初始化是SPI尚未使能因此可以保留当前SPE这一位的数值。清除了寄存器值后采用|从结构体变量中取出每一位对应的值赋值给中间变量tmpreg。随后对SPI的CFGR寄存器进行按位与SPI_Mode_Select是为了将模式选择位SPI模式排除I2S模式干扰。最后给SPI的CRC校验设了一个值。通常我们在调用完SPI_Init函数后会调用一个SPI_Cmd来开启SPI功能在SPI_Cmd中则是按位或的形式对SPI_CR寄存器的第六位也就是SPE位赋1即开启了SPI的使能位至此算完成了一次SPI的初始化与使能。本文为编写一个直接操作寄存器操作初始化SPI2外设的函数由前叙可知SPI初始化过程就是操作了SPI_CR1和SPI_I2SCFGR以及CRCPR的寄存器的值因此我们在编写SPI初始化函数可以直接对这三个寄存器进行赋值。选用同前文使用标准库进行初始化相同的SPI初始化参数SPI_Direction即SPI数据方向选择双向全双工SPI_Mode即SPI模式选择主机模式SPI_DataSize即SPI数据位宽选择8位宽SPI_CPOL即时钟极性选择空闲时保持高电平SPI_CPHA即SPI时钟相位选择第一个时钟边沿开始工作SPI_NSS为SPI的片选引脚管理选择软件模式模式SPI_BaudRatePrescaler为SPI波特率预分频这里我选用SPI2是连接在APB1总线上的选择了64分频因此工作频率为APB1/64得到对应波特率。SPI_FirstBit为数据传输位选择了MSB高位先行的传输模式。由此对应的直接写寄存器操作代码如下。/*开SPI2时钟*/ RCC-APB1ENR | ((uint32_t)0x00004000); /*配置SPI2_CR1、CRCPR寄存器参数*/ SPI2-CR1 | ((uint32_t)0x00000028); //对应BR5:3的101为APB1总线频率的64分频 SPI2-CR1 | ((uint32_t)0x00000000); //设置CPHA为0对应数据从第一个时钟边沿开始采样 SPI2-CR1 | ((uint32_t)0x00000000); //设置CPOL为1对应空闲状态下SCK保持低电平 SPI2-CRCPR | ((uint32_t)0x00000007); //设置CRCPR寄存器值为默认值7不使用CRC校验 SPI2-CR1 | ((uint32_t)0x00000000); //设置DFF数据帧格式位为0使用8位数据帧格式发送和接收 SPI2-CR1 | ((uint32_t)0x00000000); //设置BIDIMODE和BIDIOE位为0选用全双工模式 SPI2-CR1 | ((uint32_t)0x00000000); //设置LSBFIRST为0选用MSB数据高位先行 SPI2-CR1 | ((uint32_t)0x00000104); //设置SSI和MSTR位为1使用主机模式软件NSS模式 SPI2-CR1 | ((uint32_t)0x00000200); //设置SSM为1选择软件设置NNS引脚电平 /*使能SPI2*/ SPI2-CR1 | ((uint32_t)0x00000040); //设置SPI2_CR1的SPE位为1开启SPI设备接下来为GPIO初始化部分常用库函数为结构体传参初始化如下GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_12 ; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure);定义结构体变量赋值后将结构体交由GPIO_init进行初始化。进入初始化函数可以看到uint32_t currentmode 0x00, currentpin 0x00, pinpos 0x00, pos 0x00; uint32_t tmpreg 0x00, pinmask 0x00;首先定义了六个变量分别是currentmode、currentpin、pinpos、pos、tmpreg、pinmask用于存放临时参数。接下来是对于结构体传入参数进行检查是否符合GPIO初始化定义的参数assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); assert_param(IS_GPIO_MODE(GPIO_InitStruct-GPIO_Mode)); assert_param(IS_GPIO_PIN(GPIO_InitStruct-GPIO_Pin));检查参数合法后先是取出了GPIO的MODE参数按位与0X0F先取出了低四位的参数这是八位数据中输出模式下第四位为1则代表着输出模式需要配置GPIO_SPEED参数所以在随后检查了GPIO_MODE参数第四位按位与0X10后判断是否为真如果为真则确认SPEED参数合法性并将SPEED参数赋值给currentmode参数。currentmode ((uint32_t)GPIO_InitStruct-GPIO_Mode) ((uint32_t)0x0F); if ((((uint32_t)GPIO_InitStruct-GPIO_Mode) ((uint32_t)0x10)) ! 0x00) { /* Check the parameters */ assert_param(IS_GPIO_SPEED(GPIO_InitStruct-GPIO_Speed)); /* Output mode */ currentmode | (uint32_t)GPIO_InitStruct-GPIO_Speed; }最后是对GPIO_CR寄存器参数配置这里以CRL寄存器举例由数据手册可知CR寄存器主要是配置GPIO的端口模式、输入输出速度每个端口均有16个GPIO引脚每一个GPIO引脚参数需要2个2位2进制参数配置分别为CNF和MODE。由此一个32位寄存器只能配置8个引脚所以GPIO部分就分为了CRH和CRL的两个寄存器。继续对照标准库的GPIO初始化函数部分首先按位与0X00FF的32位数取出了低16位的引脚值随后由中间变量保存了此前GPIO_CRL上的参数确保不会被覆盖掉。if (((uint32_t)GPIO_InitStruct-GPIO_Pin ((uint32_t)0x00FF)) ! 0x00) { tmpreg GPIOx-CRL; for (pinpos 0x00; pinpos 0x08; pinpos) { pos ((uint32_t)0x01) pinpos; /* Get the port pins position */ currentpin (GPIO_InitStruct-GPIO_Pin) pos; if (currentpin pos) { pos pinpos 2; /* Clear the corresponding low control register bits */ pinmask ((uint32_t)0x0F) pos; tmpreg ~pinmask; /* Write the mode configuration in the corresponding bits */ tmpreg | (currentmode pos); /* Reset the corresponding ODR bit */ if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPD) { GPIOx-BRR (((uint32_t)0x01) pinpos); } else { /* Set the corresponding ODR bit */ if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPU) { GPIOx-BSRR (((uint32_t)0x01) pinpos); } } } } GPIOx-CRL tmpreg; }以GPIO_0为例初始化为复用推挽输出。以下为初始化对应的参数#define GPIO_Pin_0 ((uint16_t)0x0001) /*! Pin 0 selected */ typedef enum { GPIO_Speed_10MHz 1, GPIO_Speed_2MHz, GPIO_Speed_50MHz }GPIOSpeed_TypeDef; typedef enum { GPIO_Mode_AIN 0x0, GPIO_Mode_IN_FLOATING 0x04, GPIO_Mode_IPD 0x28, GPIO_Mode_IPU 0x48, GPIO_Mode_Out_OD 0x14, GPIO_Mode_Out_PP 0x10, GPIO_Mode_AF_OD 0x1C, GPIO_Mode_AF_PP 0x18 }GPIOMode_TypeDef;for循环内对PIN的0-7逐个进行遍历用if判断配置每一个位的CR寄存器参数以下是计算寄存器位偏移因为每一个引脚的CR寄存器对应着4bit所以需要对参数进行便宜。用掩码0x0f清除原有配置参数再用|赋值给新的参数pos pinpos 2; /* Clear the corresponding low control register bits */ pinmask ((uint32_t)0x0F) pos; tmpreg ~pinmask; /* Write the mode configuration in the corresponding bits */ tmpreg | (currentmode pos);最后是根据配置的GPIO模式如果是上下拉模式则需要配置GPIO引脚电平操作BSRR和BRR寄存器完成。BSRR为端口位清除和设置寄存器BRR为端口位清除寄存器写入对应值最终操作的都是ODR寄存器上的值从而实现改变GPIO引脚电平。如果为上拉模式则将BRR寄存器对应位值写1则操作对应ODR寄存器位置1。if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPD) { GPIOx-BRR (((uint32_t)0x01) pinpos); } else { /* Set the corresponding ODR bit */ if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPU) { GPIOx-BSRR (((uint32_t)0x01) pinpos); } }至此完成了全部的GPIO引脚配置工作。