RCC: reset and clock control 复位和时钟控制
配置系统时钟,开启/关闭各个外设的时钟电源。
一般首要操作就是通过RCC去打开相应外设的时钟,比如:
1
| RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
|
通过RCC打开APB2总线外设的GPIOC的时钟,使他开启(ENABLE)。
GPIO_InitTypeDef 结构体
对GPIO进行设置/初始化之前,要先定义一个结构体,这个模板里规定了:如果你想配置 GPIO,你必须提供 模式、速度 和 引脚号 这三样东西。
1 2 3 4
| GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_PP; GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13; GPIO_InitStryct.GPIO_Speed=GPIO_Speed_50MHz;
|
然后再去对GPIO进行配置
1
| GPIO_InitStryct.GPIO_Speed=GPIO_Speed_50MHz;
|
GPIO_SetBits/GPIO_ResetBits
1 2 3 4
| GPIO_SetBits(GPIO_TypeDef* GPIOX , GPIO_Pin);
GPIO_ResetBits(GPIO_TypeDef* GPIOX , GPIO_Pin);
|
GPIO_WriteBits
1 2 3
| GPIO_WriteBits(GPIO_TypeDef* GPIOX , GPIO_Pin , BitVal);
|
同时初始化多个GPIO要用或(|)而不是与(&)
1
| GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1 | GPIO_Pin_2;
|
因为:
1 2 3 4 5
| 0000 0000 0000 0001 (Pin_0) | 0000 0000 0000 0010 (Pin_1) | 0000 0000 0000 0100 (Pin_2) ----------------------- = 0000 0000 0000 0111 (结果是 0x0007)
|
这是计算过程,如果是与的话,就是:
1 2 3 4 5
| 0000 0000 0000 0001 (Pin_0) & 0000 0000 0000 0010 (Pin_1) & 0000 0000 0000 0100 (Pin_2) ----------------------- = 0000 0000 0000 0000 (结果是 0x0000)
|
因为这三个数的 1 都在不同的位置上,没有任何重叠,所以按位与的结果是 全 0,相当于啥也没干。
推挽输出/开漏输出驱动GPIO
推挽输出:
1
| GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
|
这使得GPIO输出高低电平都有驱动能力。
开漏输出:
1
| GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_OD;
|
这使得GPIO输出高电平时没有驱动能力,
输出低电平时才有驱动能力。
上拉输入
1
| GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
|
上拉输入模式,默认保持高电平。
接一个开关按钮,一端接GND,一端接GPIO端口。
输入寄存器默认高电平,也就是默认1(因为是上拉输入模式),如果通过GPIO读取函数读到寄存器的某一位为0,意味着按钮接通了,按钮的一端GND会导致GPIO端口被输入强下拉信号,也就是低电平,因此当检测到输入寄存器的电平为0时,说明按钮被按下。
1 2 3
| if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){ }
|
按键封装函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void key_Init(){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50Hz; GPIO_Init(GPIOB,&GPIO_InitStructure); }
uint8_t Key_GetNum(void){ uint8_t KeyNum=0; if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){ Delay(30); while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){} Delay(30); KeyNum=1; } return KeyNum; }
|
GPIO初始化固定模版
1 2 3 4 5 6
| RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_Init(GPIOB, &GPIO_InitStructure);
|
GPIO读取函数
GPIO作为输入引脚
1 2
| GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15)
|
示例应用(eg:通过读取光敏电阻输入的高低电平来控制蜂鸣器)
1 2 3 4
| if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15)==1) { GPIO_ResetBits(GPIOB, GPIO_Pin_12); }
|
GPIO作为输出引脚
1 2
| GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_2)
|
示例应用(eg:摁钮控制LED灯,翻转他的电平,让他亮或灭)
1 2 3 4 5 6 7 8 9
| if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15)==0) { Delay(50); while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15)==0){} if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_2)==1) GPIO_ResetBits(GPIOB,GPIO_Pin_2); else{ GPIO_SetBits(GPIOB,GPIO_Pin_2); } }
|
中断配置

GPIO——AFIO——EXTI——NVIC
模块化编程,单开一个文件,简历库函数和中断函数。
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
| void countsenser_init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_StructInit(&GPIO_InitStructure); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3); EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line3; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); void EXTI3_IRQHandler() { if (EXTI_GetITStatus(EXTI_Line3) != RESET) { } EXTI_ClearITPendingBit(EXTI_Line3); }
|
定时器配置中断

按照上图顺序从左到右依次配置。
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
| void Timer_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = 10000-1; TIM_TimeBaseStructure.TIM_Prescaler = 7200-1; TIM_TimeBaseStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ClearFlag(TIM2, TIM_IT_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM2, ENABLE); }
void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }
|
上方是使用内部时钟模式
下方是使用ETR外部触发输入来计数。
也就是把:
TIM_InternalClockConfig(TIM2);
换成:
TIM_ETRClockMode2Config();
具体如下:
1
| TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x03);
|
四个参数:
TIM2:选择定时器2
TIM_ExtTRGPSC_OFF:不分频,外部触发一次,计数一次。
TIM_ExtTRGPolarity_NonInverted:上升沿或高电平触发。
0x03:信号滤波,一般设0x03就行。
1 2
| TIM_TimeBaseStructure.TIM_Period = 10-1; TIM_TimeBaseStructure.TIM_Prescaler = 1-1;
|
TIM_Period = 10-1,意味着计数器记到10就触发中断。
TIM_Prescaler = 1-1,意味着外部触发信号来一次,计数器就记一次。而上一个代码用内部时钟计数时,为啥要写7200-1,是因为内部时钟触发信号相当于一秒来72,000,000次,所以你必须先分频7200次,相当于每秒来10000次,而同时TIM_Period设置为10000-1,即计数器加到10000才触发一次中断,这样就实现了一秒触发一次中断。
PWM实现呼吸灯
PWM主要靠定时器,不需要中断,所以不用配置NVIC,EXTI等等,需要配置定时器和GPIO。

PWM初始化函数
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
| void PWM_Init() { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_InternalClockConfig(TIM2); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 100-1; TIM_TimeBaseStructure.TIM_Prescaler = 720-1; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse= 0; TIM_OC1Init(TIM2,&TIM_OCInitStructure); TIM_OC2Init(TIM2,&TIM_OCInitStructure); TIM_OC3Init(TIM2,&TIM_OCInitStructure); TIM_OC4Init(TIM2,&TIM_OCInitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_Cmd(TIM2, ENABLE); }
void PWM_SetCompare1(uint16_t Compare) { TIM_SetCompare1(TIM2, Compare); }
void PWM_SetPSC(uint16_t PSC) { TIM_PrescalerConfig(TIM2,PSC,TIM_PSCReloadMode_Immediate); }
|
到这里实现了LED灯以百分之20的功率输出。但我们要实现呼吸灯的话,还要实时调整CCR的值,让他在0-100之间均匀的变化。
有一个库函数可以设置/写入CCR的值,TIM_SetCompare4(TIM2,X);,这个函数可以实时指定输出比较单元的CCR。
1 2 3 4 5
| TIM_SetCompare1(TIM2,X); TIM_SetCompare2(TIM2,X); TIM_SetCompare3(TIM2,X); TIM_SetCompare4(TIM2,X);
|
因此可以把TIM_SetCompare4(TIM2,X);放到主程序的for循环中,这样就可以实时增加/减少PWM的占空比,从而实现呼吸灯。
具体如下:
1 2 3 4 5 6 7 8 9 10 11
| void main(){ for(i=0;i<100;i++) { TIM_SetCompare4(TIM2,i); } for(j=0;j<100;j++) { TIM_SetCompare4(TIM2,100-i); } }
|
引脚重映射(暂时了解)
针对TIM2的引脚重映射

!注:重映射的配置寄存器位于 AFIO 模块内:重映射的配置信息(比如把 TIM2_CH1 从 PA0 重映射到 PA15)是存储在 AFIO 模块内部的寄存器(如 AFIO_MAPR)中的。
1 2 3
| RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
|
PWM控制直流电机转速(不包括方向)
主要是C语言逻辑这一块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| uint8_t i; uint8_t j=30; while (1) { i=Key_GetNum(); if (i==1) { j-=10; GPIO_SetBits(GPIOA, GPIO_Pin_4); GPIO_ResetBits(GPIOA, GPIO_Pin_5); if (j==0) { j=30; } PWM_SetCompare3(j); } OLED_ShowSignedNum(1, 7, j, 3); }
|
PWMI模版
输入捕获部分,任何从外部输入的信号想要进入定时器,都需要配置TIM_ICInit。


先配置GPIO,然后时基单元(TIM),然后输入捕获单元(IC)初始化(包括选择通道,滤波器,边沿检测,极性选择,分频器),然后触发源选择,从模式选择,启动定时器。
计数值存在CCR1中。
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
| void IC_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;; GPIO_Init(GPIOA, &GPIO_InitStructure); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_InternalClockConfig(TIM3); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Prescaler = 72-1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = 65536-1; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICFilter=0x03; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInit(TIM3, &TIM_ICInitStructure); TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset); TIM_Cmd(TIM3, ENABLE); } uint16_t IC_GetFreq(){ return 1000000/TIM_GetCapture1(TIM3); }
|
有一个函数可以实时读取CCR的值,就是TIM_GetCapture1(TIMX);结果会返回一个uint16_t的整型常量。
1 2 3 4 5
| TIM_GetCapture1(TIM3); TIM_GetCapture2(TIM3); TIM_GetCapture3(TIM3); TIM_GetCapture4(TIM3);
|
PWMI双通道
前面GPIO,TIM基本不变,主要是输入捕获部分要多设置一个通道。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICFilter=0x03; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM_ICInitStructure.TIM_ICFilter=0x03; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset); TIM_Cmd(TIM3, ENABLE);
|
获取频率和占空比的函数封装
1 2 3 4 5 6 7
| uint16_t IC_GetFreq() { return 1000000/TIM_GetCapture1(TIM3); } uint16_t IC_GetDuty() { return TIM_GetCapture2(TIM3)*100/TIM_GetCapture1(TIM3); }
|
双通道测频率和占空比原理
对于上图用来捕获频率和PWM占空比。输入捕获部分设置了两路通道,一路上升沿触发,一路下降沿,而触发源则特定选择了通道一,来触发从模式Reset(计数清零)。所以每次上升沿来了,计数都会清零。但清零前,STM32硬件电路会把计数器CNT的值存到CCR1中,这是硬件电路自己完成的,不需要软件操作。这样我们就通过CNT的计数,知道了一个周期的时间。而通道二下降沿来临,计数器CNT的值会计入到CCR2中。这样就通过CCR2的计数知道了高电平占总周期的比例,即占空比。
定时器的编码器接口及其库函数

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
| void Encoder_Interface_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_Init(GPIOA, &GPIO_InitStructure); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Prescaler = 1-1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = 65536-1; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICFilter=0x0A; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInit(TIM3, &TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICFilter=0x0A; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInit(TIM3, &TIM_ICInitStructure); TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising); TIM_Cmd(TIM3, ENABLE); }
int16_t GetCounter(void) { int16_t Temp =0; Temp=TIM_GetCounter(TIM3); TIM_SetCounter(TIM3,0); return Temp; }
void TIM2_IRQHandler() { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { counter=TIM_GetCounter(TIM3); TIM_SetCounter(TIM3,0); TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }
|
ADC

DMA工作的三个条件:
- 传输计数器不为0。
- DMA使能。
- 触发源(软件触发/硬件触发)有信号。
单通道,非连续转换,非扫描模式
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
| void AD_Init(){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_28Cycles5 ); ADC_InitTypeDef ADC_InitStruct; ADC_InitStruct.ADC_Mode=ADC_Mode_Independent; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStruct.ADC_ScanConvMode = DISABLE; ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; ADC_InitStruct.ADC_NbrOfChannel=1; ADC_Init(ADC1, &ADC_InitStruct); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)==SET); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)==SET); }
uint16_t AD_GetValue(void){ ADC_SoftwareStartConvCmd(ADC1,ENABLE); while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET){} return ADC_GetConversionValue(ADC1); }
|
单通道,连续转换,非扫描模式
把上述代码AD_Init函数中的结构体配置中ADC_ContinuousConvMode由DISABLE修改为ENABLE。
并且自始至终只需要一次软件触发开启AD转换,因此直接把这个指令塞进初始化函数里即可。
1 2
| ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; ADC_SoftwareStartConvCmd(ADC1,ENABLE);
|
而下面的uint16_t AD_GetValue(void)函数只需要return ADC_GetConversionValue(ADC1);即可,不需要判断EOC标志位是否被置位,因为全程连续转换,并不是单次转换。
1 2 3 4
| uint16_t AD_GetValue(void){ return ADC_GetConversionValue(ADC1); }
|
单通道模式实现多通道模式,非连续转换,非扫描模式(不用硬件电路,而是用代码实现)
在AD_Init函数中配置通道的函数:ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_28Cycles5 );把第二个参数ADC_Channel_1变成一个变量,然后在循环中不断输入不同的通道,因为每次转换都在几微秒内完成,因此在人眼来看,就是4个通道同时进行。
具体实现,先把ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_28Cycles5 );拿出来,然后封装一个需要输入形参的函数,然后把形参输入到第二个变量位置,即可实现。
1 2 3 4 5 6 7
| uint16_t AD_GetValue(uint8_t ADC_Channel) { ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_28Cycles5 ); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)==RESET){} return ADC_GetConversionValue(ADC1); }
|
然后在主函数的循环中,写入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| while (1) { AD1=AD_GetValue(ADC_Channel_1); AD2=AD_GetValue(ADC_Channel_2); AD3=AD_GetValue(ADC_Channel_3); AD4=AD_GetValue(ADC_Channel_4); OLED_ShowString(1,1,"AD1:"); OLED_ShowString(2,1,"AD2:"); OLED_ShowString(3,1,"AD3:"); OLED_ShowString(4,1,"AD4:"); OLED_ShowNum(1,5,AD1,5); OLED_ShowNum(2,5,AD2,5); OLED_ShowNum(3,5,AD3,5); OLED_ShowNum(4,5,AD4,5); }
|
表示变量地址

在stm32中,所有的地址都是16进制的,也就是0x……。
1 2 3 4 5 6 7 8 9 10 11 12 13
| const uint8_t a=66; uint8_t b=77; int main(void) { OLED_Init(); while (1) { OLED_ShowNum(1,1,a,3); OLED_ShowHexNum(2,1,(uint32_t)&a,8); OLED_ShowNum(3,1,b,3); OLED_ShowHexNum(4,1,(uint32_t)&b,8); } }
|
现象如下:

由上图结合代码可知,在变量a前加入const,那么a就被存储到了Flash中,成为了常量。而b作为uint8_t的变量,则被存储到了SRAM中。
所以如果有一个很大的表或者数据库,一般不需要修改,那就在前面加上const,让他存放在FLash中,而非SRAM中。
ADC1->DR 是“去那个地址把数据拿回来”
&ADC1->DR 才是“那个地址本身”。
DMA转运数据

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
| uint8_t Transfer_Size; void My_DMAInit(uint32_t AddrA , uint32_t AddrB , uint8_t Size) { Transfer_Size=Size; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA; DMA_InitStructure.DMA_PeripheralDataSize= DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryBaseAddr = AddrB; DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = Size; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_M2M = DMA_M2M_Enable; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, DISABLE); }
void DMA_Transfer(void) { DMA_Cmd(DMA1_Channel1, DISABLE); DMA_SetCurrDataCounter(DMA1_Channel1, Transfer_Size); DMA_Cmd(DMA1_Channel1, ENABLE); while (DMA_GetFlagStatus(DMA1_FLAG_TC1==RESET)); DMA_ClearFlag(DMA1_FLAG_TC1); }
|
ADC扫描模式+DMA
串口USART通信
配置流程图如下:

TX设置复用输出,RX设置输入。图片中间部分全部通过结构体配置。
发送部分:
初始化封装代码:
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
| void Serial_Init(void){ RCC_APb2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure1; GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_9; GPIO_Init(GPIOA, &GPIO_InitStructure1); GPIO_InitTypeDef GPIO_InitStructure2; GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure2.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOA, &GPIO_InitStructure2); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; 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(USART1, &USART_InitStructure); USART_Cmd(USART1,ENABLE); }
void Serial_SendByte(uint8_t data){ USART_SendData(USART1,data); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){} }
void Serial_SendArray(uint8_t* Arr , int16_t Length){ uint16_t i; for(i=0;i<Length;i++){ Serial_SendByte(Arr[i]); } }
void Serial_SendString(char* String) { uint16_t i; for (i=0; String[i]!='\0';i++) { Serial_SendByte(String[i]); } }
uint32_t Serial_Pow(uint16_t X,uint16_t Y) { uint16_t res=1; while (Y--){ res*=X; } return res; }
void Serial_SendNumber(uint32_t Number,uint8_t Length) { uint32_t i; for (i=0; i<Length; i++) { Serial_SendByte(Number/Serial_Pow(10,Length-i-1)%10+0x30); } }
|
拆分数字的思路
12345/10000%10=1
12345/1000%10=2
12345/100%10=3
12345/10%10=4
12345/1%10=5
想取出某个数的第x位,就把这个数除以(10的(x-1)次方),再对10取余。
发送部分主函数代码
1 2 3 4 5 6 7 8
| int main(void){ OLED_Init(); Serial_Init(); while(1){ Serial_SendByte(0x4A); Delay_ms(1000); } }
|
接收部分(查询模式或中断模式)
查询模式:
查询模式中初始化封装代码:
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
| uint8_t ReceiveData; uint8_t Serial_RxFlag; void Serial_Init(void){ RCC_APb2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure1; GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_9; GPIO_Init(GPIOA, &GPIO_InitStructure1); GPIO_InitTypeDef GPIO_InitStructure2; GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure2.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOA, &GPIO_InitStructure2); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; 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(USART1, &USART_InitStructure); USART_Cmd(USART1,ENABLE); }
|
查询模式中主函数:
1 2 3 4 5 6 7 8 9 10 11
| int main(void){ OLED_Init(); Serial_Init(); while(1){ if(USART_GetFlagStatus(USART1,USART_FlAG_RXNE)!=RESET){ RXData=USART_ReceiveData(USART1); Serial_SendByte(RXData); OLED_ShowHexNum(1,1,RXData,2); } } }
|
USART_FLAG_RXNE 是什么?
RXNE = Receive Not Empty,接收非空标志位。
硬件逻辑:
当串口硬件收到 1 字节数据,存入接收寄存器 DR 后,硬件自动把 RXNE 标志置 1;
当你调用 USART_ReceiveData() 读取数据后,硬件自动清零该标志。
中断模式:
中断模式初始化封装函数:
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
| uint8_t ReceiveData; uint8_t Serial_RxFlag; void Serial_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure1; GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_9; GPIO_Init(GPIOA, &GPIO_InitStructure1);
GPIO_InitTypeDef GPIO_InitStructure2; GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure2.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOA, &GPIO_InitStructure2);
USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; 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(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART1, ENABLE); }
void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { ReceiveData = USART_ReceiveData(USART1); Serial_RxFlag=1; USART_ClearFlag(USART1, USART_FLAG_RXNE); } }
uint8_t Serial_GetData(void) { return ReceiveData; }
uint8_t Serial_GetFlag(void) { if (Serial_RxFlag==1) { Serial_RxFlag=0; return 1; } return 0; }
|
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
这个函数是连接串口和中断的桥梁,这个函数产生最初的中断信号。
中断模式主函数
1 2 3 4 5 6 7 8 9 10 11
| int main(void){ OLED_Init(); Serial_Init(); while(1){ if(Serial_GetFlag()==1){ RXData=Serial_GetData(); Serial_SendByte(RXData); OLED_ShowHexNum(1,1,RXData,2); } } }
|
printf在stm32中重映射到串口
首先:Keil和GCC底层printf函数走的是两条完全不同的路线
Keil MDK + 勾选MicroLIB

pinrtf -> 格式化拆分字符 -> fputc(ch,FILE*) -> 串口发送
MicroLIB库强制所有printf输出走fputc,所以只要重写fputc,printf百分百生效。
这是针对Keil的。
1 2 3 4
| int fputc(int ch, FILE *f) { Serial_SendByte(ch); return ch; }
|
ARM-GCC+newlib-nano+nosys.specs(Clion+Cmake环境)
Newly-nano为了极致精简FLASH,砍掉冗余代码,重构了printf底层链路,不再默认走fputc,出现了两条分枝优先级。
优先级: _io_putchar > _write > fputc
开启nano.specs后,printf优先走最精简路径:_io_putchar,直接跳过fputc,所以如果我重写fputc等于白写。库根本不进入这个函数,串口自然没有数据。
注:newlib-nano 为了省 Flash,砍掉文件流 FILE 缓冲层,设计了**单字符直通接口__io_putchar**:
只要用户实现这个函数,nano 的 printf 直接逐个字符丢进它,绕过 FILE 结构体、绕过 fputc、绕过_write,代码体积最小(这就是你加完立刻出数据的原因)。
1 2 3 4 5
| int __io_putchar(int ch) { Serial_SendByte(ch); return ch & 0xFF; }
|
这里的printf默认行缓冲,必须在末尾写上\n,才能发送出数据,不然 数据一直存在内存里,不发串口。
串口收发HEX数据包
发送HEX数据包Serial.c代码:
1 2 3 4 5 6 7 8 9 10 11 12
| uint8_t ReceiveData; uint8_t Serial_TxPacket[4]; uint8_t Serial_RxPacket[4]; uint8_t Serial_RxFlag; void Serial_Init(void){ } void Serial_SendPacket(void) { Serial_SendByte(0xFF); Serial_SendArray(Serial_TxPacket,4); Serial_SendByte(0xFE); }
|
发送的数据包分为包头,数据,包尾三部分。
包头为0xFF。
数据为Serial_TxPacket,这里固定为4个字节长度的数组。
包尾为0xFE。
包头和包尾的作用是在接受时用来校准数据包用的。
发送HEX数据包Main.c代码:
在主函数中主要定义Serial_TxPacket的四个值,然后输出Serial_SendPacket()。
接收HEX数据包Serial.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
| uint8_t ReceiveData; uint8_t Serial_TxPacket[4]; uint8_t Serial_RxPacket[4]; uint8_t Serial_RxFlag;
void Serial_Init(void){ }
void USART1_IRQHandler(void) { static uint8_t RxState=0; static uint8_t pRxPacket=0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { uint8_t RxData=USART_ReceiveData(USART1); if (RxState==0) { if (RxData==0xFF) { RxState=1; pRxPacket=0; } } else if (RxState==1) { Serial_RxPacket[pRxPacket]=RxData; pRxPacket++; if (pRxPacket>=4) { RxState=2; } } else if (RxState==2) { if (RxData==0xFE) { RxState=0; Serial_RxFlag=1; } } } }
uint8_t Serial_GetData(void) { return ReceiveData; }
uint8_t Serial_GetFlag(void) { if (Serial_RxFlag==1) { Serial_RxFlag=0; return 1; } return 0; }
|
接收HEX数据包Main.c代码:
1 2 3 4 5 6 7 8 9 10 11 12
| void main(void){ OLED_Init(); Serial_Init(); while(1){ if (Serial_GetFlag() == 1) { OLED_ShowHexNum(4,1,Serial_RxPacket[0],2); OLED_ShowHexNum(4,4,Serial_RxPacket[1],2); OLED_ShowHexNum(4,7,Serial_RxPacket[2],2); OLED_ShowHexNum(4,10,Serial_RxPacket[3],2); } } }
|
I2C配置MPU6050
软件模拟I2C
DR/SR/CR
DR: Data Register(数据寄存器)
SR: Status Register(状态寄存器)
CR: Control Register(控制寄存器)
首先先写My_I2C.c/h,然后在通过My_I2C来配置MPU6050的寄存器,封装到MPU6050.c/h。
My_I2C.c
·先写三个函数,MyI2C_W_SCL,MyI2C_W_SDA,MyI2C_R_SDA,本质上是封装一下翻转GPIO电平和读取GPIO电平。
·初始化I2C,本质上是初始化GPIO。
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
| void MyI2C_W_SCL(uint8_t BitValue) { GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue); Delay_us(10); }
void MyI2C_W_SDA(uint8_t BitValue) { GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue); Delay_us(10); }
uint8_t MyI2C_R_SDA(void) { uint8_t BitValue; BitValue=GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11); Delay_us(10); return BitValue; }
void MyI2C_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); }
|
通过MyI2C_W_SCL,MyI2C_W_SDA,MyI2C_R_SDA三个函数来配置I2C的6个过程:
·void MyI2C_Start(void);
·void MyI2C_Stop(void);
·void MyI2C_SendByte(uint8_t Byte);
·uint8_t MyI2C_ReceiveByte(void);
·void MyI2C_SendAsk(uint8_t AckBit);
·uint8_t MyI2C_ReceiveAsk(void);
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
| void MyI2C_Start(void) { MyI2C_W_SDA(1); MyI2C_W_SCL(1);
MyI2C_W_SDA(0); MyI2C_W_SCL(0); }
void MyI2C_Stop(void) { MyI2C_W_SDA(0); MyI2C_W_SCL(1); MyI2C_W_SDA(0); }
void MyI2C_SendByte(uint8_t Byte) { uint8_t i; for (i=0;i<8;i++) { MyI2C_W_SDA(Byte & (0x80>>i));
MyI2C_W_SCL(1); MyI2C_W_SCL(0); } }
uint8_t MyI2C_ReceiveByte(void) { uint8_t i; uint8_t Byte=0x00; MyI2C_W_SDA(1); for (i=0;i<8;i++) { MyI2C_W_SCL(1); if (MyI2C_R_SDA() == 1) {Byte|=(0x80>>i);} MyI2C_W_SCL(0); }
return Byte; }
void MyI2C_SendAsk(uint8_t AckBit) { MyI2C_W_SDA(AckBit); MyI2C_W_SCL(1); MyI2C_W_SCL(0); }
uint8_t MyI2C_ReceiveAsk(void) { uint8_t AckBit; MyI2C_W_SDA(1); MyI2C_W_SCL(1); AckBit=MyI2C_R_SDA(); MyI2C_W_SCL(0); return AckBit; }
|
在写MPU6050.c之前,需要单独新建一个头文件,MPU6050_Reg.h
这里面用宏定义,来把寄存器地址0xXX换成英文,通俗易懂,,方便阅读
MPU6050_Reg.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
| #define MPU6050_SMPLRT_DIV 0x19 #define MPU6050_CONFIG 0x1A #define MPU6050_GYRO_CONFIG 0x1B #define MPU6050_ACCEL_CONFIG 0x1C
#define MPU6050_ACCEL_XOUT_H 0x3B #define MPU6050_ACCEL_XOUT_L 0x3C
#define MPU6050_ACCEL_YOUT_H 0x3D #define MPU6050_ACCEL_YOUT_L 0x3E
#define MPU6050_ACCEL_ZOUT_H 0x3F #define MPU6050_ACCEL_ZOUT_L 0x40
#define MPU6050_TEMP_OUT_H 0x41 #define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43 #define MPU6050_GYRO_XOUT_L 0x44
#define MPU6050_GYRO_YOUT_H 0x45 #define MPU6050_GYRO_YOUT_L 0x46
#define MPU6050_GYRO_ZOUT_H 0x47 #define MPU6050_GYRO_ZOUT_L 0x48
#define MPU6050_PWR_MGMT_1 0x6B #define MPU6050_PWR_MGMT_2 0x6C #define MPU6050_WHO_AM_I 0x75
|
MPU6050.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
| void MPU6050_Init(void) { MyI2C_Init(); MPU6050_WriteRegister(MPU6050_PWR_MGMT_1, 0x01); MPU6050_WriteRegister(MPU6050_PWR_MGMT_2, 0x00); MPU6050_WriteRegister(MPU6050_SMPLRT_DIV,0x09); MPU6050_WriteRegister(MPU6050_CONFIG,0x06); MPU6050_WriteRegister(MPU6050_GYRO_CONFIG,0x18); MPU6050_WriteRegister(MPU6050_ACCEL_CONFIG,0x18); }
void MPU6050_WriteRegister(uint8_t RegAddress, uint8_t Data) { MyI2C_Start(); MyI2C_SendByte(0xD0); MyI2C_ReceiveAsk(); MyI2C_SendByte(RegAddress); MyI2C_ReceiveAsk(); MyI2C_SendByte(Data); MyI2C_ReceiveAsk(); MyI2C_Stop(); }
int8_t MPU6050_ReadRegister(uint8_t RegAddress) { MyI2C_Start(); MyI2C_SendByte(0xD0); MyI2C_ReceiveAsk(); MyI2C_SendByte(RegAddress); MyI2C_ReceiveAsk(); MyI2C_Stop();
MyI2C_Start(); MyI2C_SendByte(0xD1); MyI2C_ReceiveAsk(); int8_t Byte=MyI2C_ReceiveByte(); MyI2C_SendAsk(1); MyI2C_Stop(); return Byte; }
void MPU6050_GetData(int16_t *AX, int16_t *AY, int16_t *AZ, int16_t *GX, int16_t *GY, int16_t *GZ) { uint8_t AX_H, AY_H, AZ_H, GX_H, GY_H, GZ_H; uint8_t AX_L, AY_L, AZ_L, GX_L, GY_L, GZ_L; AX_H=MPU6050_ReadRegister(MPU6050_ACCEL_XOUT_H); AX_L=MPU6050_ReadRegister(MPU6050_ACCEL_XOUT_L); *AX=((AX_H<<8)|AX_L); AY_H=MPU6050_ReadRegister(MPU6050_ACCEL_YOUT_H); AY_L=MPU6050_ReadRegister(MPU6050_ACCEL_YOUT_L); *AY=((AY_H<<8)|AY_L); AZ_H=MPU6050_ReadRegister(MPU6050_ACCEL_ZOUT_H); AZ_L=MPU6050_ReadRegister(MPU6050_ACCEL_ZOUT_L); *AZ=((AZ_H<<8)|AZ_L); GX_H=MPU6050_ReadRegister(MPU6050_GYRO_XOUT_H); GX_L=MPU6050_ReadRegister(MPU6050_GYRO_XOUT_L); *GX=((GX_H<<8)|GX_L); GY_H=MPU6050_ReadRegister(MPU6050_GYRO_YOUT_H); GY_L=MPU6050_ReadRegister(MPU6050_GYRO_YOUT_L); *GY=((GY_H<<8)|GY_L); GZ_H=MPU6050_ReadRegister(MPU6050_GYRO_ZOUT_H); GZ_L=MPU6050_ReadRegister(MPU6050_GYRO_ZOUT_L); *GZ=((GZ_H<<8)|GZ_L); }
uint8_t MPU6050_GetID(void) { return MPU6050_ReadRegister(MPU6050_WHO_AM_I); }
|
Main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| int main(void) { OLED_Init(); MPU6050_Init(); int16_t AX, AY, AZ; int16_t GX, GY, GZ; while (1) { MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); OLED_ShowSignedNum(2,1,AX,5); OLED_ShowSignedNum(3,1,AY,5); OLED_ShowSignedNum(4,1,AZ,5); OLED_ShowSignedNum(2,8,GX,5); OLED_ShowSignedNum(3,8,GY,5); OLED_ShowSignedNum(4,8,GZ,5); OLED_ShowString(1,1,"ID:"); OLED_ShowHexNum(1,4,MPU6050_GetID(),2); } }
|

硬件模拟I2C
需要更改I2C的初始化,需要开启I2C硬件外设,以及对外设进行初始化配置。
然后对于MPU6050的读写,需要用库函数来进行操作。
MyI2C_Hardware.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
| void MyI2C_Hardware_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_ClockSpeed = 100000; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_OwnAddress1= 0x00; I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE); }
|
MPU6050_Hardware.c
其中主要是修改MPU6050的读与写函数,其他与软件I2C的MPU6050函数差不多
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
| uint8_t MPU6050_WaitEvent(I2C_TypeDef *I2Cx,uint32_t event) { uint32_t timeout = 100000;
while (I2C_CheckEvent(I2Cx, event) == ERROR) { if (I2C_GetFlagStatus(I2Cx, I2C_FLAG_AF) == SET) { I2C_ClearFlag(I2Cx, I2C_FLAG_AF); I2C_GenerateSTOP(I2Cx, ENABLE); return 1; }
if (--timeout == 0) { I2C_GenerateSTOP(I2Cx, ENABLE); return 2; } } return 0; }
void MPU6050_WriteRegister_I2CHardware(uint8_t RegAddress, uint8_t Data) { I2C_GenerateSTART(I2C1, ENABLE); MPU6050_WaitEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C1, 0xD0, I2C_Direction_Transmitter); MPU6050_WaitEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
I2C_SendData(I2C1, RegAddress); MPU6050_WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
I2C_SendData(I2C1, Data); MPU6050_WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
I2C_GenerateSTOP(I2C1, ENABLE); }
uint8_t MPU6050_ReadRegister_I2CHardware(uint8_t regAddress) { uint8_t data; uint32_t timeout; volatile uint32_t clear;
timeout = 100000;
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET) { if (--timeout == 0) { return 0; } }
I2C_GenerateSTART(I2C1, ENABLE);
if (MPU6050_WaitEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != 0) { return 0; }
I2C_Send7bitAddress(I2C1,0xD0,I2C_Direction_Transmitter);
if (MPU6050_WaitEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != 0) { return 0; }
I2C_SendData(I2C1, regAddress);
if (MPU6050_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != 0) { return 0; }
I2C_GenerateSTART(I2C1, ENABLE);
if (MPU6050_WaitEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) != 0) { return 0; }
I2C_AcknowledgeConfig(I2C1, DISABLE);
I2C_Send7bitAddress(I2C1,0xD0,I2C_Direction_Receiver);
timeout = 100000;
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR) == RESET) { if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET) { I2C_ClearFlag(I2C1, I2C_FLAG_AF); I2C_GenerateSTOP(I2C1, ENABLE); I2C_AcknowledgeConfig(I2C1, ENABLE); return 0; }
if (--timeout == 0) { I2C_GenerateSTOP(I2C1, ENABLE); I2C_AcknowledgeConfig(I2C1, ENABLE); return 0; } }
clear = I2C1->SR1; clear = I2C1->SR2; (void)clear;
I2C_GenerateSTOP(I2C1, ENABLE);
timeout = 100000;
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET) { if (--timeout == 0) { I2C_AcknowledgeConfig(I2C1, ENABLE); return 0; } }
data = I2C_ReceiveData(I2C1);
I2C_AcknowledgeConfig(I2C1, ENABLE);
return data; }
void MPU6050_Init(void) { MyI2C_Hardware_Init(); MPU6050_WriteRegister_I2CHardware(MPU6050_PWR_MGMT_1, 0x00); MPU6050_WriteRegister_I2CHardware(MPU6050_PWR_MGMT_2, 0x00); MPU6050_WriteRegister_I2CHardware(MPU6050_SMPLRT_DIV,0x09); MPU6050_WriteRegister_I2CHardware(MPU6050_CONFIG,0x06); MPU6050_WriteRegister_I2CHardware(MPU6050_GYRO_CONFIG,0x18); MPU6050_WriteRegister_I2CHardware(MPU6050_ACCEL_CONFIG,0x18); }
void MPU6050_GetData(int16_t *AX, int16_t *AY, int16_t *AZ, int16_t *GX, int16_t *GY, int16_t *GZ) { uint8_t AX_H, AY_H, AZ_H, GX_H, GY_H, GZ_H; uint8_t AX_L, AY_L, AZ_L, GX_L, GY_L, GZ_L; AX_H=MPU6050_ReadRegister_I2CHardware(MPU6050_ACCEL_XOUT_H); AX_L=MPU6050_ReadRegister_I2CHardware(MPU6050_ACCEL_XOUT_L); *AX=((AX_H<<8)|AX_L); AY_H=MPU6050_ReadRegister_I2CHardware(MPU6050_ACCEL_YOUT_H); AY_L=MPU6050_ReadRegister_I2CHardware(MPU6050_ACCEL_YOUT_L); *AY=((AY_H<<8)|AY_L); AZ_H=MPU6050_ReadRegister_I2CHardware(MPU6050_ACCEL_ZOUT_H); AZ_L=MPU6050_ReadRegister_I2CHardware(MPU6050_ACCEL_ZOUT_L); *AZ=((AZ_H<<8)|AZ_L); GX_H=MPU6050_ReadRegister_I2CHardware(MPU6050_GYRO_XOUT_H); GX_L=MPU6050_ReadRegister_I2CHardware(MPU6050_GYRO_XOUT_L); *GX=((GX_H<<8)|GX_L); GY_H=MPU6050_ReadRegister_I2CHardware(MPU6050_GYRO_YOUT_H); GY_L=MPU6050_ReadRegister_I2CHardware(MPU6050_GYRO_YOUT_L); *GY=((GY_H<<8)|GY_L); GZ_H=MPU6050_ReadRegister_I2CHardware(MPU6050_GYRO_ZOUT_H); GZ_L=MPU6050_ReadRegister_I2CHardware(MPU6050_GYRO_ZOUT_L); *GZ=((GZ_H<<8)|GZ_L); }
uint8_t MPU6050_GetID(void) { return MPU6050_ReadRegister_I2CHardware(MPU6050_WHO_AM_I); }
|
main.c
和软件模拟I2C来读取MPU6050的main.c一模一样。
BKP备份寄存器
STM32硬件设计一条硬性保护逻辑:
默认状态下,CPU不能读写后备域寄存器,防止主电源波动时误改写备份数据。
其中后备域包括:
·BKP 备份寄存器
·LSE 外部低速晶振电路
· RTC实时时钟模块
而解锁后备域的开关在PWR外设里,因此想设置BKP,需要先解锁外设域,而解锁外设域需要先开启PWR的时钟,然后调用PWR_BackupAccessCmd(ENABLE);解锁函数。
BKP初始化函数
1 2 3 4 5 6
| void BKP_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); }
|
BKP写/读操作
1 2 3 4 5 6 7 8 9
| int main(void){ OLED_Init(); BKP_Init(); BKP_WriteBackupRegister(BKP_DR1,0x2233); OLED_ShowHexNum(1,1,BKP_ReadBackupRegister(BKP_DR1),4); while(1){ } }
|
RTC

MyRTC.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
| #include "MyRTC.h" #include "stm32f10x.h" #include "time.h" uint16_t MyRTC_Time[]={2026,07,05,10,01,55}; void MyRTC_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET){} RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro(); RTC_WaitForLastTask();
RTC_SetPrescaler(32768-1); RTC_WaitForLastTask();
if (BKP_ReadBackupRegister(BKP_DR1)!=0x11) { MyRTC_SetTime(); BKP_WriteBackupRegister(BKP_DR1, 0x11); }
}
void MyRTC_SetTime() { struct tm time_data; time_data.tm_year=MyRTC_Time[0]-1900; time_data.tm_mon=MyRTC_Time[1]-1; time_data.tm_mday=MyRTC_Time[2]; time_data.tm_hour=MyRTC_Time[3]; time_data.tm_min=MyRTC_Time[4]; time_data.tm_sec=MyRTC_Time[5]; time_t time_cnt=mktime(&time_data); RTC_SetCounter(time_cnt); RTC_WaitForLastTask(); }
void MyRTC_ReadTime(void) { time_t time_cnt=RTC_GetCounter(); struct tm time_date=*localtime(&time_cnt);
MyRTC_Time[0]=time_date.tm_year+1900; MyRTC_Time[1]=time_date.tm_mon+1; MyRTC_Time[2]=time_date.tm_mday; MyRTC_Time[3]=time_date.tm_hour; MyRTC_Time[4]=time_date.tm_min; MyRTC_Time[5]=time_date.tm_sec; }
|
main.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 "MyRTC.h" void BKP_Init(void); int main(void) { OLED_Init(); MyRTC_Init(); while (1) { OLED_ShowString(1,1,"Data:"); MyRTC_ReadTime(); OLED_ShowNum(2,1,MyRTC_Time[0],4); OLED_ShowString(2,5,":"); OLED_ShowNum(2,6,MyRTC_Time[1],2); OLED_ShowString(2,8,":"); OLED_ShowNum(2,9,MyRTC_Time[2],2); OLED_ShowString(2,5,":"); OLED_ShowNum(3,1,MyRTC_Time[3],2); OLED_ShowString(3,3,":"); OLED_ShowNum(3,4,MyRTC_Time[4],2); OLED_ShowString(3,6,":"); OLED_ShowNum(3,7,MyRTC_Time[5],2); OLED_ShowNum(4,1,RTC_GetCounter(),10); } }
|
WDG(看门狗)
看门狗本质上是一个定时器,在指定时间范围内,程序没有执行喂狗(重置计数器)操作时,看门狗硬件电路就自动产生复位信号。
STM32内置两个看门狗:
独立看门狗(IWDG):独立工作,对时间精度要求较低。独立使用LSI时钟。
窗口看门狗(WWDG):要求看门狗在精确计时窗口起作用。使用APB1时钟。
独立看门狗框图:

键寄存器控制电路

IWDG超时时间

My_IWDG.c函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include "My_IWDG.h" #include "stm32f10x.h" void My_IWDG_Init(void) {
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_16);
IWDG_SetReload(2500-1);
IWDG_ReloadCounter();
IWDG_Enable(); }
|
main.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
| int main(void) { OLED_Init(); MyRTC_Init(); My_IWDG_Init(); Key_Init(); OLED_ShowString(1,1,"IDWG_Test"); if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET) { OLED_ShowString(2,1,"IWDGRST"); Delay_ms(500); OLED_ShowString(2,1," "); Delay_ms(100); RCC_ClearFlag(); } else { OLED_ShowString(3,1,"RST"); Delay_ms(500); OLED_ShowString(3,1," "); Delay_ms(100); } while (1) { Key_GetNum(); IWDG_ReloadCounter(); OLED_ShowString(4,1,"FEED"); Delay_ms(600); OLED_ShowString(4,1," "); Delay_ms(200); } }
|
SPI
软件SPI读写W25Q64(一种Flash存储器)
W25Q64写入操作时:
·写入操作时,必须先写使能。写入后不需要写失能。
·写入操作后,芯片进入忙状态,不响应新的读写操作。
W25Q64读操作时:
·读取程序结束后不会进入忙状态,但不能在忙状态时读取。
MySPI.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
| void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); }
void MySPI_W_SCK(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue); }
void MySPI_W_MOSI(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue); }
uint8_t MySPI_R_MISO(void) { uint8_t BitValue; BitValue=GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6); return BitValue; }
void MySPI_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOA, &GPIO_InitStructure); MySPI_W_SS(1); MySPI_W_SCK(0); }
void MySPI_Start(void) { MySPI_W_SS(0); }
void MySPI_Stop(void) { MySPI_W_SS(1); }
uint8_t MySPI_SwapByte(uint8_t ByteSend) { uint8_t ByteReceive=0x00; for (int i=0; i<8; i++) { MySPI_W_MOSI(ByteSend&(0x80>>i)); MySPI_W_SCK(1); if (MySPI_R_MISO()==1){ByteReceive|=(0x80>>i);} MySPI_W_SCK(0); } return ByteReceive; }
|
MyW25Q64.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
| void MyW25Q64_Init(void) { MySPI_Init(); }
void MyW25Q64_ReadID(uint8_t *MID,uint16_t *DID) { MySPI_Start(); MySPI_SwapByte(W25Q64_JEDEC_ID); *MID=MySPI_SwapByte(W25Q64_DUMMY_BYTE); *DID=MySPI_SwapByte(W25Q64_DUMMY_BYTE); *DID<<=8; *DID|=MySPI_SwapByte(W25Q64_DUMMY_BYTE); MySPI_Stop(); }
void MyW25Q64_WriteEnable(void) { MySPI_Start(); MySPI_SwapByte(W25Q64_WRITE_ENABLE); MySPI_Stop(); }
void MyW25Q64_WaitBusy(void) { uint32_t Timeout=100000; MySPI_Start(); MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE)&0x01)==1) { Timeout--; if (Timeout==0) { break; } } MySPI_Stop(); }
void MyW25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint8_t Count) { MyW25Q64_WriteEnable(); MySPI_Start(); MySPI_SwapByte(W25Q64_PAGE_PROGRAM); MySPI_SwapByte(Address>>16); MySPI_SwapByte(Address>>8); MySPI_SwapByte(Address); for (int i=0;i<Count;i++) { MySPI_SwapByte(DataArray[i]); } MySPI_Stop(); MyW25Q64_WaitBusy(); }
void MyW25Q64_EraseSector(uint32_t Address) { MyW25Q64_WriteEnable(); MySPI_Start(); MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); MySPI_SwapByte(Address>>16); MySPI_SwapByte(Address>>8); MySPI_SwapByte(Address); MySPI_Stop(); MyW25Q64_WaitBusy(); }
void MyW25Q64_ReadData(uint32_t Address ,uint8_t *DataArray, uint8_t Count) { MyW25Q64_WriteEnable(); MySPI_Start(); MySPI_SwapByte(W25Q64_READ_DATA); MySPI_SwapByte(Address>>16); MySPI_SwapByte(Address>>8); MySPI_SwapByte(Address); for (int i=0;i<Count;i++) { DataArray[i]=MySPI_SwapByte(W25Q64_DUMMY_BYTE); } MySPI_Stop(); MyW25Q64_WaitBusy(); }
|
Main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int main(void) { uint8_t Array1[4]={0xA1,0xB2,0xC3,0xD4}; uint8_t Array2[4]={0x00,0x00,0x00,0x00}; OLED_Init(); MyW25Q64_Init(); MyW25Q64_EraseSector(0x00000); MyW25Q64_PageProgram(0x00000,Array1,4); MyW25Q64_ReadData(0x00000,Array2,4); while (1) { OLED_ShowHexNum(1,1,Array2[0],4); OLED_ShowHexNum(2,1,Array2[1],4); OLED_ShowHexNum(3,1,Array2[2],4); OLED_ShowHexNum(4,1,Array2[3],4); } }
|
硬件SPI读取W25Q64

先配置GPIO,再通过一个结构体配置SPI硬件电路,最后开启硬件SPI,SPI_Cmd
SPI.c
SS仍然用软件模拟,因为用软件模拟非常简单
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
| void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); }
void MySPI_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7; GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
MySPI_W_SS(1); }
void MySPI_Start(void) { MySPI_W_SS(0); }
void MySPI_Stop(void) { MySPI_W_SS(1); }
uint8_t MySPI_SwapByte(uint8_t ByteSend) { uint8_t ByteReceive=0x00; while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)!=SET); SPI_I2S_SendData(SPI1, ByteSend); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)!=SET); return SPI_I2S_ReceiveData(SPI1); }
|
Main.c
与软件模拟SPI读取W25Q64的Main一模一样。