目录
初识工程文件目录层次
stm32f10x_it.c、stm32f10x_it.h和stm32f10x_conf.h文件作用: 链接
- system_stm32f10x:
包含了STM32 芯片上电后初始化系统时钟、扩展外部存储器用的函数- stm32f10x头文件:片上外设固件库头文件
初始化GPIO
如何使用结构体控制寄存器:
GPIO_Init函数中左边的GPIOC为GPIO_TypeDef类型的指针(地址)变量
GPIO_Init函数中右边的结构体变量是自定义的,成员变量也是库函数中给定的(自己选择合适参数)赋值
通过GPIO_Init函数使寄存器相关的结构体通过自定义的结构体完成初始化
GPIO_Init(GPIOC, &GPIO_InitStructure);
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;
使用#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) 将该结构体首地址赋值为寄存器位置
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
初识库函数
初始化GPIO
步骤:
- 声明GPIO_InitTypeDef类型的结构体
- 初始化结构体变量,赋值
- 开启RCC_APB2PeriphClockCmd时钟
- 将结构体变量传入GPIO_Init函数
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;//定义一个GPIO_InitTypeDef类型的结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//开启GPIO端口时钟
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13; //PC13
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//通用推挽输出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//引脚速率50MHZ
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
typedef struct
{
uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;
注意结构体中成员变量类型
GPIO_Pin类型是uint16_t
在stm32f10x_gpio.h头文件中使用#define来定义的
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected /
#define GPIO_Pin_1 ((uint16_t)0x0002) /!< Pin 1 selected */
……GPIO_Speed类似是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;
Keil软件
STM32F10X_MD,USE_STDPERIPH_DRIVER
在stm32f10x.h
中使用了条件编译
#ifdef USE_STDPERIPH_DRIVER
#include "stm32f10x_conf.h"
#endif
#ifdef STM32F10X_MD
ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */
USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */
USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */
CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */
CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */
EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */
TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */
TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */
TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */
TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */
TIM2_IRQn = 28, /*!< TIM2 global Interrupt */
TIM3_IRQn = 29, /*!< TIM3 global Interrupt */
TIM4_IRQn = 30, /*!< TIM4 global Interrupt */
I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */
I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */
I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */
I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */
SPI1_IRQn = 35, /*!< SPI1 global Interrupt */
SPI2_IRQn = 36, /*!< SPI2 global Interrupt */
USART1_IRQn = 37, /*!< USART1 global Interrupt */
USART2_IRQn = 38, /*!< USART2 global Interrupt */
USART3_IRQn = 39, /*!< USART3 global Interrupt */
EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */
RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */
USBWakeUp_IRQn = 42 /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */
#endif /* STM32F10X_MD */
容量大小选择
断言函数
definition 定义
reference 引用
@arg:argument自变量
@param:parameter参数
填入的自变量可以通过”或“运算符组合使用(”|“)
assert_param
:断言函数,判断所填入的参数是否合法
GPIO_Init(GPIOC, &GPIO_InitStructure);
/**
* @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));
使用库函数操作硬件
USART
串口:链接
串口简介:通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter)是一个串行通信设备, 可以灵活地与外部设备进行全双工数据交换。 有别于USART还有一个UART(Universal Asynchronous Receiver and Transmitter), 它是在USART基础上裁剪掉了同步通信功能,只有异步通信。 简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是UART。
串口使用步骤:
1.串口时钟,GPIO时钟使能
2.GPIO端口模式设置
3.串口参数初始化
4.开启中断并且初始化NVIC(在接受串口数据时使用)
5.使能串口
6.编写中断处理函数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//1.串口时钟,GPIO时钟使能
GPIO_Init(GPIOA, &GPIO_InitStructure);// 2.GPIO端口模式设置
USART_Init(USART1, &USART_InitStructure);//3.串口参数初始化
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_Init(&NVIC_InitStructure);// 4.开启中断并且初始化NVIC(在接受串口数据时使用)
USART_Cmd(USART1, ENABLE);//5.使能串口
void USART1_IRQHandler(void)// 6.编写中断处理函数
串口传输状态相关函数:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG); ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT); void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
关于TXE标志位(Transmit Exchange Empty),TC(Transmission Complete)RXNE(Receive Exchange Not Empty)
TXE为1:TDR里面的数据全部移到了移位寄存器,且没有新的数据进入TDR
TXE为0:TDR里面有数据。
TC为1:从TDR发过来的数据全部移送到TX引脚,且TDR里也没有新的数据
TC为0:从TDR里过来的数据还没有全部移过来,或者之前TDR里的数据被移走了,但TDR里又来了新的数据
时钟
HSE、HSI、LSE、LSI:内外部,高低速时钟
S(Speed)、H(High)、L(Low)、E(External)
PLL的意思是Phase-locked Loop,中⽂意思即为锁相环
STM32时钟讲解:链接
系统时钟配置:
- 在startup_stm32f10x md.s文件中找到要运行的SystemInit函数
- 跳转到了system_stm32f10x.c文件中
3.同system_stm32f10x.c文件中设置系统时钟,是条件编译
在这里定义的宏
使用SYSTICK滴答定时器
使用SYSTICK滴答定时器编写延时函数
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
中断
NVIC是内核外设
core_cm3.h和stm32f10x.h头文件区别
misc.h
STM32最多有256种中断,每个产品不全部拥有所有中断,优先级概念和C51不同,然后对优先级分组:用4bit(16种)抢占优先级>响应优先级
每个工程为NVIC设置一次分组即可
软硬件调试
Keil仿真
开始配置
其中软件仿真在Target->XTal下配置时钟,硬件在Setting下配置
通过数据库中芯片描述来填写软件仿真参数
开始仿真
- 进入软件仿真
- 添加断点
- 全速运行到第一个断点位置
查看GPIO寄存器状态
查看参数波形 (硬件仿真中不支持)
设置一些参数
位带操作
STM32单片机不支持单个位操作
Bit-banding简称位带,有人也叫位段。支持位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。
1Bit膨胀到32Bit(4字节)
位带操作:链接
1.SRAM区的最低1MB(0x2000 0000 — 0x200F FFFF) 映射到(0x2200 0000 — 0x23FF FFFF)。
2.片内外设区的最低1MB(0x4000 0000 — 0x400F FFFF)映射到(0x4200 0000 — 0x43FF FFFF)。
1字 = 4字节
1字节 = 8bit
I2C
PID
一种闭环控制算法(程序)
开环:输入不受输出的影响
PID在线模拟网站
PID讲解
博客园讲解
连续PID:
离散化:
P项: Kp∗ek
误差越大,P的输出越大
P的作用是减小测量值和理论值之间的误差,让测量值不断接近理论值
D项: Kd(ek-ek-1)
两次误差值相差越大,输出的值越大
D的作用是“阻尼”如果系统的误差很大,或者P参数设置的很大,那么P的输出就会很大,导致系统剧烈相应,出现过冲现象,此时就用D来抑制,让系统刚好可以停在理论值而不过程。
I项: K i ∑ j = 1 k e j Ki\sum_{j= 1}^{k}e_j Kij=1∑kej
只要存在误差(这种误差叫稳态误差),不论误差有多小,i的输出也会不断的累积,越来越大.
I的作用是消除稳态误差,当系统误差已经接近0时,P的输出会很小,起不到继续减小误差的作用,导致误差始终没办法减小到0,这时用到I项,让误差不断累加,并将累加的值输出.
I项常用于追求更精准的控制.
程序积累
EC11消抖:链接
A脚设置为上升下降沿均会进中断,下降上升一个变换周期,判断这个周期的A脚,B脚的始末状态,来判断正反转一次。
#define Aio PA3
#define Bio PA4
int flag = 0; //标志位,默认是0,且在中断中和a相是否低电平 相与操作
boolean CW_1 = 0;
boolean CW_2 = 0;
//只看中断代码即可
void Aio_inter()
{
// 只要处理一个脚的外部中断--上升沿&下降沿
int alv = digitalRead(Aio);
int blv = digitalRead(Bio);
if (flag == 0 && alv == LOW) //flag默认是0,a相下降沿时,读取b相点平,且标志位置1
{
CW_1 = blv;
flag = 1;
}//执行完毕,不会进入另一个if判断
if (flag && alv)//EC11继续拧,又进入中断,在a相上升沿时(一个周期了)
{
CW_2 = !blv; //取反是因为 alv,blv必然异步,一高一低。
if (CW_1 && CW_2) {
dir++;
}
if (CW_1 == false && CW_2 == false) {
dir--;
}
flag = 0;
}
}//以上是中断处理函数
void setup(){
Serial.begin(115200);
pinMode(Aio, INPUT);
pinMode(Bio, INPUT);
//只要消耗一个外部中断资源
attachInterrupt(Aio, Aio_inter, CHANGE);
}
//变量dir在中断里处理,loop中随时调用
void loop(){
Serial.printf("dir : %d",dir);
delay(1000);
}