初学者学习笔记,错误不足望指正。
上一篇:搭建开发环境,建立工程模板 https://blog.csdn.net/qq_37241109/article/details/104098239
本篇任务:
1.完成第一个实例,用库函数操作流水灯
2.挖掘代码
一、用库函数操作流水灯
在User文件夹下新建文件夹Led。
在Led文件夹中新建main.c,bsp_led.c,bsp_led.h这三个文件。
打开工程,将main.c,bsp_led.c,bsp_led.h添加到工程的USER组中。
通过各种途径,我们不难模仿写出代码或者找出现成代码。我综合书上和教学视频上写出一个版本。
ps:发光二极管接GPIOB端口8,9,10。
bsp_led.h
bsp_led.c
main.c
二、挖掘代码
由于从应用角度学习,我并没有足够扎实的基础。所以对于代码理解并不一定清晰,所以我打算整理一下程序思路。
2.1 寄存器初识
1.时钟源
STM32 F103 时钟树详解STM32 F103 时钟树详解_我是唐的博客-CSDN博客_stm32f103时钟树
知道普通I/O端口(PA~PE)连接在APB2设备上,需要初始化APB2的时钟,即时钟控制(RCC)d的对应使能寄存器。
例如:开启PB口时钟的寄存器的代码为:
RCC->APB2ENR |=(1<<3); //将1左移3位
PS:没有深入讨论,时钟不是本例了解的重点内容。
2.I/O端口寄存器
每个I/O端口有7个寄存器控制,分别是:
两个32位配置寄存器(端口配置低寄存器,端口配置高寄存器):GPIOx_CRL,GPIOx_CRH
两个32位数据寄存器(端口输入数据寄存器,端口输出数据寄存器):GPIOx_IDR,GPIOx_ODR
一个32位置位/复位寄存器:GPIOx_BSRR
一个16位复位寄存器:GPIOx_BRR
一个32位锁位寄存器:GPIOx_LCKR
与本例相关的寄存器为端口配置高寄存器GPIOx_CRH(端口配置低寄存器0~7,端口配置高寄存器8~15),端口输出寄存器GPIOx_ODR。由于是库函数操作所以或许还有GPIOx_BSRR,GPIOx_BRR,GPIOx_LCKR,我们并不清楚。针对寄存器的操作封装在库函数之中,但是我们还是需要了解一下。
例如,选择通用推挽输出模式,设置熟读为2MHz,代码为:
GPIOB->CRH = 0x2222222222;
例如,PB端口连接都是LED小灯,控制LED灯亮灭的代码为:
GPIOB->ODR = 0x0000;//灭
GPIOB->ODR = 0xFFFF;//亮
2.2 代码分析
从main.c文件入手。
1.头文件引用
查看stm32f10x.h这个头文件,通过阅读简介我们可以大概了解到它包含……所有外设寄存器的定义,位定义和存储器映射……该文件是应用程序程序员在C源代码(通常在main.c中)中使用的唯一包含文件。
借用野火教学视频里老师的说法“上帝之手”,很厉害就对了。这个头文件从上至下大概看一眼,发现很多结构体定义以及很多看不懂的内容,先不细究。
2.延时函数的声明、定义及调用
延时函数的作用应该不用细说,这里关注一下数据类型_IO uint32_t,uint32_t为无符号整型。
volatile为关键字。volatile的作用是告知编译器,它修饰的变量随时都可能被改变,因此,编译后的程序每次在使用该变量的值时,都会从变量的地址中读取数据,而不是从寄存器中获取。
3.端口初始化函数(LED_GPIO_Config())的调用
LED_GPIO_Config()的定义在bsp_led.c文件中,打开这个文件
首先了解GPIO_InitTypeDef类型的结构体
三个成员类型:
分别为无符号短整型,两个枚举类型。
再看程序中对于GPIO_Pin,GPIO_Speed,GPIO_Mode的赋值。
这是程序中GPIO_Pin_8,GPIO_Pin_9,GPIO_Pin_10的宏定义。他们对应的值应该是对应端口配置寄存器中的一位的赋值。另外,端口速度选择50MHz,模式是通用推挽输出。
将赋值好的结构体写入寄存器进行端口初始化。
GPIO_Init()函数定义如下:
/**
* @brief Initializes the GPIOx peripheral according to the specified
* parameters in the GPIO_InitStruct.
* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.
* @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that
* contains the configuration information for the specified GPIO peripheral.
* @retval None
*/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
uint32_t tmpreg = 0x00, pinmask = 0x00;
/* Check the parameters */
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 Configuration -----------------------*/
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 CRL Configuration ------------------------*/
/* Configure the eight low port pins */
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 CRH Configuration ------------------------*/
/* Configure the eight high port pins */
if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
{
tmpreg = GPIOx->CRH;
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
pos = (((uint32_t)0x01) << (pinpos + 0x08));
/* Get the port pins position */
currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
if (currentpin == pos)
{
pos = pinpos << 2;
/* Clear the corresponding high 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 + 0x08));
}
/* Set the corresponding ODR bit */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
}
}
GPIOx->CRH = tmpreg;
}
}
这个程序不详细分析。
接下来分析时钟。
形参中RCC_APB2Periph_GPIOB、RCC_APB2Periph_GPIOC、ENABLE宏定义如下:
RCC_APB2PeriphClockCmd( )函数定义如下:
自此,寄存器初始化配置完成,我们开启PB,PC口时钟,控制PB端口8,PC端口9、10有效,端口速度50MHz,输出模式为通用推挽输出。
4.端口引脚控制函数(LEDx())
main函数中LED1,LED2,LED3函数的函数定义在bsp_led.h 中
关注函数GPIO_SetBits(),GPIO_ResetBits()
重点是两个赋值语句中GPIOx->BSRR,GPIOx->BRR。BRR和BSRR的最显著用处就是可以只改变某一个或某几个针脚的值而不改变其他,即针对于位。
STM32 通用输入输出端口GPIO BRR、BSRR、ODR寄存器详解STM32 通用输入输出端口GPIO BRR、BSRR、ODR寄存器详解_alanzjl的博客-CSDN博客_odr寄存器
三、总结
选择用库函数操作可以在某种程度上简化代码,提高编程效率,作为初学者还是要力求深究原理。不得不赞叹,封装成结构体是一个很聪明的做法。