stm32f103X(stm32f103RCT6)
方法1:变量定义
0x40028000地址参考2.存储器映像
int main(void){
volatile unsigned int * pointer = (unsigned int *)0x40028000;//* pointer指向寄存器地址,0x40028000指内存,内存里存放着数据,
* pointer = 1; //* pointer解指针对数据数据访问
}
方法2:宏定义节省空间
#define pointer (volatile unsigned int *)0x40028000
int main(void)
{
*pointer = 1;
}
void SystemInit(void)
{
}
2.3 存储器映像[STM32F10x参考手册.pdf]
请参考相应器件的数据手册中的存储器映像图。表1列出了所用STM32F10xxx中内置外设的起 始地址。
表1 寄存器组起始地址
起始地址 |
外设 |
总线 |
0x5000 0000 – 0x5003 FFFF |
USB OTG 全速 |
AHB |
0x4003 0000 – 0x4FFF FFFF |
保留 |
|
0x4002 8000 – 0x4002 9FFF |
以太网 |
|
0x4002 3400 - 0x4002 3FFF |
保留 |
AHB |
0x4002 3000 - 0x4002 33FF |
CRC |
|
0x4002 2000 - 0x4002 23FF |
闪存存储器接口 |
|
0x4002 1400 - 0x4002 1FFF |
保留 |
|
0x4002 1000 - 0x4002 13FF |
复位和时钟控制(RCC) |
|
0x4002 0800 - 0x4002 0FFF |
保留 |
|
0x4002 0400 - 0x4002 07FF |
DMA2 |
|
0x4002 0000 - 0x4002 03FF |
DMA1 |
|
0x4001 8400 - 0x4001 7FFF |
保留 |
|
0x4001 8000 - 0x4001 83FF |
SDIO |
|
0x4001 4000 - 0x4001 7FFF |
保留 |
APB2 |
0x4001 3C00 - 0x4001 3FFF |
ADC3 |
|
0x4001 3800 - 0x4001 3BFF |
USART1 |
|
0x4001 3400 - 0x4001 37FF |
TIM8定时器 |
|
0x4001 3000 - 0x4001 33FF |
SPI1 |
|
0x4001 2C00 - 0x4001 2FFF |
TIM1定时器 |
|
0x4001 2800 - 0x4001 2BFF |
ADC2 |
|
0x4001 2400 - 0x4001 27FF |
ADC1 |
|
0x4001 2000 - 0x4001 23FF |
GPIO端口G |
|
0x4001 2000 - 0x4001 23FF |
GPIO端口F |
|
0x4001 1800 - 0x4001 1BFF |
GPIO端口E |
|
0x4001 1400 - 0x4001 17FF |
GPIO端口D |
|
0x4001 1000 - 0x4001 13FF |
GPIO端口C |
|
0X4001 0C00 - 0x4001 0FFF |
GPIO端口B |
|
0x4001 0800 - 0x4001 0BFF |
GPIO端口A |
|
0x4001 0400 - 0x4001 07FF |
EXTI |
|
0x4001 0000 - 0x4001 03FF |
AFIO |
|
0x4000 7800 - 0x4000FFFF |
保留 |
APB1 |
0x4000 7400 - 0x4000 77FF |
DAC |
|
0x4000 7000 - 0x4000 73FF |
电源控制(PWR) |
|
0x4000 6C00 - 0x4000 6FFF |
后备寄存器(BKP) |
|
0x4000 6800 - 0x4000 6BFF |
bxCAN2 |
|
0x4000 6400 - 0x4000 67FF |
bxCAN1 |
|
0x4000 6000(1) - 0x4000 63FF |
F USB/CAN共享的512字节SRAM |
|
0x4000 5C00 - 0x4000 5FFF |
USB全速设备寄存器 |
|
0x4000 5800 - 0x4000 5BFF |
I2C2 |
|
0x4000 5400 - 0x4000 57FF |
I2C1 |
|
0x4000 5000 - 0x4000 53FF |
UART5 |
|
0x4000 4C00 - 0x4000 4FFF |
UART4 |
|
0x4000 4800 - 0x4000 4BFF |
USART3 |
|
0x4000 4400 - 0x4000 47FF |
USART2 |
|
0x4000 4000 - 0x4000 3FFF |
保留 |
|
0x4000 3C00 - 0x4000 3FFF |
SPI3/I2S3 |
|
0x4000 3800 - 0x4000 3BFF |
SPI2/I2S3 |
|
0x4000 3400 - 0x4000 37FF |
保留 |
|
0x4000 3000 - 0x4000 33FF |
独立看门狗(IWDG) |
|
0x4000 2C00 - 0x4000 2FFF |
窗口看门狗(WWDG) |
|
0x4000 2800 - 0x4000 2BFF |
RTC |
|
0x4000 1800 - 0x4000 27FF |
保留 |
|
0x4000 1400 - 0x4000 17FF |
TIM7定时器 |
|
0x4000 1000 - 0x4000 13FF |
TIM6定时器 |
|
0x4000 0C00 - 0x4000 0FFF |
TIM5定时器 |
|
0x4000 0800 - 0x4000 0BFF |
TIM4定时器 |
|
0x4000 0400 - 0x4000 07FF |
TIM3定时器 |
|
0x4000 0000 - 0x4000 03FF |
TIM2定时器 |
3.0.知道stm32f103RCT6的LED引脚一个PD2一个PA8,以PD2为例
软件开发步骤:
1、使能GPIOB的外设时钟。外设基地址:0x40021000偏移:0×18
2、通过查阅参考手册的GPO章节知道了要配置推挽输出模式。通过端口配置表展示的寄存器来进行配置。
参考 7.3.7 APB2 外设时钟使能寄存器(RCC_APB2ENR)[STM32F10x参考手册.pdf]
偏移地址:0x18
复位值:0x0000 0000
访问:字,半字和字节访问
通常无访问等待周期。但在APB2总线上的外设被访问时,将插入等待状态直到APB2的外设访问结束。
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
18 |
17 |
16 |
保留 |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
保留 |
USART1EN |
保留
|
SPI1EN |
TIM1EN |
ADC2EN |
ADC1EN |
保留 |
IOPEEN |
IOPDEN |
IOPCEN |
IOPBEN |
IOPAEN |
保留
|
AFIOEN |
|
|
rw |
|
rw |
rw |
rw |
rw |
|
rw |
rw |
rw |
rw |
rw |
|
rw |
参考[STM32F10x参考手册.pdf]8.1 GPIO
表17 端口位配置表
配置模式 |
CNF1 |
CNF0 |
MODE1 |
MODE0 |
PxODR 寄存器 |
|
通用输出 |
推挽(Push-Pull) |
0 |
0 |
01 |
0 或 1 |
|
开漏(Open-Drain) |
1 |
10 |
0 或 1 |
|||
复用功能 输出 |
推挽(Push-Pull) |
1 |
0 |
11 |
不使用 |
|
开漏(Open-Drain) |
1 |
见表18 |
不使用 |
|||
输入 |
模拟输入 |
0 |
0 |
0 |
不使用 |
|
浮空输入 |
1 |
不使用 |
||||
下拉输入 |
1 |
0 |
0 |
|||
上拉输入 |
1 |
表18 输出模式位
MODE[1:0] |
意义 |
00 |
保留 |
01 |
最大输出速度为10MHz |
10 |
最大输出速度为2MHz |
11 |
最大输出速度为50MHz |
8.2.1 端口配置低寄存器 端口配置低寄存器(GPIOx_CRL) (x=A..E)[STM32F10x参考手册.pdf]
偏移地址:0x00
复位值:0x4444 4444
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
18 |
17 |
16 |
CNF7[1:0] |
CNF7[1:0] |
CNF6[1:0] |
CNF6[1:0] |
CNF5[1:0] |
CNF5[1:0] |
CNF4[1:0] |
CNF4[1:0] |
||||||||
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
CNF3[1:0] |
CNF3[1:0] |
CNF2[1:0] |
CNF2[1:0] |
CNF1[1:0] |
CNF1[1:0] |
CNF0[1:0] |
CNF0[1:0] |
||||||||
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
位31:30 27:26 23:22 19:18 15:14 11:10 7:6 3:2
|
CNFy[1:0]:端口x配置位(y = 0…7) (Port x configuration bits) 软件通过这些位配置相应的I/O端口,请参考表17端口位配置表。 在输入模式(MODE[1:0]=00): 00:模拟输入模式 01:浮空输入模式(复位后的状态) 10:上拉/下拉输入模式 11:保留 在输出模式(MODE[1:0]>00): 00:通用推挽输出模式 01:通用开漏输出模式 10:复用功能推挽输出模式 11:复用功能开漏输出模式 |
位29:28 25:24 21:20 17:16 13:12 9:8, 5:4 1:0 |
MODEy[1:0]:端口x的模式位(y = 0…7) (Port x mode bits) 软件通过这些位配置相应的I/O端口,请参考表17端口位配置表。 00:输入模式(复位后的状态) 01:输出模式,最大速度10MHz 10:输出模式,最大速度2MHz 11:输出模式,最大速度50MHz |
代码实现:
(2*4)其中2为PD2的第二个引脚 |
8.2.4 端口输出数据寄存器 端口输出数据寄存器(GPIOx_ODR) (x=A..E)[STM32F10x参考手册.pdf]
地址偏移:0Ch
复位值:0x0000 XXXX
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
18 |
17 |
16 |
保留 |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
ODR15 |
ODR14 |
ODR13 |
ODR12 |
ODR11 |
ODR10 |
ODR9 |
ODR8 |
ODR7 |
ODR6 |
ODR5 |
ODR4 |
ODR3 |
ODR2 |
ODR1 |
ODR0 |
r |
r |
r |
r |
r |
r |
r |
r |
r |
r |
r |
r |
r |
r |
r |
r
|
位31:16 |
保留,始终读为0。 |
位15:0 |
IDRy[15:0]:端口输入数据(y = 0…15) (Port input data) 位15:0 这些位为只读并只能以字(16位)的形式读出。读出的值为对应I/O口的状态。 |
配置GPIOE_IDR寄存器得第5位为1. 代码实现:
|
3.3、找到GPIO对应得时钟
3.1 参考 图1 系统结构[STM32F10x参考手册.pdf]
其PD2对应得时钟为APB2
3.2表1 寄存器组起始地址[STM32F10x参考手册.pdf]
0x4002 1000 - 0x4002 13FF |
复位和时钟控制(RCC) |
其基地址为:0x40021000
3.3、打开GPIO对口B和端口E对应得时钟
6.3.7 APB2 外设时钟使能寄存器(RCC_APB2ENR)[STM32F10x参考手册.pdf]
偏移地址:0x18
复位值:0x0000 0000
访问:字,半字和字节访问
通常无访问等待周期。但在APB2总线上的外设被访问时,将插入等待状态直到APB2的外设访问结束。
注: 当外设时钟没有启用时,软件不能读出外设寄存器的数值,返回的数值始终是 0x0 。
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
18 |
17 |
16 |
保留 |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
ADC3 EN |
USART1 EN |
TIM8 EN |
SPI1 EN |
TIM1 EN |
ADC2 EN |
ADC1 EN |
IOPG EN |
IOPF EN |
IOPE EN |
IOPD EN |
IOPC EN |
IOPB EN |
IOPA EN |
保留 |
AFIO EN |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
|
rw |
位31:16 |
保留,始终读为0。 |
位15 |
ADC3EN:ADC3接口时钟使能 (ADC 3 interface clock enable) 由软件置’1’或清’0’ 0:ADC3接口时钟关闭; 1:ADC3接口时钟开启 |
位14 |
USART1EN:USART1时钟使能 (USART1 clock enable) 由软件置’1’或清’0’ 0:USART1时钟关闭; 1:USART1时钟开启。 |
位13 |
TIM8EN:TIM8定时器时钟使能 (TIM8 Timer clock enable) 由软件置’1’或清’0’ 0:TIM8定时器时钟关闭; 1:TIM8定时器时钟开启。 |
位12 |
SPI1EN:SPI1时钟使能 (SPI 1 clock enable) 由软件置’1’或清’0’ 0:SPI1时钟关闭; 1:SPI1时钟开启。 |
位11
|
TIM1EN:TIM1定时器时钟使能 (TIM1 Timer clock enable) 由软件置’1’或清’0’ 0:TIM1定时器时钟关闭; 1:TIM1定时器时钟开启。 |
位10 |
ADC2EN:ADC2接口时钟使能 (ADC 2 interface clock enable) 由软件置’1’或清’0’ 0:ADC2接口时钟关闭; 1:ADC2接口时钟开启。 |
位9 |
ADC1EN:ADC1接口时钟使能 (ADC 1 interface clock enable) 由软件置’1’或清’0’ 0:ADC1接口时钟关闭; 1:ADC1接口时钟开启。 |
位8 |
IOPGEN:IO端口G时钟使能 (I/O port G clock enable) 由软件置’1’或清’0’ 0:IO端口G时钟关闭; 1:IO端口G时钟开启。 |
位7 |
IOPFEN:IO端口F时钟使能 (I/O port F clock enable) 由软件置’1’或清’0’ 0:IO端口F时钟关闭; 1:IO端口F时钟开启 |
位6 |
IOPEEN:IO端口E时钟使能 (I/O port E clock enable) 由软件置’1’或清’0’ 0:IO端口E时钟关闭; 1:IO端口E时钟开启。 |
位5
|
IOPDEN:IO端口D时钟使能 (I/O port D clock enable) 由软件置’1’或清’0’ 0:IO端口D时钟关闭; 1:IO端口D时钟开启。 |
位4 |
IOPCEN:IO端口C时钟使能 (I/O port C clock enable) 由软件置’1’或清’0’ 0:IO端口C时钟关闭; 1:IO端口C时钟开启。 |
位3 |
IOPBEN:IO端口B时钟使能 (I/O port B clock enable) 由软件置’1’或清’0’ 0:IO端口B时钟关闭; 1:IO端口B时钟开启。 |
位2 |
IOPAEN:IO端口A时钟使能 (I/O port A clock enable) 由软件置’1’或清’0’ 0:IO端口A时钟关闭; 1:IO端口A时钟开启。 |
位1 |
保留,始终读为0。 |
位0
|
AFIOEN:辅助功能IO时钟使能 (Alternate function I/O clock enable) 由软件置’1’或清’0’ 0:辅助功能IO时钟关闭; 1:辅助功能IO时钟开启。 |
代码实现: 使能IO端口D:
|
方法1:采用寄存器
代码 "D:\stm32\led\User\main.c"
#define GPIOx_CLK (*(volatile unsigned int *)(0x40021000 + 0x18))
#define GPIOD_Pin2_CRL (*(volatile unsigned int *)(0x40011400 + 0x00)) //参考8.2.1
#define GPIOD_ODR (*(volatile unsigned int *)(0x40011004 + 0x08)) //参考8.2.4
int main(void)
{
GPIOx_CLK |= 1 <<5;
GPIOD_Pin2_CRL |= (1 <<2 * 4);
GPIOD_ODR &= ~(0x01 << 2);
}
连接st-link
点击Dowload或者F8可以上传了
方法2:采用结构体寄存器写代码
typedef struct
{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
}RCC_TypeDef;
GPIOC为例:
GPIOC BASE:地址是0x40011000
CRL偏移0x00地址是0x40011000
CRH偏移0x04地址是0x40011004
也就是说把32位的寄存器CRL、CRH、、LCKR组成一个整体,名字叫做 GPIO_TypeDef,在内存中是连续排列的,假设CRL的地址为0x00,那么CRH的地址为0x04,依次排列下去。
D:\stm32\led_struct\User\stm32f10x.h
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
/* AHB总线基地址 */
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
/*GPIOD外设基地址*/
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
/*RCC外设基地址*/
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
typedef unsigned int uint32_t;
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
}GPIO_TypeDef;
typedef struct
{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
}RCC_TypeDef;
#define GPIOD ((GPIO_TypeDef*)GPIOD_BASE)
#define RCC ((RCC_TypeDef*)RCC_BASE)
D:\stm32\led_struct\User\main.c
#include "stm32f10x.h"
int main(void)
{
RCC->APB2ENR |= 1<<5;
GPIOD->CRL |=(1<<(2 * 4));
GPIOD->ODR &=~(1<<2);
while(1);
}
void SystemInit()
{
}
D:\stm32\led_struct\User\main.c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
int main(void)
{
RCC->APB2ENR |= 1<<5;
GPIOD->CRL |=(1<<(2 * 4));
//GPIOD->ODR &=~(1<<2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
while(1);
}
void SystemInit()
{
}
D:\stm32\led_struct\User\stm32f10x.h
#ifndef __STM32F10X_H
#define __STM32F10X_H
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
/* AHB总线基地址 */
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
/*GPIOD外设基地址*/
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
/*RCC外设基地址*/
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
}GPIO_TypeDef;
typedef struct
{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
}RCC_TypeDef;
#define GPIOD ((GPIO_TypeDef*)GPIOD_BASE)
#define RCC ((RCC_TypeDef*)RCC_BASE)
#endif
D:\stm32\led_struct\User\stm32f10x_gpio.c
#include "stm32f10x_gpio.h"
void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
GPIOx->BSRR |= GPIO_Pin;
}
void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
GPIOx->BRR |= GPIO_Pin;
}
D:\stm32\led_struct\User\stm32f10x_gpio.h
#ifndef __STM32F10X_GPIO_H
#define __STM32F10X_GPIO_H
#include "stm32f10x.h"
#define GPIO_Pin_0 ((uint16_t)0x0001)//二进制:0b0000 0001
#define GPIO_Pin_1 ((uint16_t)0x0002)//二进制:0b0000 0010
#define GPIO_Pin_2 ((uint16_t)0x0004)//二进制:0b0000 0100
#define GPIO_Pin_3 ((uint16_t)0x0008)
#define GPIO_Pin_4 ((uint16_t)0x0010)
#define GPIO_Pin_5 ((uint16_t)0x0020)
#define GPIO_Pin_6 ((uint16_t)0x0040)
#define GPIO_Pin_7 ((uint16_t)0x0080)
#define GPIO_Pin_8 ((uint16_t)0x0100)
#define GPIO_Pin_9 ((uint16_t)0x0200)
#define GPIO_Pin_10 ((uint16_t)0x0400)
#define GPIO_Pin_11 ((uint16_t)0x0800)
#define GPIO_Pin_12 ((uint16_t)0x1000)
#define GPIO_Pin_13 ((uint16_t)0x2000)
#define GPIO_Pin_14 ((uint16_t)0x4000)
#define GPIO_Pin_15 ((uint16_t)0x8000)
#define GPIO_Pin_all ((uint16_t)0xFFFF)
void GPIO_SetBits(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin);
#endif
1.Undefined symbol HAL_NVIC_SetPriority]
解决:加入stm32f1xx_hal_cortex.c库
手动添加
USE_HAL_DRIVER,STM32F103xE
..\Libraries\STM32F1xx_HAL_Driver\Inc
..\Libraries\CMSIS\Include
..\Libraries\CMSIS\Device\ST\STM32F1xx\Include
..\User
把stm32f1xx_hal_conf_template.h复制到User目录下改名stm32f1xx_hal_conf.h,并设置includes paths路径D:\stm32\1project\User
最后截图
1.先下载java
在电脑管家搜索即可
2.下载cubeMX
官网下载:https://www.st.com/zh/development-tools/stm32cubemx.html#get-software
建议选择大的硬盘,后面会下载不少软件占用空间
3.软件的使用
3.1.打开软件
3.2.选access to muc selector
3.3.搜索型号f103rctx
3.4.设置引脚Pinout & Configuration
点击左侧system core→GPIO
led引脚分别:PA8,PD2
下面放大镜搜索这个两个引脚分别设置成功output
3.5.project Manger
project→设置好名字、路径、Toolchain/IDE选择MDK-ARM
code Generator →stm32 cube mcu packages →选择 copy only the necessarily library fules, →Generated files 选择Gnerate peripheral initialization as a pair of '.c/.h' files per peripheral (这样可以不用把文件生成在main.c文件夹)
Advanced Setting → Visibility的√去掉,序号可以上下换
3.6最后点右上角GENERATE CODE 可以生成代码,第一次比较慢,要下载插件,生成结束后点open project自动用keil5软件打开
goio.c的第50行 GPIO_PIN_RESET(点亮)改为GPIO_PIN_SET灯就会熄灭
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
5.3.1 在工程目录新建一个User\LED文件夹,里面新建两个文件如下
"D:\stm32\cube\cubegpio\User\LED\bsp_led.c"
"D:\stm32\cube\cubegpio\User\LED\bsp_led.h"
5.3.2 把bsp_led.c添加main.c同目录下
Include Paths 添加bsp_led.h的文件夹..\User
5.3.3 写代码
和两个文件有密切关系,里面有注释
D:\stm32\cube\cubegpio\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio.c⭐⭐⭐⭐
D:\stm32\cube\cubegpio\Drivers\STM32F1xx_HAL_Driver\Inc\stm32f1xx_hal_gpio.h⭐
只写了PA8的LED灯,PD2的灯没写
D:\stm32\cube\cubegpio\User\LED\bsp_led.h
#ifndef __BSP_LED_H_
#define __BSP_LED_H_
#include "stm32f1xx.h"
# define LED_0_ON do{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8, GPIO_PIN_RESET);}while(0)
# define LED_0_OFF do{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8, GPIO_PIN_SET);}while(0)
# define LED_0_Toggle do{HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);}while(0)
# define LED_1_ON do{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2, GPIO_PIN_RESET);}while(0)
# define LED_1_OFF do{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2, GPIO_PIN_SET);}while(0)
# define LED_1_Toggle do{HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_2);}while(0)
//初始化LED灯对应的GPIO引脚
void LED_GPIO_Init(void);
#endif
D:\stm32\cube\cubegpio\User\LED\bsp_led.c
#include "./LED/bsp_led.h"
void LED_GPIO_Init(void)
{
// led0 PA8
GPIO_InitTypeDef LED_GPIO_Init;
__HAL_RCC_GPIOA_CLK_ENABLE();
LED_GPIO_Init.Mode = GPIO_MODE_OUTPUT_PP ; //推挽式输出
LED_GPIO_Init.Pin = GPIO_PIN_8; //如果两个灯都是一个GPIO可以用|分割如 GPIO_PIN_8| GPIO_PIN_4
LED_GPIO_Init.Pull = GPIO_NOPULL;
LED_GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &LED_GPIO_Init);
// led1 PD2
__HAL_RCC_GPIOD_CLK_ENABLE();
LED_GPIO_Init.Pin = GPIO_PIN_2;
HAL_GPIO_Init(GPIOD, &LED_GPIO_Init);
}
D:\stm32\cube\cubegpio\Core\Src\main.c
只写了行添加的内容,注释了//MX_GPIO_Init();不注释的话gpio.c会运行
#include "./LED/bsp_led.h"
int main(void)
{
/* Initialize all configured peripherals */
//MX_GPIO_Init();
/* USER CODE BEGIN 2 */
LED_GPIO_Init();
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
LED_0_ON;
HAL_Delay(500);
LED_0_OFF;
HAL_Delay(500);
LED_0_Toggle;
HAL_Delay(500);
LED_1_ON;
HAL_Delay(500);
LED_1_OFF;
HAL_Delay(500);
LED_1_Toggle;
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}
}
与LED类似
5.4.1 在工程目录新建一个User\KEY文件夹,里面新建两个文件如下
D:\stm32\cube\cubegpio\User\KEY\bsp_key.h
D:\stm32\cube\cubegpio\User\LED\bsp_led.h
5.4.2 把bsp_led.c添加main.c同目录下
Include Paths 添加bsp_led.h的文件夹..\User
5.4.3 写代码
D:\stm32\cube\cubegpio\User\KEY\bsp_key.c
#include "./KEY/bsp_key.h"
void KEY_GPIO_Init(void)
{
GPIO_InitTypeDef KEY_GPIO_Init;
__HAL_RCC_GPIOC_CLK_ENABLE();
KEY_GPIO_Init.Mode = GPIO_MODE_INPUT; //输入模式
KEY_GPIO_Init.Pin = GPIO_PIN_0;
KEY_GPIO_Init.Pull = GPIO_NOPULL;
KEY_GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &KEY_GPIO_Init);
}
uint8_t Key_Scan(GPIO_TypeDef* GPIOx, uint16_t Gpio_pin)
{ /*判断按键是否按下GPIO_PIN_SET高电平, GPIO_PIN_RESET低电平*/
if(HAL_GPIO_ReadPin(GPIOx, Gpio_pin) == GPIO_PIN_SET)
{
/*按键按下*/
while(HAL_GPIO_ReadPin(GPIOx, Gpio_pin) == GPIO_PIN_SET);
return KEY_ON;
}
else
{
/*按键没有按下*/
return KEY_OFF;
}
}
D:\stm32\cube\cubegpio\User\KEY\bsp_key.h
#ifndef __BSP_KEY_H_
#define __BSP_KEY_H_
#include "stm32f1xx.h"
#define KEY_ON 1
#define KEY_OFF 0
//初始化KEY灯对应的GPIO引脚
void KEY_GPIO_Init(void);
uint8_t Key_Scan(GPIO_TypeDef* GPIOx, uint16_t Gpio_pin); //更通用性
#endif
D:\stm32\cube\cubegpio\Core\Src\main.c
#include "./KEY/bsp_key.h"
int main(void)
{
/* USER CODE BEGIN 2 */
KEY_GPIO_Init();
while (1)
{
/* USER CODE END WHILE */
// led0 PA8
if (Key_Scan(GPIOC, GPIO_PIN_0) == KEY_ON)
{
LED_1_OFF;
}
}
2.4 启动配置 启动配置 [STM32F10x参考手册.pdf]
在STM32F10xxx里,可以通过BOOT[1:0]引脚选择三种不同启动模式。
表6 启动模式
启动模式选择引脚 |
启动模式 |
说明 |
|
BOOT1 |
BOOT0
|
||
X |
0 |
主闪存存储器 |
主闪存存储器被选为启动区域 |
0 |
1 |
系统存储器 |
系统存储器被选为启动区域 |
1 |
1 |
内置SRAM |
内置SRAM被选为启动区域 |
在系统复位后,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存。用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启动模式。
在从待机模式退出时,BOOT引脚的值将被被重新锁存;因此,在待机模式下BOOT引脚应保持为需要的启动配置。在启动延迟之后,CPU从地址0x0000 0000获取堆栈顶的地址,并从启动存储器的0x0000 0004指示的地址开始执行代码。
因为固定的存储器映像,代码区始终从地址0x0000 0000开始(通过ICode和DCode总线访问),而数据区(SRAM)始终从地址0x2000 0000开始(通过系统总线访问)。Cortex-M3的CPU始终从ICode总线获取复位向量,即启动仅适合于从代码区开始(典型地从Flash启动)。STM32F10xxx微控制器实现了一个特殊的机制,系统可以不仅仅从Flash存储器或系统存储器启动,还可以从内置SRAM启动。
根据选定的启动模式,主闪存存储器、系统存储器或SRAM可以按照以下方式访问:
● 从主闪存存储器启动:主闪存存储器被映射到启动空间(0x0000 0000),但仍然能够在它原有的地址(0x0800 0000)访问它,即闪存存储器的内容可以在两个地址区域访问,0x00000000或0x0800 0000。
● 从系统存储器启动:系统存储器被映射到启动空间(0x0000 0000),但仍然能够在它原有的地址(互联型产品原有地址为0x1FFF B000,其它产品原有地址为0x1FFF F000)访问它。
● 从内置SRAM启动:只能在0x2000 0000开始的地址区访问SRAM。注意: 当从内置 SRAM 启动,在应用程序的初始化代码中,必须使用 NVIC 的异常表和偏移寄存器,重新映射向量表至 SRAM 中。
内嵌的自举程序
内嵌的自举程序存放在系统存储区,由ST在生产线上写入,用于通过可用的串行接口对闪存存
储器进行重新编程:
● 对于小容量、中容量和大容量的产品而言,可以通过USART1接口启用自举程序。进一步的细节请查询AN2606。
● 对于互联型产品而言,可以通过以下某个接口启用自举程序:USART1、USART2(重映像的)、CAN2(重映像的)或USB OTG全速接口的设备模式(通过设备固件更新DFU协议)。USART接口依靠内部8MHz振荡器(HSI)运行。只有在外部使用8MHz、14.7456MHz或25MHz时钟(HSE)时,才能使用CAN或USB OTG接口。进一步的细节请查询AN2606。
1、初始化堆栈指针 2、设置PC指针的值 3、设置中断向量表 4、配置系统时钟 5、调用C库函数main初始化堆栈的工作,最终会跳转到我们自己编写的main |
·STM32的复位功能
·系统复位:系统复位将复位除时钟控制寄存器CSR中的复位标志和备份区域中的寄存器以外的所有寄存器为它们的复位数值。
·电源复位:
一电源复位将复位除了备份区域外的所有寄存器。
·后备域复位:
备份区域拥有两个专门的复位,它们只影响备份区域。
·STM32的时钟
·时钟是什么?
一时钟可以简单理解为“心跳”。对于电子器件来说,时钟就是它的心跳。
STM32芯片,会根据程序给定它的时钟节拍来工作。常说的72Mhz
480Mh,就是指STM32的主时钟(系统时钟)频率。STM32芯片就以这样的频率,在芯片内部做着各种器件的同步工作。
打开cubmax
选access to muc selector →选择芯片→选择RCC→Crystal/Ceramic Resonator
→ Clock Configuration→ PLLCLK→ HCLK选择需要的频率,最大72
5.3.1 通用工作条件[STM32F103x 数据手册.pdf]
表10 通用工作条件
符号 |
参数 |
条件 |
最小值 |
最大值 |
单 位 |
fHCLK |
内部AHB时钟频率 |
|
0 |
72 |
MHz |
fPCLK1 |
内部APB1时钟频率 |
|
0 |
36 |
|
fPCLK2 |
内部APB2时钟频率 |
|
0 |
72 |
对STM32上的时钟,具体怎么配置,根据需求决定。
时钟频率选取越高,功耗也会更高。
另一方面要考虑芯片的工作条件,根据芯片运行的工作条件选取时钟频率。
→ 设置好Project manager→General code
生成代码,打开,这自动生成的时钟
D:\stm32\cube\RCCclock\Core\Src\main.c(107, 139行)
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
·中断的优先级
- 中断的优先级分别两种:可编程、不可编程。
- 对于STM32的中断优先级,决定着内核优先相应谁的中断请求
- 小值优先原则,中断优先级数值越小,中断会被优先响应。
- 中断优先级按照优先级分组配置。
·优先级分组
优先级分组 |
抢占优先级 |
子优先级 |
描述 |
NVIC_PriorityGroup_0 |
0 |
0-15 |
主-0bit,子-4bit |
NVIC_PriorityGroup_1 |
0-1 |
0-7 |
主-1bit,子-3bit |
NVIC_PriorityGroup_2 |
0-3 |
0-3 |
主-2bit,子-2bit |
NVIC_PriorityGroup_3 |
0-7 |
0-1 |
主-3bit,子-1bit |
NVIC_PriorityGroup_4 |
0-15 |
0 |
主-4bit,子-0bit |
·通过优先级分组,可以管理中断的响应顺序。只有抢占优先级才有抢占中断权限,发生中断嵌套。例:B中断正在执行,A中断抢占优先级数值比B中断小(A抢占优先级比B高),A中断则抢过B中断的使用权,响应A的中断服务函数,A中断执行完再交回B如果中断抢占优先级相同,不发生抢占行为。如果多个挂起的中断具有相同的抢占优先级,则子优先级高的先行,如果子优先级相同,则RQ编号小的先行。可编程的优先级,通过嵌套向量中断控制器(NVIC)实现。
1、已经有中断在工作。根据抢占优先级决定,新来的中断打不打断原有中断,打断就发生中断嵌套;不打断就挂起等着。
2、中断都在挂起等待的状态,先按抢占优先级排序,抢占优先级高的先行,抢占优先级相同,就按子优先级排序,子优先级高的先行,子优先级还相同,那就按IRQ编号小的先行。
所以:抢占优先级>子优先级>RQ编号。
嵌套向量中断控制器(NVIC)功能
·表格17-3符 CMSIS合标准的NVIC库函数
NVIC库函数 |
描述 |
void NVIC_EnableIRQ(IRQn_Type IRQn) |
使能中断 |
void NVIC_DisableIRQ(IRQn_ Type IRQn) |
失能中断 |
void NVIC_SetPendingIRQ(IRQn_Type IRQn) |
设置中断悬起位 |
void NVIC_ClearPendingIRQ(IRQn Type IRQn) |
清除中断悬起位 |
uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) |
获取悬起中断编号 |
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) |
设置中断优先级 |
uint32_t NVIC_GetPriority(IRQn_Type IRQn) |
获取中断优先级 |
void NVIC_SystemReset(void) |
系统复位 |
·事件的概念
·STM32上许许多多的外设,是通过内部信号来协同工作的。
这个信号,可以理解为事件。比如一个定时器(TM),当我们使用定时器来计数的时候,怎么知道定时器已经计数完毕?这时就往往通过一个“计数完成事件”来告知(系统/用户)计数已经完成。
·有些事件是可见的,有些事件是不可见的。事件的可不可见,具体体现在寄存器里可不可以查到这些事件的标志。那如果我们想使用这些事件的时候,该怎么做?查询事件标志吗?
·在STM32中,绝大多数事件是不可见的,但是事件几乎都与中断功能绑定在一起。所以通常我们会开启中断的功能,来使用相应的事件。
事件→中断 事件→事件
·EXTI扩展中断和事件控制器
·EXTI-扩展中断和事件控制器,是STM32上的一个外设。它可以捕获外部输入线电平变化等等的一些事件。EXTI捕获到了事件后,还可以生成相应的EXTI中断及等等的一些中断。
所以EXT外设可以大致概括为两个功能:
捕获外部输入等事件。
生成EXT中断等中断请求。
EXT主要特性
EXT的主要特性如下:
所有事件输入均可让CPU唤醒以及生成CP中断和/或CPU事件
某些事件输入允许用户唤醒自主运行模式下的D3域,以及针对D3域(即 DMAMUX2)
生成中断
异步事件输入分为2组:
可配置事件(来自能够生成脉冲的或外设的信号),这类事件具有以下特性:
- 可选择的有效触发边沿
- 中断挂起状态寄存器位
- 单独的中断和事件生成屏蔽
- 支持软件触发
- 可配置系统D3域唤醒事件包含一个D3挂起屏蔽和状态寄存器,并且可能包含一个D3中断信号
直接事件(来自其他外设的中断和唤醒源,需要在外设中清除),这类事件具有以下特性
- 固定上升沿有效触发
- EXTI中无中断挂起状态寄存器位(中断挂起状态由生成事件的外设提供)
- 单独的中断和事件生成屏蔽
- 不支持软件触发
- 直接系统D3域唤醒事件包含一个D3挂起屏蔽和状态寄存器,并且可能包含一个D3中断信号
用cubeMX生成外部中断
打开cubeMX→选择Stm32f103RCTx
Pinout View→点击选择一个引脚选择GPIO_EXTI
RCC→Hight Speed Clock选择Crystal/Ceramic Resonator
Clock Configuration→PLLCLK→HSE→HCLK(MHz) 72(最大)
SYS→Debug:Seiral Write
GPIO→选中引脚→GPIO mode: External Interrupt Mode with Rising/Falling edge trigger detection(上升沿和下降沿都检测)
NVIC→Sort by Premption Priority abd Sub Priority 选择√→最下面Exit line数字 interrupt √→Preemption Pirority(12) subPirority(0)
Tool→按上面设置好,GENERATE COODE 如3.5.project Manger
打开工程gpio.c就生成了代码
D:\stm32\cube\EXTI_GPIO\Core\Src\gpio.c(41-61行)
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin : PC3 */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI3_IRQn, 12, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
}
通过EXT线,捕获EXT线事件,并且去生成中断,在中断中,反转LED灯的状态,并且清除EXT中断标志(中断标志要清除,不然就会一直产生中断)。
·常见的 Debug方法
·硬件调试:
一通过LED灯、蜂鸣器等,能使调试人员感知到的器件,利。
用其交互性,进行调试
·打印调试:
使用串口等,能将数据信息发送出来的方式,追踪程序运行状态。
·调试器调试:
如果设备支持硬件内部运行状态追踪,优先选择使用调试器调试。大多数的单片机都支持调试器调试,如TM32,可以使用野火推出的 fireDAP工具调试或ST-ink等调试工具。
连上st-link
点start debug session
调试变量
1.watch
在全局变量定义一个变量比如int i = 0;while中i++;,选中i鼠标右键add"i"to→watch1再按run(全速运行),可以看见i再增加
2.memory
与1类似add"i"tomemory
3.system view window
3.1 core pripherals
3.2 GPIO
通信分类概览
- 串行通讯与并行通讯
- 全双工、半双工及单工通讯
- 同步通讯与异步通讯
- 通讯速率
串行通讯与并行通讯
- 串行通讯是指设备之间通过少量数据信号线(一般是8根以下),地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。
- 并行通讯一般是指使用8、16、32及64根或更多的数据线进行传输的通讯方式。
特性 |
串行通讯 |
并行通讯 |
通讯距离 |
较远 |
较近 |
抗干扰能力 |
较强 |
较弱 |
传输速率 |
较慢 |
较高 |
成本 |
较低 |
较高 |
通讯方式 |
说明 |
全双工 |
在同一时刻,两个设备之间可以同时收发数据 |
半双工 |
两个设备之间可以收发数据,但不能在同一时刻进行 |
单工 |
在任何时刻都只能进行一个方向的通讯,即一个固定为发送设备,另一个固定为接收设备 |
注意
- 1、串行通讯与并行通讯
- 2、全双工、半双工及单工通讯
- 3、同步通讯与异步通讯
- ·以上的三组概念,是可以共存的。
- ·即一个通信方式,可以是上述1、2、3三点中的任意组合。如:一个器件可以具备串行、全双工、同步通讯方式
通讯速率
- 衡量通讯性能的一个非常重要的参数就是通讯速率通常以比特率(Bitrate)来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)
- 比特率容易与“波特率”(Baudrate)混淆。
- 波特率”它表示每秒钟传输了多少个码元,单位是baud/s
- 码元是通讯信号调制的概念,一个码可以由多个二进制位表示。
- 总结:1“波特”可以含由多个bit。当我们把“比特率”和“波特率”等价时,默认一个码元用一个bit表示,即1“波特”=1“比特”。
10.2 通用同步异步收发器(USART)
USART概览
通用异步收发传输器,英文全称 Universal AsynchronousReceiver/Transmitter,简称UART
STM32上 USART的外设可以实现同步传输功能,所以外设名为
USART,比UART多了一个S,即 synchronous(同步)。
·UART器件主要用来产生相关接口的协议信号,如RS232\RS485等串行接口标准规范和总线标准规范,要使用传输数据这些接口,就要按照接口规定的协议信号发送数据。所以UART器件广泛应用于串口通信中,扮演者传输器的角色。
·RS-232协议电平标准对比
- 一般开发板上使用的电平标准与通讯使用的电平标准不同,如TTL标准及RS-232标准。
通讯标准 |
电平标准(发送端) |
5V TTL |
逻辑1:2.4V-5V 逻辑0:0~0.5V |
RS-232 |
逻辑1:-15V-3V 逻辑0:+3V+15V |
- 因为控制器一般使用L电平标准,所以常常会使用MA3232芯片对TTL及RS-232电平的信号进行互相转换。
·协议概念
波特率
本章中主要讲解的是串口异步通讯,异步通讯中由于没有时钟信号(如前面讲解的DB9接口中是没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,图20-6中用虚线分开的每一格就是代表一个码元。常见的波特率为4800、9600、115200等。
通讯的起始和停止信号
串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑0的数据位表示,而数据包的停止信号可由0.、1、1.5或2个逻辑1的数据位表示,只要双方约定一致即可。
·有效数据
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为5、6、7或8位长。
数据校验
在有效数据之后,有一个可选的数据校验位由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd偶校验even)、0校验(space)、1校验(mark)以及无校验(noparity)
cubemx配置
打开cubemx
1.RCC(同上)
sysy(同上)要选SerialWrite
connectivity→USART1→MODE→Asynchronous→NVIC Setting下面(√)
Basic Parameters Baud Rate 115200 Bits/s Word Length 8 Bits (including Parity) Parity None |
2.实现串口自动接收命令
D:\stm32\cube\test\Core\Src\main.c
加入头文件
# include "stdio.h" //需要添加的库
点击stdio.h进入stdio.h搜索fputc定位673行,这个是把字符输入文件流里
/* USER CODE BEGIN 4 */
int fputc(int c, FILE * f)
{
uint8_t ch;
ch = c;
HAL_UART_Transmit(&huart1, &ch, 1, 1000);
}
加上while循环打印hello:最终为:
/* Includes*/
# include "stdio.h" //需要添加的库
int main(void)
{
while (1)
{
/* USER CODE END WHILE */
printf("hello");
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
}
/* USER CODE BEGIN 4 */
int fputc(int c, FILE * f)
{
uint8_t ch;
ch = c;
HAL_UART_Transmit(&huart1, &ch, 1, 1000);
}
Gpio Setting PA9(TX) 和PA10(Rx)接口
用usb转串口可以连接stm32
连线:
tx→rx
rx→tx
打开调试助手软件
串口配置(与cubeMX一致)
端口 | COMx(usb串口连接的端口号) |
波特率 | 115200 |
校验位 | 无 |
数据位 | 8 |
停止位 | 1 |
3.实现发送命令
D:\stm32\cube\test\Core\Src\main.c
/* USER CODE BEGIN PV */
uint8_t rx_buffer[10]; //开辟空间,接受接收到的数据
/* USER CODE BEGIN 4 */
/*D:\stm32\cube\test\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_uart.c 第2184行__weak void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //__weak只要被实现别的地方会失效
{
if(huart->Instance==USART1) //如果串口1产生中断就进行处理
{
//串口1内容发送出去
HAL_UART_Transmit(&huart1, rx_buffer, 1, 1000);
HAL_UART_Receive_IT(&huart1, rx_buffer, 1);//重新使能串口中断
}
}
打开调试助手软件可以发送命令了
一、定义一个ART_HandleTypeDef
结构体句柄
、通过 HAL UART MspInit函数来实
现串口外设的底层初始化
要做什么功能:
1、使能UART外设时钟
2、配置UART使用的引脚模式
3、如果要用中断,就配置中断
4、如果要用DMA,就配置DMA
三、通过前面定义的结构体具备,来配置串口的波特率、数据字长、停止位、奇偶校验位、硬件流控制
D:\stm32\cube\UART\Core\Src\main.c(149行-175行)
DMA的作用
直接存储器访问(DMA)用于在外设与存储器之间以及存储器与存储器之间,提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速移动数据。这样节省的CPU资源可供其它操作使用。
总结:DMA的就是CPU的助手、数据搬运工
DMA外设要点概括
对于DMA这个器件,它的功能就是建立起一个数据传输通道。
数据or存储器 |
👈DMA控制器建立通道👉 |
数据or存储器 |
|
👆 |
|
|
具体的传输细节 |
|
把10.2改成DMA(方式对应10.3.3DMA通道[STM32F103x 数据手册.pdf])
1.接着10.2的工作
connectivity→USART1→DMA Setting
添加两个如下
DMA Request |
Channel |
Direction |
Priority |
USART1_RX |
DMA1 Channel 5 |
Peripheral To Memory |
Low |
USART1_TX |
DMA1 Channel 4 |
Memory To Peripheral |
Low |
把所有的发送和接收改成DMA方式
System core→DMA→MemToMem(内存到内存)→Add
DMA Request |
Channel |
Direction |
Priority |
MEMTOMEM |
DMA1 Channel 2 |
Memory To Memory |
Low |
第一处
HAL_UART_Receive_IT(&huart1, rx_buffer, 1);
改成 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
HAL_UART_Receive_DMA(&huart1, rx_buffer, 1);
第二处:dma没有超时概念,DMA和CPU并行执行所以分开写
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //只要被实现别的地方会失效
{
if(huart->Instance==USART1) //如果串口1产生中断就进行处理
{
//串口1内容发送出去
HAL_UART_Transmit(&huart1, rx_buffer, 1, 1000);
HAL_UART_Receive_IT(&huart1, rx_buffer, 1);//重新使能串口中断
}
}
改成↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //只要被实现别的地方会失效
{
if(huart->Instance==USART1) //如果串口1产生中断就进行处理
{
//串口1内容发送出去
HAL_UART_Transmit_DMA(&huart1, rx_buffer, 1);
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) //只要被实现别的地方会失效
{
if(huart->Instance==USART1) //如果串口1产生中断就进行处理
{
//串口1内容发送出去
HAL_UART_Receive_DMA(&huart1, rx_buffer, 1);//重新使能串口中断
}
}
数据有效性
12C使用SDA信号线来传输数据,使用SCL信号线进行数据同步。
SDA数据线在SCL的每个时钟周期传输一位数据。
SCL为高电平的时候SDA表示的数据有效,即此时的SDA为高电平时表示数据“1”,为低电平时表示数据“0”。
当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平切换,为下一次表示数据做好准备。
当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平切换,为下一次表示数据做好准备。
23.2.1SP特征
3线全双工同步传输
●带或不带第三根双向数据线的双线单工同步传输
●8或16位传输帧格式选择
●主或从操作
●支持多主模式
●8个主模式波特率预分频系数(最大为fcu/2)
●从模式频率(最大为fPCLK/2)
●主模式和从模式的快速通信
●主模式和从模式下均可以由软件或硬件进行NSS管理:主从操作模式的动态改变
●可编程的时钟极性和相位
●可编程的数据顺序,MSB在前或LSB在前
●可触发中断的专用发送和接收标志
SPI总线忙状态标志
●支持可靠通信的硬件CRC
在发送模式下,CRC值可以被作为最后一个字节发送SP接口一在全双工模式中对接收到的最后一个字节自动进行CRC校验
●可触发中断的主模式故障、过载以及CRC错误标志
●支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求
在cubeMX创建
前几步和前面一样
RCC→Crystal/Ceramic Resonator
sys→Serial Write
→ Clock Configuration→ PLLCLK→ HCLK选择需要的频率,最大72
SPI1→ mode: Full-Duplex Master→Hardware NSS Signal Disable(选用软件)
Parameter Settings |
|
Basic Parameters |
|
Frame Format |
Motorola |
Data Size |
8 Bits |
First Bit |
MSB First |
Clock Parameters |
|
Prescaler(for Baud Rate) |
4 |
Baud Rate |
18.0 MBits/s |
Clock Polarity (CPOL) |
Low |
Clock Phase(CPHA) |
1 Edge |
Advanced Parameters |
|
CRC Calculation |
Disabled |
NSS Signal Type |
Software |
.CPOL/CPHA及通讯模式
由CPOL及CPHA的不同状态,SPI分成了四种模式,主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式0”与“模式3”。
SPI模式 |
CPOL |
CPHA |
空闲时SCK时钟 |
采样时刻 |
0 |
0 |
0 |
低电平 |
奇数边沿 |
1 |
0 |
1 |
低电平 |
偶数边沿 |
2 |
1 |
0 |
高电平 |
奇数边沿 |
3 |
1 |
1 |
高电平 |
偶数边沿 |
本文作者: 永生
本文链接: https://yys.zone/detail/?id=197
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
评论列表 (0 条评论)