- 此文用于记录学习STM32中所学习或者巩固的知识,主要围绕但并不局限于STM32。
- 本次学习并非为了深入了解使用STM32,而是为了接触其它嵌入式芯片打基础,因此学习中偏重于寄存器操作方法的学习,库函数方法依情况而定。
GPIO
General-purpose I/Os
<h6>推挽输出与开漏输出</h6>
推挽输出 (push-pull,TTL/CMOS反相器)
输出驱动能力有限
无法直接 “线与”
开漏输出 (OC门,即集电极开路门)
必须有上拉电阻,否则只能输出低电平
驱动能力可由外接电源调节
可以直接 “线与”
<h6>寄存器声明与定义</h6>
- C语言在给结构体分配内存空间时,会给每个成员依次分配连续的地址空间,而GPIO的七个寄存器在内存映射中也被分配了连续的地址空间。所以只要在结构体内按照内存映射中分配的顺序声明成员,每个寄存器的地址就能够依次一一正确对应。
typedef struct
{
__IO uint32_t CRL;//volatile unsigned int 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;
<h6>volatile的使用</h6>
- 由于IO口的寄存器值随时可能会变化,每次使用时应重新读取,故加上volatile防止编译器对其进行优化。
- 需要使用volatile的情况:
- 变量的值随时可能会变化
- 变量的值会受到其它共享此变量的寄存器的影响
- 位带操作中,编译器并不知道同一个bit有另一个别名区可以影响该比特的值,所以要用volatile使每次都重新读值
<h6>上拉/下拉输入 & 浮空输入</h6>
- 上拉/下拉输入表示IO口内部已经集成了上拉/下拉电阻,外部输入只需要提供上拉/下拉电压即可;USART的RX端采用的即是上拉/下拉输入模式。
- 浮空输入则适合外部有提供上拉/下拉电阻的情况。例如作为按键检测输入端口使用。
时钟和复位 RCC
Reset & Clock Control
<h6>右移操作符</h6>
- Eg:
while( ! (RCC->CR>>17) );//判断RCC_CR第17位是否为 1
- RCC_CR是clock control register,第17位为外部高速时钟就绪标志位HSERDY,18-31位初始默认为0,更保险的写法为:
while( (RCC->CR >> 17) & 0x0001 == 0 );
- 运算操作符只在CPU的寄存器中进行,不影响内存的值。所以对RCC_CR移位并不会影响RCC_CR对应的映射内存中的值。
RCC->CR>>17;//不影响RCC_CR的值
RCC->CR = RCC->CR>>17;//会改变RCC_CR的值
- 第二条语句操作步骤为:
1.将内存中RCC_CR的值复制到CPU寄存器中;
2.在CPU寄存器中右移17位;
3.将CPU寄存器中的值复制回内存中
<h6>锁相环PLL</h6>
-
u_d = u_i - u_o 的相位差,通过相位差来控制VCO频率的快慢,使输出的频率锁定在输入的频率上。假使在反馈线上增加一个N分频器,则 u_d = u_i - u_o/N,那么输出的频率则会锁定在 (N*输入的频率),即实现了N倍频。可用作时钟的倍频。
- STM32中,PLL最大输出时钟为72MHz
串口通信
CMOS输入端不使用时不能够悬空
MAX232电平转换(TTL电平与串口的EIA-RS-232电平特性不匹配)
IC(Intergrated Circuit)芯片 datasheet 的作用:
1.芯片管脚的分配
2.典型应用电路的接法
3.电气特性,适用范围,电压极限等电荷泵:电荷泵可用于升压、产生负压等。典型的电荷泵电路由开关控制部分和电容部分组成,利用电容能够暂时保持电压的特性反复充放电以达到叠加升压的目的,或将正电荷端接地产生负压。因此电路中至少要有2~3个电容(分别起搬运、输出的作用),并且产生的电压并不是很稳定。
电荷泵开关控制部分集成在 IC 中,外围只需要加电容就可以实现。(如max232芯片,开关控制部分集成在max232内部,从datasheet提供的典型应用电路图可看出,外部需连接若干1uF的电容)
二进制小数转十进制:乘积取整法
USART 的波特率寄存器 USART_BRR 中由高到低存储了12位的整数位,4位的小数位。将4位二进制小数转化为十进制时,可以按以下步骤进行:
1.先将小数部分左移4位变成4位整数(相当于乘以16)
2.将4位整数转化成整数的十进制数
3.再用16除十进制整数即可得到十进制的小数stm32f10x_usart.c 中,USART_ClearFlag函数为何清零代码是
USARTx->SR = (uint16_t)~USART_FLAG;
而非
USARTx->SR &= (uint16_t)~USART_FLAG;
-
解答:经过查阅手册,发现USART_SR寄存器如图:
当中仅有9、8、6、5位可写,并且只能写0,因此置零某一位时只要将某位赋值为0,其它位赋值为1并不会影响其它位原本的状态。因此上面两种代码都是正确的。(已调试验证了标记了w0的bit只能写0,写1无效。有些地方标记为 wc,即 wright clear 。rc 同。)
USART_DR由2个寄存器 TDR & RDR 组成。可以进行读写操作,读的是 RDR 的值,写的是 TDR 的值。
转义字符 \r 与 \n 的区别
Mac | Linux | Windows | |
---|---|---|---|
\r | 换行+回到行首 | 回到行首 | / |
\n | / | 换行 | 换行+回到行首 |
- 重定向使printf输出到串口
/*使用 Keil 的 MicroLIB,否则需要考虑 no semihosting 模式*/
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
//
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
USART_SendData(USART2, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
{
}
return ch;
}
中断与事件
- 中断优先级分组
- 抢占式优先级(PreemptionPriority) & 响应优先级(SubPriority)
待解决的问题
- 为什么中断标志位通过写'1'清零?(如 EXTI_PR 寄存器)