我是靠谱客的博主 美丽狗,这篇文章主要介绍stm32f4外设学习篇(代码集合)1、串口12、串口2(RS485)3、定时器(多路)4、PWM5、窗口看门狗6、独立看门狗7、GPIO(LED)8、外部中断(EXTI)9、随机数发生器10、ADC11、DAC12、DMA13、IIC14、SPI15、24C0216、W25QXX完整工程,现在分享给大家,希望可以做个参考。

stm32f4外设学习篇(代码集合)

  • 1、串口1
  • 2、串口2(RS485)
  • 3、定时器(多路)
  • 4、PWM
  • 5、窗口看门狗
  • 6、独立看门狗
  • 7、GPIO(LED)
  • 8、外部中断(EXTI)
  • 9、随机数发生器
  • 10、ADC
  • 11、DAC
  • 12、DMA
  • 13、IIC
  • 14、SPI
  • 15、24C02
  • 16、W25QXX
  • 完整工程

芯片:STM32F407(理论支持F4所有芯片)
开发环境:KEIL5-ARM
目的:单独的文件除了24c02和W25QXX会需要依赖iic和spi其余文件都是可以直接拷贝到工程使用的
日期:2021-8-12
有一些外设并没有经过测试所以在使用过程中自行调试

1、串口1

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "debug_usart.h" /** * @brief DEBUG_USART GPIO 配置,工作模式配置。115200 8-N-1 * @param 无 * @retval 无 */ void Debug_USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_AHB1PeriphClockCmd(DEBUG_USART_RX_GPIO_CLK | DEBUG_USART_TX_GPIO_CLK, ENABLE); /* 使能 UART 时钟 */ RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE); /* 连接 PXx 到 USARTx_Tx*/ GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT, DEBUG_USART_RX_SOURCE, DEBUG_USART_RX_AF); /* 连接 PXx 到 USARTx__Rx*/ GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT, DEBUG_USART_TX_SOURCE, DEBUG_USART_TX_AF); /* 配置Tx引脚为复用功能 */ GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure); /* 配置Rx引脚为复用功能 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN; GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure); /* 配置串DEBUG_USART 模式 */ USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(DEBUG_USART, &USART_InitStructure); USART_Cmd(DEBUG_USART, ENABLE); } u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. //接收状态 //bit15, 接收完成标志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字节数目 u16 USART_RX_STA = 0; //接收状态标记 void USART1_IRQHandler(void) //串口1中断服务程序 { u8 Res; if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) { Res = USART_ReceiveData(USART1); //(USART1->DR); //读取接收到的数据 if ((USART_RX_STA & 0x8000) == 0) //接收未完成 { if (USART_RX_STA & 0x4000) //接收到了0x0d { if (Res != 0x0a) USART_RX_STA = 0; //接收错误,重新开始 else USART_RX_STA |= 0x8000; //接收完成了 } else //还没收到0X0D { if (Res == 0x0d) USART_RX_STA |= 0x4000; else { USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res; USART_RX_STA++; if (USART_RX_STA > (USART_REC_LEN - 1)) USART_RX_STA = 0; //接收数据错误,重新开始接收 } } } } } //buf:接收缓存首地址 //len:读到的数据长度 void get_uart_buff(uint8_t *buf, uint8_t *len) { u8 rxlen = USART_RX_STA; u8 i = 0; *len = 0; //默认为0 _uart_delay(0xff); //等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束 if (rxlen == USART_RX_STA && rxlen) //接收到了数据,且接收完成了 { for (i = 0; i < rxlen; i++) { buf[i] = USART_RX_BUF[i]; } *len = USART_RX_STA; //记录本次数据长度 USART_RX_STA = 0; //清零 } } //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数 int fputc(int ch, FILE *f) { /* 发送一个字节数据到串口DEBUG_USART */ USART_SendData(DEBUG_USART, (uint8_t)ch); /* 等待发送完毕 */ while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET) ; return (ch); } //重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数 int fgetc(FILE *f) { /* 等待串口输入数据 */ while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_RXNE) == RESET) ; return (int)USART_ReceiveData(DEBUG_USART); } /*********************************************END OF FILE**********************/

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef __DEBUG_USART_H #define __DEBUG_USART_H #include "stm32f4xx.h" #include <stdio.h> //引脚定义 /*******************************************************/ #define DEBUG_USART USART1 #define DEBUG_USART_CLK RCC_APB2Periph_USART1 #define DEBUG_USART_RX_GPIO_PORT GPIOA #define DEBUG_USART_RX_GPIO_CLK RCC_AHB1Periph_GPIOA #define DEBUG_USART_RX_PIN GPIO_Pin_10 #define DEBUG_USART_RX_AF GPIO_AF_USART1 #define DEBUG_USART_RX_SOURCE GPIO_PinSource10 #define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_CLK RCC_AHB1Periph_GPIOA #define DEBUG_USART_TX_PIN GPIO_Pin_9 #define DEBUG_USART_TX_AF GPIO_AF_USART1 #define DEBUG_USART_TX_SOURCE GPIO_PinSource9 #define USART_REC_LEN 200 //定义最大接收字节数 200 extern uint8_t USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 extern uint16_t USART_RX_STA; //接收状态标记 /************************************************************/ // 不精确的延时 static void _uart_delay(__IO u32 nCount) { for (; nCount != 0; nCount--) ; } //串口波特率 #define DEBUG_USART_BAUDRATE 115200 void Debug_USART_Config(void); void get_uart_buff(uint8_t *buf, uint8_t *len); int fputc(int ch, FILE *f); #endif /* __USART1_H */

2、串口2(RS485)

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include "rs485.h" // 配置USART接收中断 static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Configure the NVIC Preemption Priority Bits */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); /* Enable the USARTy Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = _485_INT_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /* * 函数名:_485_Config * 描述 :USART GPIO 配置,工作模式配置 * 输入 :无 * 输出 : 无 * 调用 :外部调用 */ void _485_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; USART_ClockInitTypeDef USART_ClockInitStruct; RCC_AHB1PeriphClockCmd(_485_USART_RX_GPIO_CLK | _485_USART_TX_GPIO_CLK | _485_RE_GPIO_CLK, ENABLE); RCC_APB1PeriphClockCmd(_485_USART_CLK, ENABLE); USART_DeInit(_485_USART); USART_StructInit(&USART_InitStructure); USART_ClockStructInit(&USART_ClockInitStruct); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Pin = _485_USART_TX_PIN; GPIO_Init(_485_USART_TX_GPIO_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(_485_USART_TX_GPIO_PORT, GPIO_PinSource3, GPIO_AF_USART2); GPIO_InitStructure.GPIO_Pin = _485_USART_RX_PIN; GPIO_Init(_485_USART_RX_GPIO_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(_485_USART_RX_GPIO_PORT, GPIO_PinSource2, GPIO_AF_USART2); USART_ClockInit(_485_USART, &USART_ClockInitStruct); USART_InitStructure.USART_BaudRate = _485_USART_BAUDRATE; //波特率设置 USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; //无校验 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(_485_USART, &USART_InitStructure); USART_ITConfig(_485_USART, USART_IT_RXNE, ENABLE); //接收中断使能 USART_ClearITPendingBit(_485_USART, USART_IT_TC); //清除中断TC位 USART_Cmd(_485_USART, ENABLE); //使能串口 USART_ClearFlag(_485_USART, USART_FLAG_TC); /***********************************GPIOA 1,RS485方向控制******************************/ GPIO_InitStructure.GPIO_Pin = _485_RE_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(_485_RE_GPIO_PORT, &GPIO_InitStructure); NVIC_Configuration(); /* 使能串口接收中断 */ USART_ITConfig(_485_USART, USART_IT_RXNE, ENABLE); GPIO_ResetBits(_485_RE_GPIO_PORT, _485_RE_PIN); //默认进入接收模式 } /***************** 发送一个字符 **********************/ //使用单字节数据发送前要使能发送引脚,发送后要使能接收引脚。 void _485_SendByte(uint8_t ch) { /* 发送一个字节数据到USART1 */ USART_SendData(_485_USART, ch); /* 等待发送完毕 */ while (USART_GetFlagStatus(_485_USART, USART_FLAG_TXE) == RESET) ; } /***************** 发送指定长度的字符串 **********************/ void _485_SendStr_length(uint8_t *str, uint32_t strlen) { unsigned int k = 0; _485_TX_EN(); // 使能发送数据 do { _485_SendByte(*(str + k)); k++; } while (k < strlen); _485_delay(0xfff); _485_RX_EN(); // 使能接收数据 } /***************** 发送字符串 **********************/ void _485_SendString(uint8_t *str) { unsigned int k = 0; _485_TX_EN(); // 使能发送数据 do { _485_SendByte(*(str + k)); k++; } while (*(str + k) != ''); _485_delay(0xff); _485_RX_EN(); // 使能接收数据 } //中断缓存串口数据 #define UART_BUFF_SIZE 64 //接收缓存区 u8 RS485_RX_BUF[64]; //接收缓冲,最大64个字节. //接收到的数据长度 u8 RS485_RX_CNT = 0; void bsp_485_IRQHandler(void) { u8 res; if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据 { res = USART_ReceiveData(USART2); //;读取接收到的数据USART2->DR if (RS485_RX_CNT < UART_BUFF_SIZE) { RS485_RX_BUF[RS485_RX_CNT] = res; //记录接收到的值 RS485_RX_CNT = RS485_RX_CNT + 1; //接收数据增加1 if (RS485_RX_CNT >= UART_BUFF_SIZE) { RS485_RX_CNT = 0; } } else { RS485_RX_CNT = 0; RS485_RX_BUF[RS485_RX_CNT] = res; //记录接收到的值 } } } //RS485查询接收到的数据 //buf:接收缓存首地址 //len:读到的数据长度 void RS485_Receive_Data(u8 *buf, u8 *len) { u8 rxlen = RS485_RX_CNT; u8 i = 0; *len = 0; //默认为0 _485_delay(0xff); //等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束 if (rxlen == RS485_RX_CNT && rxlen) //接收到了数据,且接收完成了 { for (i = 0; i < rxlen; i++) { buf[i] = RS485_RX_BUF[i]; } *len = RS485_RX_CNT; //记录本次数据长度 RS485_RX_CNT = 0; //清零 } } // crc16 modbus const uint16_t polynom = 0xA001; //uint16_t ccr_data_con = crc16bitbybit(rs485buf,length-2); //rs485buf[length-1] = ccr_data_con&0XFF; //低八位 //rs485buf[length-2] = ccr_data_con>>8; //高八位 uint16_t crc16bitbybit(uint8_t *ptr, uint16_t len) { uint8_t i; uint16_t crc = 0xffff; uint8_t ch; if (len == 0) { len = 1; } while (len--) { ch = *ptr; crc ^= ch; for (i = 0; i < 8; i++) { if (crc & 1) { crc >>= 1; crc ^= polynom; } else { crc >>= 1; } } ptr++; } return crc; }

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#ifndef __485_H #define __485_H #include "stm32f4xx.h" #include "stdio.h" #define _485_USART USART2 #define _485_USART_CLK RCC_APB1Periph_USART2 #define _485_USART_BAUDRATE 9600 #define _485_USART_RX_GPIO_PORT GPIOA #define _485_USART_RX_GPIO_CLK RCC_AHB1Periph_GPIOA #define _485_USART_RX_PIN GPIO_Pin_2 #define _485_USART_RX_AF GPIO_AF_USART2 #define _485_USART_RX_SOURCE GPIO_PinSource2 #define _485_USART_TX_GPIO_PORT GPIOA #define _485_USART_TX_GPIO_CLK RCC_AHB1Periph_GPIOA #define _485_USART_TX_PIN GPIO_Pin_3 #define _485_USART_TX_AF GPIO_AF_USART2 #define _485_USART_TX_SOURCE GPIO_PinSource3 #define _485_RE_GPIO_PORT GPIOA #define _485_RE_GPIO_CLK RCC_AHB1Periph_GPIOA #define _485_RE_PIN GPIO_Pin_1 #define _485_INT_IRQ USART2_IRQn #define _485_IRQHandler USART2_IRQHandler /// 不精确的延时 static void _485_delay(__IO u32 nCount) { for (; nCount != 0; nCount--) ; } /*控制收发引脚*/ //进入接收模式,必须要有延时等待485处理完数据 #define _485_RX_EN() _485_delay(1000); GPIO_ResetBits(_485_RE_GPIO_PORT, _485_RE_PIN); _485_delay(1000); //进入发送模式,必须要有延时等待485处理完数据 #define _485_TX_EN() _485_delay(1000); GPIO_SetBits(_485_RE_GPIO_PORT, _485_RE_PIN); _485_delay(1000); void _485_Config(void); void _485_SendByte(uint8_t ch); void _485_SendStr_length(uint8_t *str, uint32_t strlen); void _485_SendString(uint8_t *str); void rs485_modbus_print(uint8_t *rs485_write_buff, uint8_t len); uint16_t crc16bitbybit(uint8_t *ptr, uint16_t len); void bsp_485_IRQHandler(void); void RS485_Receive_Data(u8 *buf, u8 *len); #endif /* __485_H */

3、定时器(多路)

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include "timer.h" //arr:自动重装值。 //psc:时钟预分频数 //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us. //Ft=定时器工作频率,单位:Mhz //通用定时器3中断初始化 void TIM3_Int_Init(u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); ///使能TIM3时钟 TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //初始化TIM3 TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //允许定时器3更新中断 TIM_Cmd(TIM3, ENABLE); //使能定时器3 NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //定时器3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } //通用定时器2中断初始化 void TIM2_Int_Init(u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); ///使能TIM3时钟 TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //初始化TIM3 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //允许定时器3更新中断 TIM_Cmd(TIM2, ENABLE); //使能定时器3 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //定时器3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } //通用定时器4中断初始化 void TIM4_Int_Init(u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); ///使能TIM4时钟 TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure); //初始化TIM4 TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //允许定时器4更新中断 TIM_Cmd(TIM4, ENABLE); //使能定时器4 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //定时器4中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x04; //子优先级4 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } //定时器2中断服务函数 void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //溢出中断 { // app程序 } TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志位 } //定时器3中断服务函数 void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //溢出中断 { // app程序 } TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除中断标志位 } //定时器4中断服务函数 void TIM4_IRQHandler(void) { if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET) //溢出中断 { // app程序 } TIM_ClearITPendingBit(TIM4, TIM_IT_Update); //清除中断标志位 }

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef _TIMER_H #define _TIMER_H #include "stm32f4xx.h" void TIM3_Int_Init(uint16_t arr, uint16_t psc); void TIM2_Int_Init(uint16_t arr, uint16_t psc); void TIM4_Int_Init(uint16_t arr, uint16_t psc); #endif

4、PWM

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "pwm.h" //TIM14 PWM部分初始化 //PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 void TIM14_PWM_Init(u32 arr,u32 psc) { //此部分需手动修改IO口设置 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); //TIM14时钟使能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能PORTF时钟 GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM14); //GPIOF9复用为定时器14 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIOF9 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PF9 TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14 //初始化TIM14 Channel1 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低 TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1 TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器 TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能 TIM_Cmd(TIM14, ENABLE); //使能TIM14 }

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
#ifndef _PWM_H #define _PWM_H #include "stm32f4xx.h" //TIM_SetCompare1(TIM14,pwmval);// 可以通过这个函数占空比调节 void TIM14_PWM_Init(u32 arr,u32 psc); #endif

5、窗口看门狗

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include "wwdg.h" //保存WWDG计数器的设置值,默认为最大. uint8_t WWDG_CNT = 0X7F; //初始化窗口看门狗 //tr :T[6:0],计数器值 //wr :W[6:0],窗口值 //fprer:分频系数(WDGTB),仅最低2位有效 //Fwwdg=PCLK1/(4096*2^fprer). 一般PCLK1=42Mhz void WWDG_Init(u8 tr, u8 wr, u32 fprer) { NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //使能窗口看门狗时钟 WWDG_CNT = tr & WWDG_CNT; //初始化WWDG_CNT. WWDG_SetPrescaler(fprer); //设置分频值 WWDG_SetWindowValue(wr); //设置窗口值 // WWDG_SetCounter(WWDG_CNT);//设置计数值 WWDG_Enable(WWDG_CNT); //开启看门狗 NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //窗口看门狗中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级为2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级为3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能窗口看门狗 NVIC_Init(&NVIC_InitStructure); WWDG_ClearFlag(); //清除提前唤醒中断标志位 WWDG_EnableIT(); //开启提前唤醒中断 } //窗口看门狗中断服务程序 void WWDG_IRQHandler(void) { WWDG_SetCounter(WWDG_CNT); //重设窗口看门狗值 WWDG_ClearFlag(); //清除提前唤醒中断标志位 }

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef _WWDG_H #define _WWDG_H #include "stm32f4xx.h" // WWDG_Init(0x7F,0X5F,WWDG_Prescaler_8); //计数器值为7f,窗口寄存器为5f,分频数为8 void WWDG_Init(u8 tr, u8 wr, u32 fprer); void WWDG_IRQHandler(void); #endif

6、独立看门狗

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "iwdg.h" //初始化独立看门狗 //prer:分频数:0~7(只有低3位有效!) //rlr:自动重装载值,0~0XFFF. //分频因子=4*2^prer.但最大值只能是256! //rlr:重装载寄存器值:低11位有效. //时间计算(大概):Tout=((4*2^prer)*rlr)/32 (ms). void IWDG_Init(u8 prer, u16 rlr) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能对IWDG->PR IWDG->RLR的写 IWDG_SetPrescaler(prer); //设置IWDG分频系数 IWDG_SetReload(rlr); //设置IWDG装载 IWDG_ReloadCounter(); //reload IWDG_Enable(); //使能看门狗 } //喂独立看门狗 void IWDG_Feed(void) { IWDG_ReloadCounter(); //reload }

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _IWDG_H #define _IWDG_H #include "stm32f4xx.h" //IWDG_Init(4,500); //与分频数为64,重载值为500,溢出时间为1s //IWDG_Feed(); //1s内必须调用一次的喂狗函数 void IWDG_Init(u8 prer, u16 rlr); //IWDG初始化 void IWDG_Feed(void); //喂狗函数 #endif

7、GPIO(LED)

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "led.h" /** 不精准延时函数 **/ static void Delay(uint32_t time) { while (time--) { int i = 10000; for (; i > 0; i--) ; } } //LED IO初始化 void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_GPIO_X, ENABLE); //使能GPIOF时钟 GPIO_InitStructure.GPIO_Pin = LED0_PIN; //LED0 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //普通输出模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; //2MHz:led灯低速就行 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(LED0_PORT, &GPIO_InitStructure); //初始化GPIO GPIO_InitStructure.GPIO_Pin = LED1_PIN; //LED0 GPIO_Init(LED1_PORT, &GPIO_InitStructure); //初始化GPIO GPIO_SetBits(LED0_PORT, LED0_PIN); //设置高,灯灭 GPIO_SetBits(LED1_PORT, LED1_PIN); //设置高,灯灭 } /* 主函数中调用这个函数即可看到LED实验效果 */ void LED_Demo(void) { LED_Init(); while (1) { LED0_ON; LED1_OFF; Delay(0xfff); LED0_OFF; LED1_ON; Delay(0xfff); } }

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef __LED_H #define __LED_H #include "stm32f4xx.h" /** API **/ #define LED0_ON GPIO_ResetBits(LED0_PORT, LED0_PIN); //设置低,灯亮 #define LED0_OFF GPIO_SetBits(LED0_PORT, LED0_PIN); //设置高,灯灭 #define LED1_ON GPIO_ResetBits(LED1_PORT, LED1_PIN); #define LED1_OFF GPIO_SetBits(LED1_PORT, LED1_PIN); /** PORT AND PIN **/ #define LED0_PIN GPIO_Pin_9 #define LED0_PORT GPIOF #define LED1_PIN GPIO_Pin_10 #define LED1_PORT GPIOF /** RCC时钟 **/ #define RCC_GPIO_X RCC_AHB1Periph_GPIOF void LED_Init(void); //初始化 #endif

8、外部中断(EXTI)

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include "exti.h" //外部中断0服务程序 void EXTI0_IRQHandler(void) { // app程序 EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位 } //外部中断2服务程序 void EXTI2_IRQHandler(void) { // app程序 EXTI_ClearITPendingBit(EXTI_Line2); //清除LINE2上的中断标志位 } //外部中断3服务程序 void EXTI3_IRQHandler(void) { EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位 } //外部中断4服务程序 void EXTI4_IRQHandler(void) { EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中断标志位 } //外部中断初始化程序 //初始化PE2~4,PA0为中断输入. void EXTIX_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //使能SYSCFG时钟 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2); //PE2 连接到中断线2 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3); //PE3 连接到中断线3 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4); //PE4 连接到中断线4 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); //PA0 连接到中断线0 /* 配置EXTI_Line0 */ EXTI_InitStructure.EXTI_Line = EXTI_Line0; //LINE0 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断事件 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能LINE0 EXTI_Init(&EXTI_InitStructure); //配置 /* 配置EXTI_Line2,3,4 */ EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line4; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断事件 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能 EXTI_Init(&EXTI_InitStructure); //配置 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //外部中断0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; //抢占优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道 NVIC_Init(&NVIC_InitStructure); //配置 NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //外部中断2 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03; //抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道 NVIC_Init(&NVIC_InitStructure); //配置 NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //外部中断3 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道 NVIC_Init(&NVIC_InitStructure); //配置 NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //外部中断4 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道 NVIC_Init(&NVIC_InitStructure); //配置 }

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
#ifndef __EXTI_H #define __EXIT_H #include "stm32f4xx.h" /** 外部中断初始化后可以直接在服务函数中写逻辑函数 **/ void EXTIX_Init(void); //外部中断初始化 #endif

9、随机数发生器

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "rng.h" /** 不精准延时函数 **/ static void Delay(uint32_t time) { while (time--) { int i = 10000; for (; i > 0; i--) ; } } //初始化RNG uint8_t RNG_Init(void) { uint16_t retry = 0; RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE); //开启RNG时钟,来自PLL48CLK RNG_Cmd(ENABLE); //使能RNG while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET && retry < 10000) //等待随机数就绪 { retry++; Delay(100); } if (retry >= 10000) return 1; //随机数产生器工作不正常 return 0; } //得到随机数 //返回值:获取到的随机数 uint32_t RNG_Get_RandomNum(void) { while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET) ; //等待随机数就绪 return RNG_GetRandomNumber(); } //生成[min,max]范围的随机数 int RNG_Get_RandomRange(int min, int max) { return RNG_Get_RandomNum() % (max - min + 1) + min; }

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
#ifndef __RNG_H #define __RNG_H #include "stm32f4xx.h" uint8_t RNG_Init(void); //RNG初始化 uint32_t RNG_Get_RandomNum(void); //得到随机数 int RNG_Get_RandomRange(int min, int max); //生成[min,max]范围的随机数 #endif

10、ADC

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifndef __ADC_H #define __ADC_H #include "stm32f4xx.h" #define RHEOSTAT_NOFCHANEL 5 // ADC1 IO ADC2 IO ADC3 IO // 通道0 PA0 通道0 PA0 通道0 PA0 // 通道1 PA1 通道1 PA1 通道1 PA1 // 通道2 PA2 通道2 PA2 通道2 PA2 // 通道3 PA3 通道3 PA3 通道3 PA3 // 通道4 PA4 通道4 PA4 通道4 PF6 // 通道5 PA5 通道5 PA5 通道5 PF7 // 通道6 PA6 通道6 PA6 通道6 PF8 // 通道7 PA7 通道7 PA7 通道7 PF9 // 通道8 PB0 通道8 PB0 通道8 PF10 // 通道9 PB1 通道9 PB1 通道9 PF3 // 通道10 PC0 通道10 PC0 通道10 PC0 // 通道11 PC1 通道11 PC1 通道11 PC1 // 通道12 PC2 通道12 PC2 通道12 PC2 // 通道13 PC3 通道13 PC3 通道13 PC3 // 通道14 PC4 通道14 PC4 通道14 PF4 // 通道15 PC5 通道15 PC5 通道15 PF5 // 通道16 连接内部温度传感器 通道16 连接内部VSS 通道16 连接内部VSS // 通道17 连接内部Vrefint 通道17 连接内部VSS 通道17 连接内部VSS /*=====================通道1 IO======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT1 GPIOB #define RHEOSTAT_ADC_GPIO_PIN1 GPIO_Pin_0 #define RHEOSTAT_ADC_GPIO_CLK1 RCC_AHB1Periph_GPIOB #define RHEOSTAT_ADC_CHANNEL1 ADC_Channel_8 /*=====================通道2 IO ======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT2 GPIOB #define RHEOSTAT_ADC_GPIO_PIN2 GPIO_Pin_1 #define RHEOSTAT_ADC_GPIO_CLK2 RCC_AHB1Periph_GPIOB #define RHEOSTAT_ADC_CHANNEL2 ADC_Channel_9 /*=====================通道3 IO ======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT3 GPIOA #define RHEOSTAT_ADC_GPIO_PIN3 GPIO_Pin_6 #define RHEOSTAT_ADC_GPIO_CLK3 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC_CHANNEL3 ADC_Channel_6 /*=====================通道4 IO ======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT4 GPIOA #define RHEOSTAT_ADC_GPIO_PIN4 GPIO_Pin_0 #define RHEOSTAT_ADC_GPIO_CLK4 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC_CHANNEL4 ADC_Channel_0 /*=====================通道5 IO ======================*/ // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT5 GPIOA #define RHEOSTAT_ADC_GPIO_PIN5 GPIO_Pin_5 #define RHEOSTAT_ADC_GPIO_CLK5 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC_CHANNEL5 ADC_Channel_5 // ADC 序号宏定义 #define RHEOSTAT_ADC ADC1 #define RHEOSTAT_ADC_CLK RCC_APB2Periph_ADC1 // ADC DR寄存器宏定义,ADC转换后的数字值则存放在这里 #define RHEOSTAT_ADC_DR_ADDR ((u32)ADC1 + 0x4c) // ADC DMA 通道宏定义,这里我们使用DMA传输 #define RHEOSTAT_ADC_DMA_CLK RCC_AHB1Periph_DMA2 #define RHEOSTAT_ADC_DMA_CHANNEL DMA_Channel_0 #define RHEOSTAT_ADC_DMA_STREAM DMA2_Stream0 void Rheostat_Init(void); #endif /* __BSP_ADC_H */

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include "adc.h" __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL] = {0}; static void Rheostat_ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*=====================通道1======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK1, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT1, &GPIO_InitStructure); /*=====================通道2======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK2, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT2, &GPIO_InitStructure); /*=====================通道3=======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK3, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT3, &GPIO_InitStructure); /*=====================通道4======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK4, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT4, &GPIO_InitStructure); /*=====================通道5=======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK5, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(RHEOSTAT_ADC_GPIO_PORT5, &GPIO_InitStructure); } static void Rheostat_ADC_Mode_Config(void) { DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; // ------------------DMA Init 结构体参数 初始化-------------------------- // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的 // 开启DMA时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_DMA_CLK, ENABLE); // 外设基址为:ADC 数据寄存器地址 DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_DR_ADDR; // 存储器地址,实际上就是一个内部SRAM的变量 DMA_InitStructure.DMA_Memory0BaseAddr = (u32)ADC_ConvertedValue; // 数据传输方向为外设到存储器 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 缓冲区大小为,指一次传输的数据量 DMA_InitStructure.DMA_BufferSize = RHEOSTAT_NOFCHANEL; // 外设寄存器只有一个,地址不用递增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 存储器地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // // 外设数据大小为半字,即两个字节 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 存储器数据大小也为半字,跟外设数据大小相同 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 循环传输模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 禁止DMA FIFO ,使用直连模式 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // FIFO 大小,FIFO模式禁止时,这个不用配置 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 选择 DMA 通道,通道存在于流中 DMA_InitStructure.DMA_Channel = RHEOSTAT_ADC_DMA_CHANNEL; //初始化DMA流,流相当于一个大的管道,管道里面有很多通道 DMA_Init(RHEOSTAT_ADC_DMA_STREAM, &DMA_InitStructure); // 使能DMA流 DMA_Cmd(RHEOSTAT_ADC_DMA_STREAM, ENABLE); // 开启ADC时钟 RCC_APB2PeriphClockCmd(RHEOSTAT_ADC_CLK, ENABLE); // -------------------ADC Common 结构体 参数 初始化------------------------ // 独立ADC模式 ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; // 时钟为fpclk x分频 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // 禁止DMA直接访问模式 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; // 采样时间间隔 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; ADC_CommonInit(&ADC_CommonInitStructure); // -------------------ADC Init 结构体 参数 初始化-------------------------- ADC_StructInit(&ADC_InitStructure); // ADC 分辨率 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 扫描模式,多通道采集需要 ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 连续转换 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //禁止外部边沿触发 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发通道,本例子使用软件触发,此值随便赋值即可 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //数据右对齐 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //转换通道 1个 ADC_InitStructure.ADC_NbrOfConversion = RHEOSTAT_NOFCHANEL; ADC_Init(RHEOSTAT_ADC, &ADC_InitStructure); //--------------------------------------------------------------------------- // 配置 ADC 通道转换顺序和采样时间周期 ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL1, 1, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL2, 2, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL3, 3, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL4, 4, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL5, 5, ADC_SampleTime_3Cycles); // 使能DMA请求 after last transfer (Single-ADC mode) ADC_DMARequestAfterLastTransferCmd(RHEOSTAT_ADC, ENABLE); // 使能ADC DMA ADC_DMACmd(RHEOSTAT_ADC, ENABLE); // 使能ADC ADC_Cmd(RHEOSTAT_ADC, ENABLE); //开始adc转换,软件触发 ADC_SoftwareStartConv(RHEOSTAT_ADC); } // 初始化 void Rheostat_Init(void) { Rheostat_ADC_GPIO_Config(); Rheostat_ADC_Mode_Config(); }

11、DAC

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef __DAC_H #define __DAC_H #include "stm32f4xx.h" #define DAC_CLK RCC_APB1Periph_DAC #define DAC_DMA_CLK RCC_AHB1Periph_DMA1 #define DAC_CHANNEL DMA_Channel_7 #define DAC_DMA_STREAM DMA1_Stream5 #define DAC_CH1_GPIO_CLK RCC_AHB1Periph_GPIOA #define DAC_CH1_GPIO_PORT GPIOA #define DAC_CH1_GPIO_PIN GPIO_Pin_4 #define DAC_CH1_CHANNEL DAC_Channel_1 #define DAC_CH2_GPIO_CLK RCC_AHB1Periph_GPIOA #define DAC_CH2_GPIO_PORT GPIOA #define DAC_CH2_GPIO_PIN GPIO_Pin_5 #define DAC_CH2_CHANNEL DAC_Channel_2 #define DAC_CHANNEL1 1 #define DAC_CHANNEL2 2 void DAC_CH1_Configuration(void); void DAC_CH2_Configuration(void); void DAC_Set_V(unsigned char dac_channel, float V); #endif /* __DAC_H */

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "dac.h" //DAC1配置 void DAC_CH1_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; DAC_InitTypeDef DAC_InitType; RCC_AHB1PeriphClockCmd(DAC_CH1_GPIO_CLK, ENABLE); //使能GPIOA时钟 RCC_APB1PeriphClockCmd(DAC_CLK, ENABLE); //使能DAC时钟 GPIO_InitStructure.GPIO_Pin = DAC_CH1_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(DAC_CH1_GPIO_PORT, &GPIO_InitStructure); //初始化 DAC_InitType.DAC_Trigger = DAC_Trigger_None; //不使用触发功能 TEN1=0 DAC_InitType.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生 DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; //屏蔽、幅值设置 DAC_InitType.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //DAC1输出缓存关闭 BOFF1=1 DAC_Init(DAC_CH1_CHANNEL, &DAC_InitType); //初始化DAC通道1 DAC_Cmd(DAC_CH1_CHANNEL, ENABLE); //使能DAC通道1 DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值 } //DAC1配置 void DAC_CH2_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; DAC_InitTypeDef DAC_InitType; RCC_AHB1PeriphClockCmd(DAC_CH2_GPIO_CLK, ENABLE); //使能GPIOA时钟 RCC_APB1PeriphClockCmd(DAC_CLK, ENABLE); //使能DAC时钟 GPIO_InitStructure.GPIO_Pin = DAC_CH2_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(DAC_CH2_GPIO_PORT, &GPIO_InitStructure); //初始化 DAC_InitType.DAC_Trigger = DAC_Trigger_None; //不使用触发功能 TEN1=0 DAC_InitType.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生 DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; //屏蔽、幅值设置 DAC_InitType.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //DAC1输出缓存关闭 BOFF1=1 DAC_Init(DAC_CH2_CHANNEL, &DAC_InitType); //初始化DAC通道2 DAC_Cmd(DAC_CH2_CHANNEL, ENABLE); //使能DAC通道2 DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值 } //DAC通道和输出电压设置 : single-precision operand implicitly converted to double-precision add 4096.0f and 3.3f void DAC_Set_V(unsigned char dac_channel, float V) { if (dac_channel == DAC_CHANNEL1) { DAC_SetChannel1Data(DAC_Align_12b_R, V * 4096.0f / 3.3f); //12位右对齐数据格式设置DAC值 } else if (dac_channel == DAC_CHANNEL2) { DAC_SetChannel2Data(DAC_Align_12b_R, V * 4096.0f / 3.3f); //12位右对齐数据格式设置DAC值 } }

12、DMA

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
#ifndef __DMA_H #define __DMA_H #include "stm32f4xx.h" void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx, uint32_t chx, uint32_t par, uint32_t mar, uint16_t ndtr); //配置DMAx_CHx void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx, uint16_t ndtr); //使能一次DMA传输 #endif

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "dma.h" //DMAx的各通道配置 //这里的传输形式是固定的,这点要根据不同的情况来修改 //从存储器->外设模式/8位数据宽度/存储器增量模式 //DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 //chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7 //par:外设地址 //mar:存储器地址 //ndtr:数据传输量 void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx, uint32_t chx, u32 par, uint32_t mar, uint16_t ndtr) { DMA_InitTypeDef DMA_InitStructure; if ((u32)DMA_Streamx > (u32)DMA2) //得到当前stream是属于DMA2还是DMA1 { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //DMA2时钟使能 } else { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //DMA1时钟使能 } DMA_DeInit(DMA_Streamx); while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE) { } //等待DMA可配置 /* 配置 DMA Stream */ DMA_InitStructure.DMA_Channel = chx; //通道选择 DMA_InitStructure.DMA_PeripheralBaseAddr = par; //DMA外设地址 DMA_InitStructure.DMA_Memory0BaseAddr = mar; //DMA 存储器0地址 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //存储器到外设模式 DMA_InitStructure.DMA_BufferSize = ndtr; //数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //中等优先级 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输 DMA_Init(DMA_Streamx, &DMA_InitStructure); //初始化DMA Stream } //开启一次DMA传输 //DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 //ndtr:数据传输量 void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx, uint16_t ndtr) { DMA_Cmd(DMA_Streamx, DISABLE); //关闭DMA传输 while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE) { } //确保DMA可以被设置 DMA_SetCurrDataCounter(DMA_Streamx, ndtr); //数据传输量 DMA_Cmd(DMA_Streamx, ENABLE); //开启DMA传输 }

13、IIC

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#ifndef __I2C_H #define __I2C_H #include "stm32f4xx.h" #include <inttypes.h> #define EEPROM_I2C_WR 0 /* 写控制bit */ #define EEPROM_I2C_RD 1 /* 读控制bit */ /* 定义I2C总线连接的GPIO端口 */ #define EEPROM_I2C_GPIO_PORT GPIOB /* GPIO端口 */ #define EEPROM_I2C_GPIO_CLK RCC_AHB1Periph_GPIOB /* GPIO端口时钟 */ #define EEPROM_I2C_SCL_PIN GPIO_Pin_8 /* 连接到SCL时钟线的GPIO */ #define EEPROM_I2C_SDA_PIN GPIO_Pin_9 /* 连接到SDA数据线的GPIO */ /* 定义读写SCL和SDA的宏 */ #if 1 /* 条件编译: 1 选择GPIO的库函数实现IO读写 */ #define EEPROM_I2C_SCL_1() GPIO_SetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN) /* SCL = 1 */ #define EEPROM_I2C_SCL_0() GPIO_ResetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN) /* SCL = 0 */ #define EEPROM_I2C_SDA_1() GPIO_SetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* SDA = 1 */ #define EEPROM_I2C_SDA_0() GPIO_ResetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* SDA = 0 */ #define EEPROM_I2C_SDA_READ() GPIO_ReadInputDataBit(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* 读SDA口线状态 */ #else /* 这个分支选择直接寄存器操作实现IO读写 */ /* 注意:如下写法,在IAR最高级别优化时,会被编译器错误优化 */ #define EEPROM_I2C_SCL_1() EEPROM_I2C_GPIO_PORT->BSRRL = EEPROM_I2C_SCL_PIN /* SCL = 1 */ #define EEPROM_I2C_SCL_0() EEPROM_I2C_GPIO_PORT->BSRRH = EEPROM_I2C_SCL_PIN /* SCL = 0 */ #define EEPROM_I2C_SDA_1() EEPROM_I2C_GPIO_PORT->BSRRL = EEPROM_I2C_SDA_PIN /* SDA = 1 */ #define EEPROM_I2C_SDA_0() EEPROM_I2C_GPIO_PORT->BSRRH = EEPROM_I2C_SDA_PIN /* SDA = 0 */ #define EEPROM_I2C_SDA_READ() ((EEPROM_I2C_GPIO_PORT->IDR & EEPROM_I2C_SDA_PIN) != 0) /* 读SDA口线状态 */ #endif /* * AT24C02 2kb = 2048bit = 2048/8 B = 256 B * 32 pages of 8 bytes each * * Device Address * 1 0 1 0 A2 A1 A0 R/W * 1 0 1 0 0 0 0 0 = 0XA0 * 1 0 1 0 0 0 0 1 = 0XA1 */ /* AT24C01/02 every page have 8 bit * AT24C04/08A/16A every page have 16 bit */ #define EEPROM_DEV_ADDR 0xA0 /* 24xx02 address */ #define EEPROM_PAGE_SIZE 8 /* 24xx02 page size*/ #define EEPROM_SIZE 256 /* 24xx02 all size */ void i2c_Start(void); void i2c_Stop(void); void i2c_SendByte(uint8_t _ucByte); uint8_t i2c_ReadByte(void); uint8_t i2c_WaitAck(void); void i2c_Ack(void); void i2c_NAck(void); uint8_t i2c_CheckDevice(uint8_t _Address); uint8_t ee_Test(void); #endif

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#include "i2c.h" /*在访问I2C设备前,请先调用 i2c_CheckDevice() 检测I2C设备是否正常,该函数会配置GPIO*/ static void i2c_Config(void); /* ********************************************************************************************************* * 函 数 名: i2c_Delay * 功能说明: I2C总线位延迟,最快400KHz * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void i2c_Delay(void) { uint8_t i; /*  可用逻辑分析仪测量I2C通讯时的频率 工作条件:CPU主频168MHz ,MDK编译环境,1级优化 经测试,循环次数为20~250时都能通讯正常 */ for (i = 0; i < 40; i++) ; } /* ********************************************************************************************************* * 函 数 名: i2c_Start * 功能说明: CPU发起I2C总线启动信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Start(void) { /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */ EEPROM_I2C_SDA_1(); EEPROM_I2C_SCL_1(); i2c_Delay(); EEPROM_I2C_SDA_0(); i2c_Delay(); EEPROM_I2C_SCL_0(); i2c_Delay(); } /* ********************************************************************************************************* * 函 数 名: i2c_Start * 功能说明: CPU发起I2C总线停止信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Stop(void) { /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */ EEPROM_I2C_SDA_0(); EEPROM_I2C_SCL_1(); i2c_Delay(); EEPROM_I2C_SDA_1(); } /* ********************************************************************************************************* * 函 数 名: i2c_SendByte * 功能说明: CPU向I2C总线设备发送8bit数据 * 形 参:_ucByte : 等待发送的字节 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_SendByte(uint8_t _ucByte) { uint8_t i; /* 先发送字节的高位bit7 */ for (i = 0; i < 8; i++) { if (_ucByte & 0x80) { EEPROM_I2C_SDA_1(); } else { EEPROM_I2C_SDA_0(); } i2c_Delay(); EEPROM_I2C_SCL_1(); i2c_Delay(); EEPROM_I2C_SCL_0(); if (i == 7) { EEPROM_I2C_SDA_1(); // 释放总线 } _ucByte <<= 1; /* 左移一个bit */ i2c_Delay(); } } /* ********************************************************************************************************* * 函 数 名: i2c_ReadByte * 功能说明: CPU从I2C总线设备读取8bit数据 * 形 参:无 * 返 回 值: 读到的数据 ********************************************************************************************************* */ uint8_t i2c_ReadByte(void) { uint8_t i; uint8_t value; /* 读到第1个bit为数据的bit7 */ value = 0; for (i = 0; i < 8; i++) { value <<= 1; EEPROM_I2C_SCL_1(); i2c_Delay(); if (EEPROM_I2C_SDA_READ()) { value++; } EEPROM_I2C_SCL_0(); i2c_Delay(); } return value; } /* ********************************************************************************************************* * 函 数 名: i2c_WaitAck * 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号 * 形 参:无 * 返 回 值: 返回0表示正确应答,1表示无器件响应 ********************************************************************************************************* */ uint8_t i2c_WaitAck(void) { uint8_t re; EEPROM_I2C_SDA_1(); /* CPU释放SDA总线 */ i2c_Delay(); EEPROM_I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */ i2c_Delay(); if (EEPROM_I2C_SDA_READ()) /* CPU读取SDA口线状态 */ { re = 1; } else { re = 0; } EEPROM_I2C_SCL_0(); i2c_Delay(); return re; } /* ********************************************************************************************************* * 函 数 名: i2c_Ack * 功能说明: CPU产生一个ACK信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Ack(void) { EEPROM_I2C_SDA_0(); /* CPU驱动SDA = 0 */ i2c_Delay(); EEPROM_I2C_SCL_1(); /* CPU产生1个时钟 */ i2c_Delay(); EEPROM_I2C_SCL_0(); i2c_Delay(); EEPROM_I2C_SDA_1(); /* CPU释放SDA总线 */ } /* ********************************************************************************************************* * 函 数 名: i2c_NAck * 功能说明: CPU产生1个NACK信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_NAck(void) { EEPROM_I2C_SDA_1(); /* CPU驱动SDA = 1 */ i2c_Delay(); EEPROM_I2C_SCL_1(); /* CPU产生1个时钟 */ i2c_Delay(); EEPROM_I2C_SCL_0(); i2c_Delay(); } /* ********************************************************************************************************* * 函 数 名: i2c_CfgGpio * 功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void i2c_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(EEPROM_I2C_GPIO_CLK, ENABLE); /* 打开GPIO时钟 */ GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; /* 开漏输出 */ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(EEPROM_I2C_GPIO_PORT, &GPIO_InitStructure); /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */ i2c_Stop(); } /* ********************************************************************************************************* * 函 数 名: i2c_CheckDevice * 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在 * 形 参:_Address:设备的I2C总线地址 * 返 回 值: 返回值 0 表示正确, 返回1表示未探测到 ********************************************************************************************************* */ uint8_t i2c_CheckDevice(uint8_t _Address) { uint8_t ucAck; i2c_Config(); /* 配置GPIO */ i2c_Start(); /* 发送启动信号 */ /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */ i2c_SendByte(_Address | EEPROM_I2C_WR); ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */ i2c_Stop(); /* 发送停止信号 */ return ucAck; }

14、SPI

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef __SPI_H #define __SPI_H #include "stm32f4xx.h" void SPI1_Init(void); //初始化SPI1口 void SPI1_SetSpeed(uint8_t SpeedSet); //设置SPI1速度 uint8_t SPI1_ReadWriteByte(uint8_t TxData); //SPI1总线读写一个字节 #endif

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "spi.h" //以下是SPI模块的初始化代码,配置成主机模式 //SPI口初始化 //这里针是对SPI1的初始化 void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //使能SPI1时钟 //GPIOFB3,4,5初始化设置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; //PB3~5复用功能输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1); //PB3复用为 SPI1 GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1); //PB4复用为 SPI1 GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1); //PB5复用为 SPI1 //这里只针对SPI口初始化 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE); //复位SPI1 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE); //停止复位SPI1 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式 SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 SPI_Cmd(SPI1, ENABLE); //使能SPI外设 SPI1_ReadWriteByte(0xff); //启动传输 } //SPI1速度设置函数 //SPI速度=fAPB2/分频系数 //@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256 //fAPB2时钟一般为84Mhz: void SPI1_SetSpeed(uint8_t SPI_BaudRatePrescaler) { assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler)); //判断有效性 SPI1->CR1 &= 0XFFC7; //位3-5清零,用来设置波特率 SPI1->CR1 |= SPI_BaudRatePrescaler; //设置SPI1速度 SPI_Cmd(SPI1, ENABLE); //使能SPI1 } //SPI1 读写一个字节 //TxData:要写入的字节 //返回值:读取到的字节 uint8_t SPI1_ReadWriteByte(uint8_t TxData) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) { } // 等待发送区空 SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte 数据 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) { } //等待接收完一个byte return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据 }

15、24C02

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifndef __24C02_H #define __24C02_H #include "stm32f4xx.h" /* * AT24C02 2kb = 2048bit = 2048/8 B = 256 B * 32 pages of 8 bytes each * * Device Address * 1 0 1 0 A2 A1 A0 R/W * 1 0 1 0 0 0 0 0 = 0XA0 * 1 0 1 0 0 0 0 1 = 0XA1 */ /* AT24C01/02每页有8个字节 * AT24C04/08A/16A每页有16个字节 */ #define EEPROM_DEV_ADDR 0xA0 /* 24xx02的设备地址 */ #define EEPROM_PAGE_SIZE 8 /* 24xx02的页面大小 */ #define EEPROM_SIZE 256 /* 24xx02总容量 */ uint8_t ee_Init(void); uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize); uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize); void ee_Erase(void); uint8_t ee_Test(void); #endif /* __24C02_H */

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#include "_24C02.h" #include "i2c.h" /* ********************************************************************************************************* * 函 数 名: ee_Init() * 功能说明: 判断串行EERPOM是否正常 * 形 参:无 * 返 回 值: 1 表示正常, 0 表示不正常 ********************************************************************************************************* */ uint8_t ee_Init(void) { if (i2c_CheckDevice(EEPROM_DEV_ADDR) == 0) { return 1; } else { /* 失败后,切记发送I2C总线停止信号 */ i2c_Stop(); return 0; } } /* ********************************************************************************************************* * 函 数 名: ee_ReadBytes * 功能说明: 从串行EEPROM指定地址处开始读取若干数据 * 形 参:_usAddress : 起始地址 * _usSize : 数据长度,单位为字节 * _pReadBuf : 存放读到的数据的缓冲区指针 * 返 回 值: 0 表示失败,1表示成功 ********************************************************************************************************* */ uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize) { uint16_t i; /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */ /* 第1步:发起I2C总线启动信号 */ i2c_Start(); /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR); /* 此处是写指令 */ /* 第3步:等待ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */ i2c_SendByte((uint8_t)_usAddress); /* 第5步:等待ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */ i2c_Start(); /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD); /* 此处是读指令 */ /* 第8步:发送ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第9步:循环读取数据 */ for (i = 0; i < _usSize; i++) { _pReadBuf[i] = i2c_ReadByte(); /* 读1个字节 */ /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */ if (i != _usSize - 1) { i2c_Ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */ } else { i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */ } } /* 发送I2C总线停止信号 */ i2c_Stop(); return 1; /* 执行成功 */ cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */ /* 发送I2C总线停止信号 */ i2c_Stop(); return 0; } /* ********************************************************************************************************* * 函 数 名: ee_WriteBytes * 功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率 * 形 参:_usAddress : 起始地址 * _usSize : 数据长度,单位为字节 * _pWriteBuf : 存放读到的数据的缓冲区指针 * 返 回 值: 0 表示失败,1表示成功 ********************************************************************************************************* */ uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize) { uint16_t i, m; uint16_t usAddr; /* 写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。 对于24xx02,page size = 8 简单的处理方法为:按字节写操作模式,没写1个字节,都发送地址 为了提高连续写的效率: 本函数采用page wirte操作。 */ usAddr = _usAddress; for (i = 0; i < _usSize; i++) { /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */ if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0) { /* 第0步:发停止信号,启动内部写操作 */ i2c_Stop(); /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms CLK频率为200KHz时,查询次数为30次左右 */ for (m = 0; m < 1000; m++) { /* 第1步:发起I2C总线启动信号 */ i2c_Start(); /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR); /* 此处是写指令 */ /* 第3步:发送一个时钟,判断器件是否正确应答 */ if (i2c_WaitAck() == 0) { break; } } if (m == 1000) { goto cmd_fail; /* EEPROM器件写超时 */ } /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */ i2c_SendByte((uint8_t)usAddr); /* 第5步:等待ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } } /* 第6步:开始写入数据 */ i2c_SendByte(_pWriteBuf[i]); /* 第7步:发送ACK */ if (i2c_WaitAck() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } usAddr++; /* 地址增1 */ } /* 命令执行成功,发送I2C总线停止信号 */ i2c_Stop(); return 1; cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */ /* 发送I2C总线停止信号 */ i2c_Stop(); return 0; } void ee_Erase(void) { uint16_t i; uint8_t buf[EEPROM_SIZE]; /* 填充缓冲区 */ for (i = 0; i < EEPROM_SIZE; i++) { buf[i] = 0xFF; } /* 写EEPROM, 起始地址 = 0,数据长度为 256 */ if (ee_WriteBytes(buf, 0, EEPROM_SIZE) == 0) { // printf("擦除eeprom出错!rn"); return; } else { // printf("擦除eeprom成功!rn"); } }

16、W25QXX

.h文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#ifndef __W25QXX_H #define __W25QXX_H #include "stm32f4xx.h" //W25X系列/Q系列芯片列表 //W25Q80 ID 0XEF13 //W25Q16 ID 0XEF14 //W25Q32 ID 0XEF15 //W25Q64 ID 0XEF16 //W25Q128 ID 0XEF17 #define W25Q80 0XEF13 #define W25Q16 0XEF14 #define W25Q32 0XEF15 #define W25Q64 0XEF16 #define W25Q128 0XEF17 #define NM25Q80 0X5213 #define NM25Q16 0X5214 #define NM25Q32 0X5215 #define NM25Q64 0X5216 #define NM25Q128 0X5217 #define NM25Q256 0X5218 extern uint16_t W25QXX_TYPE; //定义W25QXX芯片型号 #define W25QXX_CS *((volatile unsigned long *)((((GPIOB_BASE + 20) & 0xF0000000) + 0x2000000 + (((GPIOB_BASE + 20) & 0xFFFFF) << 5) + (14 << 2)))) //W25QXX的片选信号 // //指令表 #define W25X_WriteEnable 0x06 #define W25X_WriteDisable 0x04 #define W25X_ReadStatusReg 0x05 #define W25X_WriteStatusReg 0x01 #define W25X_ReadData 0x03 #define W25X_FastReadData 0x0B #define W25X_FastReadDual 0x3B #define W25X_PageProgram 0x02 #define W25X_BlockErase 0xD8 #define W25X_SectorErase 0x20 #define W25X_ChipErase 0xC7 #define W25X_PowerDown 0xB9 #define W25X_ReleasePowerDown 0xAB #define W25X_DeviceID 0xAB #define W25X_ManufactDeviceID 0x90 #define W25X_JedecDeviceID 0x9F void W25QXX_Init(void); uint16_t W25QXX_ReadID(void); //读取FLASH ID uint8_t W25QXX_ReadSR(void); //读取状态寄存器 void W25QXX_Write_SR(uint8_t sr); //写状态寄存器 void W25QXX_Write_Enable(void); //写使能 void W25QXX_Write_Disable(void); //写保护 void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead); //读取flash void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); //写入flash void W25QXX_Erase_Chip(void); //整片擦除 void W25QXX_Erase_Sector(uint32_t Dst_Addr); //扇区擦除 void W25QXX_Wait_Busy(void); //等待空闲 void W25QXX_PowerDown(void); //进入掉电模式 void W25QXX_WAKEUP(void); //唤醒 // 不精确的延时 static void _w25qxx_delay(__IO uint32_t nCount) { for (; nCount != 0; nCount--) ; } #endif

.c文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#include "w25qxx.h" #include "spi.h" u16 W25QXX_TYPE = W25Q128; //默认是W25Q128 //4Kbytes为一个Sector //16个扇区为1个Block //W25Q128 //容量为16M字节,共有128个Block,4096个Sector //初始化SPI FLASH的IO口 void W25QXX_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); //使能GPIOG时钟 //GPIOB14 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //PB14 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //PG7 GPIO_Init(GPIOG, &GPIO_InitStructure); //初始化 GPIO_SetBits(GPIOG, GPIO_Pin_7); //PG7输出1,防止NRF干扰SPI FLASH的通信 W25QXX_CS = 1; //SPI FLASH不选中 SPI1_Init(); //初始化SPI SPI1_SetSpeed(SPI_BaudRatePrescaler_4); //设置为21M时钟 W25QXX_TYPE = W25QXX_ReadID(); //读取FLASH ID. } //读取W25QXX的状态寄存器 //BIT7 6 5 4 3 2 1 0 //SPR RV TB BP2 BP1 BP0 WEL BUSY //SPR:默认0,状态寄存器保护位,配合WP使用 //TB,BP2,BP1,BP0:FLASH区域写保护设置 //WEL:写使能锁定 //BUSY:忙标记位(1,忙;0,空闲) //默认:0x00 u8 W25QXX_ReadSR(void) { u8 byte = 0; W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令 byte = SPI1_ReadWriteByte(0Xff); //读取一个字节 W25QXX_CS = 1; //取消片选 return byte; } //写W25QXX状态寄存器 //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!! void W25QXX_Write_SR(uint8_t sr) { W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令 SPI1_ReadWriteByte(sr); //写入一个字节 W25QXX_CS = 1; //取消片选 } //W25QXX写使能 //将WEL置位 void W25QXX_Write_Enable(void) { W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_WriteEnable); //发送写使能 W25QXX_CS = 1; //取消片选 } //W25QXX写禁止 //将WEL清零 void W25QXX_Write_Disable(void) { W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令 W25QXX_CS = 1; //取消片选 } //读取芯片ID //返回值如下: //0XEF13,表示芯片型号为W25Q80 //0XEF14,表示芯片型号为W25Q16 //0XEF15,表示芯片型号为W25Q32 //0XEF16,表示芯片型号为W25Q64 //0XEF17,表示芯片型号为W25Q128 u16 W25QXX_ReadID(void) { u16 Temp = 0; W25QXX_CS = 0; SPI1_ReadWriteByte(0x90); //发送读取ID命令 SPI1_ReadWriteByte(0x00); SPI1_ReadWriteByte(0x00); SPI1_ReadWriteByte(0x00); Temp |= SPI1_ReadWriteByte(0xFF) << 8; Temp |= SPI1_ReadWriteByte(0xFF); W25QXX_CS = 1; return Temp; } //读取SPI FLASH //在指定地址开始读取指定长度的数据 //pBuffer:数据存储区 //ReadAddr:开始读取的地址(24bit) //NumByteToRead:要读取的字节数(最大65535) void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead) { u16 i; W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_ReadData); //发送读取命令 SPI1_ReadWriteByte((u8)((ReadAddr) >> 16)); //发送24bit地址 SPI1_ReadWriteByte((u8)((ReadAddr) >> 8)); SPI1_ReadWriteByte((u8)ReadAddr); for (i = 0; i < NumByteToRead; i++) { pBuffer[i] = SPI1_ReadWriteByte(0XFF); //循环读数 } W25QXX_CS = 1; } //SPI在一页(0~65535)内写入少于256个字节的数据 //在指定地址开始写入最大256字节的数据 //pBuffer:数据存储区 //WriteAddr:开始写入的地址(24bit) //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!! void W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { u16 i; W25QXX_Write_Enable(); //SET WEL W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_PageProgram); //发送写页命令 SPI1_ReadWriteByte((u8)((WriteAddr) >> 16)); //发送24bit地址 SPI1_ReadWriteByte((u8)((WriteAddr) >> 8)); SPI1_ReadWriteByte((u8)WriteAddr); for (i = 0; i < NumByteToWrite; i++) SPI1_ReadWriteByte(pBuffer[i]); //循环写数 W25QXX_CS = 1; //取消片选 W25QXX_Wait_Busy(); //等待写入结束 } //无检验写SPI FLASH //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败! //具有自动换页功能 //在指定地址开始写入指定长度的数据,但是要确保地址不越界! //pBuffer:数据存储区 //WriteAddr:开始写入的地址(24bit) //NumByteToWrite:要写入的字节数(最大65535) //CHECK OK void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { u16 pageremain; pageremain = 256 - WriteAddr % 256; //单页剩余的字节数 if (NumByteToWrite <= pageremain) pageremain = NumByteToWrite; //不大于256个字节 while (1) { W25QXX_Write_Page(pBuffer, WriteAddr, pageremain); if (NumByteToWrite == pageremain) break; //写入结束了 else //NumByteToWrite>pageremain { pBuffer += pageremain; WriteAddr += pageremain; NumByteToWrite -= pageremain; //减去已经写入了的字节数 if (NumByteToWrite > 256) pageremain = 256; //一次可以写入256个字节 else pageremain = NumByteToWrite; //不够256个字节了 } }; } //写SPI FLASH //在指定地址开始写入指定长度的数据 //该函数带擦除操作! //pBuffer:数据存储区 //WriteAddr:开始写入的地址(24bit) //NumByteToWrite:要写入的字节数(最大65535) u8 W25QXX_BUFFER[4096]; void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { u32 secpos; u16 secoff; u16 secremain; u16 i; u8 *W25QXX_BUF; W25QXX_BUF = W25QXX_BUFFER; secpos = WriteAddr / 4096; //扇区地址 secoff = WriteAddr % 4096; //在扇区内的偏移 secremain = 4096 - secoff; //扇区剩余空间大小 //printf("ad:%X,nb:%Xrn",WriteAddr,NumByteToWrite);//测试用 if (NumByteToWrite <= secremain) secremain = NumByteToWrite; //不大于4096个字节 while (1) { W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096); //读出整个扇区的内容 for (i = 0; i < secremain; i++) //校验数据 { if (W25QXX_BUF[secoff + i] != 0XFF) break; //需要擦除 } if (i < secremain) //需要擦除 { W25QXX_Erase_Sector(secpos); //擦除这个扇区 for (i = 0; i < secremain; i++) //复制 { W25QXX_BUF[i + secoff] = pBuffer[i]; } W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096); //写入整个扇区 } else W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间. if (NumByteToWrite == secremain) break; //写入结束了 else //写入未结束 { secpos++; //扇区地址增1 secoff = 0; //偏移位置为0 pBuffer += secremain; //指针偏移 WriteAddr += secremain; //写地址偏移 NumByteToWrite -= secremain; //字节数递减 if (NumByteToWrite > 4096) secremain = 4096; //下一个扇区还是写不完 else secremain = NumByteToWrite; //下一个扇区可以写完了 } }; } //擦除整个芯片 //等待时间超长... void W25QXX_Erase_Chip(void) { W25QXX_Write_Enable(); //SET WEL W25QXX_Wait_Busy(); W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_ChipErase); //发送片擦除命令 W25QXX_CS = 1; //取消片选 W25QXX_Wait_Busy(); //等待芯片擦除结束 } //擦除一个扇区 //Dst_Addr:扇区地址 根据实际容量设置 //擦除一个山区的最少时间:150ms void W25QXX_Erase_Sector(uint32_t Dst_Addr) { //监视falsh擦除情况,测试用 // printf("fe:%xrn",Dst_Addr); Dst_Addr *= 4096; W25QXX_Write_Enable(); //SET WEL W25QXX_Wait_Busy(); W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令 SPI1_ReadWriteByte((u8)((Dst_Addr) >> 16)); //发送24bit地址 SPI1_ReadWriteByte((u8)((Dst_Addr) >> 8)); SPI1_ReadWriteByte((u8)Dst_Addr); W25QXX_CS = 1; //取消片选 W25QXX_Wait_Busy(); //等待擦除完成 } //等待空闲 void W25QXX_Wait_Busy(void) { while ((W25QXX_ReadSR() & 0x01) == 0x01) ; // 等待BUSY位清空 } //进入掉电模式 void W25QXX_PowerDown(void) { W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_PowerDown); //发送掉电命令 W25QXX_CS = 1; //取消片选 _w25qxx_delay(0xff); //等待TPD } //唤醒 void W25QXX_WAKEUP(void) { W25QXX_CS = 0; //使能器件 SPI1_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB W25QXX_CS = 1; //取消片选 _w25qxx_delay(0xff); //等待TRES1 }

完整工程

在这里插入图片描述
在这里插入图片描述

天翼云盘:下载链接:https://cloud.189.cn/t/uINbm2F7bYrm (访问码:5ptj)
码云:https://gitee.com/stylle/stm32f4-bsp-all/tree/master

最后

以上就是美丽狗最近收集整理的关于stm32f4外设学习篇(代码集合)1、串口12、串口2(RS485)3、定时器(多路)4、PWM5、窗口看门狗6、独立看门狗7、GPIO(LED)8、外部中断(EXTI)9、随机数发生器10、ADC11、DAC12、DMA13、IIC14、SPI15、24C0216、W25QXX完整工程的全部内容,更多相关stm32f4外设学习篇(代码集合)1、串口12、串口2(RS485)3、定时器(多路)4、PWM5、窗口看门狗6、独立看门狗7、GPIO(LED)8、外部中断(EXTI)9、随机数发生器10、ADC11、DAC12、DMA13、IIC14、SPI15、24C0216、W25QXX完整工程内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(119)

评论列表共有 0 条评论

立即
投稿
返回
顶部