STM32
环境搭建
- 系统启动相关
- 标准库函数
- 库函数配置
- main
GPIO 输入输出
- 类型
- 输入
- 浮空输入 悬空不确定 floating
- 上拉输入 默认高 ipu
- 下拉输入 默认低 ipd
- 模拟输入 接 adc ain
- 输出
- 开漏输出 高电平高阻态 od
- 推挽输出 准确输出 pp
- 复用开漏输出 由片上外设控制,高电平为高阻态,低电平接VSS
- 复用推挽输出 由片上外设控制,高电平接VDD,低电平接VSS
- 输入
- 方法
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE)
开启外设时钟GPIO_InitTypeDef gpio
初始化结构体gpio.GPIO_Mode = GPIO_Mode_Out_PP
推挽输出GPIO_Mode_Out_OD
开漏输出GPIO_Mode_IPU
上拉输入GPIO_Mode_AIN
模拟输入GPIO_Mode_IN_FLOATING
浮空输入GPIO_Mode_IPD
输入下拉GPIO_Mode_Out_PP
推挽输出GPIO_Mode_AF_OD
开漏复用GPIO_Mode_AF_PP
推挽复用
gpio.GPIO_Pin = GPIO_Pin_0
pin 脚gpio.GPIO_Speed = GPIO_Speed_50MHz;
翻转速度
GPIO_Init(GPIOA, &gpio)
初始化GPIO_ResetBits(GPIOA, GPIO_Pin_0)
设置为低电平GPIO_SetBits(GPIOA, GPIO_Pin_0)
设置为高电平GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET / Bit_SET)
设置高低GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
读取输入寄存器GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_1)
读取输出寄存器
c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
void GPIO_Configuration(void);
void Delay(uint32_t nCount);
int main(void)
{
GPIO_Configuration();
while (1)
{
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_SET) // 检测按键是否按下
{
GPIO_SetBits(GPIOC, GPIO_Pin_13); // 点亮LED
Delay(0xFFFFF); // 延时消抖
}
else
{
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // 熄灭LED
}
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
// 配置按键输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置LED输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void Delay(uint32_t nCount)
{
for (; nCount != 0; nCount--)
;
}
EXTI 外部中断
- 优先级 四位寄存器
- 抢占优先级 n
- 响应优先级 4-n
- NVIC 分诊台叫号
- EXTI 外部中断
- AFIO 引脚选择(同数字只选一个)
- 流程 rcc gpio afio exti nvic
- 方法
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE)
gpio 时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE)
afio 时钟- gpio 用输入
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA-G, GPIO_PinSource0-15)
配置 AFIO 接哪个脚EXTI_InitTypeDef exti
配置 EXTI.EXIT_Line = EXTI_Line14
14 线路.EXIT_LineCmd = ENABLE
使能.EXTI_Mode = EXTI_Mode_Interrupt | EXTI_Mode_Event
中断模式和事件模式.EXTI_Trigger = EXTI_Trigger_Falling
上升沿 下降沿 都
EXIT_Init(&exti)
初始化 EXTINVIC_PriorityGroupConfig(NVIC_PriorityGroup_1-4)
分组NVIC_InitTypeDef nvic
配置 nvic.NVIC_IRQChannel = EXTI15_10_IRQn
指定通道.NVIC_IRQChannelCmd = ENABLE
使能.NVIC_IRQChannelPreemptionPriority = 1
抢占优先级.NVIC_IRQChannelSubPriority = 1
响应优先级
NVIC_Init(&exti)
初始化- 中断函数
c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_rcc.h"
void GPIO_Configuration(void);
void EXTI_Configuration(void);
void NVIC_Configuration(void);
int main(void)
{
GPIO_Configuration();
EXTI_Configuration();
NVIC_Configuration();
while (1)
{
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void EXTI_Configuration(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 在此处添加中断处理代码
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
TIM 定时器
- 类型
- 高级定时器 APB2
- 通用定时器 APB1
- 基本定时器 APB1 c8t6 没有
- 硬件 内外部时钟->预分频器->计数器->自动重装寄存器->中断/事件
- 流程 rcc 选时钟源 配置时基单元 配置中断 配置 nvic 中断函数
- 定时频率 = 72m/(预分频+1)/(重装+1)
- 方法
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE)
时钟TIM_InternalClockConfig(TIM2)
使用内部时钟(可不写)- 使用外部时钟
- 配置 GPIO 上拉输入 gpio 引脚要匹配
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00)
TIM_TimeBaseInitTypeDef tim
配置 tim.TIM_ClockDivision = TIM_CKD_DIV1
滤波器时钟分频.TIM_CounterMode = TIM_CounterMode_Up
向上计数.TIM_Period = 10000-1
自动重装 记 10k 个数 即 1s 因为从 0 计数 要减一 arr.TIM_Prescaler = 7200-1
预分频器 在 72m/7.2k=10k 的频率下 psc.TIM_RepetitionCounter = 0
重复计数器(高级计数器才有 可不写)
TIM_TimeBaseInit(TIM2, &tim)
初始化TIM_ClearFlag(TIM2, TIM_FLAG_Update)
清除中断标志位(因为初始化就会中断一次)TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE )
开启 TIM2 中断- NVIC 分组 配置 初始化
TIM_Cmd(TIM2, ENABLE)
使能- 中断函数
TIM_GetCounter(TIM2)
获取计数器的值(本例 0-9999)TIM_PrescalerConfig(TIM2, Prescaler, update/immediate)
设置 psc
c
#include "stm32f10x.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
void TIM_Configuration(void);
void GPIO_Configuration(void);
int main(void)
{
GPIO_Configuration();
TIM_Configuration();
while (1)
{
}
}
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 9999;
TIM_TimeBaseStructure.TIM_Prescaler = 7199;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
GPIOC->ODR ^= GPIO_Pin_13;
}
}
PWM
输出比较
- 概念
- 将计数器的值放到比较器里比较 就能输出 pwm
- 计算
- 频率 等于计数器更新频率
- 占空比 crr / 重装个数(arr+1)
- 分辨率 1/(arr+1)
- 流程 rcc(tim gpio) 时基单元 输出比较单元 gpio 启动定时器
- 方法
- 时基单元配置 略 arr 设 100
TIM_OCInitTypeDef pwm
TIM_OCStructInit(&pwm)
给结构体赋默认值.TIM_OCMode = TIM_OCMode_PWM1
比较模式.TIM_OCPolarity = TIM_OCPolarity_High
比较极性.TIM_OutputState = TIM_OutputState_Enable
输出使能.TIM_Pulse = 50
ccr
TIM_OC1Init(TIM2, &pwm)
oc1 通道- gpio 模式要用复用推挽输出
TIM_Cmd(TIM2, ENABLE)
使能定时器TIM_SetCompare1(TIM2, compare)
调节占空比TIM_SetCompare1(TIM2, compare)
调节占空比
c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
void GPIO_Configuration(void);
void TIM_Configuration(void);
int main(void)
{
GPIO_Configuration();
TIM_Configuration();
while (1)
{
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 999;
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 499;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
输入捕获
- 电平跳变时,读取 cnt 值计算出频率 占空比 脉冲间隔 电平持续时间
- 测频率
- 高频 测频法 在单位时间 T 得到 N 个上升沿 f = N / T 用之前的方法可以实现
- 低频 测周法 在两个上升沿之间经过了 N 个标准频率 f = fc / N
- 流程 rcc gpio 输入模式 时基单元 输入捕获单元 从模式触发源及其触发操作 开启定时器 读取 ccr 算 fc/N
- 方法
TIM_ICInitTypeDef ic
.TIM_Channel = TIM_Channel_1
通道.TIM_ICFilter = 0x00 / 0xF
最低/高滤波.TIM_ICPolarity = TIM_ICPolarity_Rising
上升沿触发.TIM_ICPrescaler = TIM_ICPSC_DIV1
不分频.TIM_ICSelection = TIM_ICSelection_DirectTI
直连/交叉通道
TIM_ICInit(TIM3, &ic)
初始化TIM_SelectInputTrigger(TIM3, TIM_TSTI1FP1)
从模式触发源TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset)
从模式TIM_Cmd(TIM3, ENABLE)
启动10000 / TIM_GetCapture1(TIM3)
获取频率 来计算TIM_PWMIConfig(TIM3, &ic)
设为 pwmi 模式TIM_GetCapture1(TIM3) / TIM_GetCapture2(TIM3) * 100
获取占空比%
- 测试范围 (15hz, 1KHz)
c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
#include "misc.h"
void GPIO_Configuration(void);
void TIM_Configuration(void);
void NVIC_Configuration(void);
int main(void)
{
GPIO_Configuration();
TIM_Configuration();
NVIC_Configuration();
while (1)
{
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
{
// 处理输入捕获事件
uint16_t capture = TIM_GetCapture1(TIM2);
// 在这里添加处理逻辑,例如计算脉冲宽度等
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
}
}
编码器 TI
- 结构 TI1FP1 TI2FP2 接到编码器接口 然后连时基单元
- 流程 rcc-gpio&tim gpio 输入模式 时基单元 输入捕获单元(滤波器和极性) 编码器接口模式 启动定时器
- 方法
TIM_EncodeInterfaceConfig(TIMx, 模式, 极性1, 极性2S)
- code
- gpio6/7
- arr 65536-1
- psc 0
- 通道 1/2 滤波器 0xF
TIM_EncodeInterfaceConfig(TIM3, TIM_EncoderMode_TI12, Rising,Rising)
- 定时器使能
- TIM_GetCounter(TIM3)
c
#include "stm32f10x.h"
void Encoder_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void Encoder_TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 6;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
int main(void)
{
SystemInit();
Encoder_GPIO_Config();
Encoder_TIM_Config();
while (1)
{
// 读取计数值并计算速度
uint16_t count = TIM_GetCounter(TIM2);
float speed = (float)count / (float)(TIM2->PSC + 1) * (float)SystemCoreClock;
}
}
AFIO 引脚重映射
- 方法
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE)
开时钟GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE)
重映射- 如果碰到调试接口 要先解除调试接口
(GPIO_Remap_SWJ_JTAGDisable, ENABLE)
c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_afio.h"
void GPIO_Configuration(void);
int main(void)
{
GPIO_Configuration();
while (1)
{
// 在这里添加你的代码
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
// 重映射PA8到PB0
GPIO_PinRemapConfig(GPIO_Remap_PA8_AFIO_MCO, ENABLE);
// 配置GPIOA的PA8为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
ADC 模数转换
- 转换模式
- 单次/连续转换
- 触发一次是否一直转换
- 是否扫描模式
- 是否使用多个通道
- 单次/连续转换
- 流程 rcc-gpio&adc&adcclk gpio 接通道 adc 转换器配置 (模拟看门狗和中断) 开启 adc
- 方法
- gpio ain 模式 A0
RCC_APB2PeriphClockCmd(adc1,1)
RCC_ADCCLKConfig(div6)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5)
ADC_InitTypeDef adc
.ADC_Mode = ADC_Mode_Independent
独立模式.ADC_DataAlign = ADC_DataAlign
数据对齐.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None
使用内部软件触发.ADC_ContinuousConvMode = DISABLE
单次转换.ADC_ScanConvMode = DISABLE
非扫描模式.ADC_NbrOfConversion = 1
通道数目 1
ADC_Init(ADC1, &adc)
初始化ADC_Cmd(ADC1,ENABLE)
开启 adcADC_ResetCalibration(ADC1)
复位校准while(ADC_GetResetCalibrationStatus(ADC1))
ADC_StartCalibration(ADC1)
开始校准while(ADC_GetCalibrationStatus(ADC1))
等校准完成ADC_SoftwareStartConvCmd(ADC1, 1)
软件触发转换while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
等待 eoc 置一表示转换完成ADC_GetConversionValue(ADC1)
读取结果 0-4095
c
#include "stm32f10x.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
void ADC_Configuration(void);
uint16_t Read_ADC(void);
int main(void)
{
uint16_t adc_value;
ADC_Configuration();
while (1)
{
adc_value = Read_ADC();
// 在这里处理ADC转换后的数据,例如显示在LCD上或者通过串口发送到计算机等
}
}
void ADC_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
uint16_t Read_ADC(void)
{
return ADC_GetConversionValue(ADC1);
}
DMA 直接寄存器存取
- 流程 rcc-dma dmaInit dma-cmd
- 代码
RCC_AHBPeriphClockCmd(dma1,1)
DMA_InitTypeDef dma
.DMA_PeripheralBaseAddr = arr1
外设起始地址.DMA_PeripheralDataSize = Byte
数据宽度.DMA_PeripheralInc = Enable
是否自增.DMA_MemoryBaseAddr = arr2
存储器起始地址.DMA_MemoryDataSize = Byte
数据宽度.DMA_MemoryInc = Enable
是否自增.DMA_DIR = src
传输方向 外设站点做 src/dst.DMA_BufferSize = sizeof(arr1) / sizeof(arr1[0])
传输计数器(0,65535).DMA_Mode = normal
传输模式(是否自动重装)normal/circular.DMA_M2M = Enable
是否软件触发.DMA_Priority = medium
优先级
DMA_Init(DMA1_Channel1, &dma)
DMA_Cmd(DMA1_Channel1, ENABLE)
- 重新触发转运
DMA_Cmd(DMA1_Channel1, DISABLE)
DMA_SetCurrDataCounter(DMA1_Channel1,size)
重新设定传输寄存器DMA_Cmd(DMA1_Channel1, ENABLE)
while(!DMA_GetFlagStatus(DMA1_FLAG_TC1))
DMA_ClearFlag(DMA1_FLAG_TC1)
c
#include "stm32f10x.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
void DMA_Config(void);
void GPIO_Config(void);
int main(void)
{
GPIO_Config();
DMA_Config();
while (1)
{
}
}
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOA->IDR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0x20000000;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 10;
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_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
ADC + DMA
- adc 使用 扫描模式连续模式
- dma 循环模式
- dma 使用 硬件触发
ADC_DMACmd(ADC1,ENABLE)
c
#include "stm32f10x.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
void ADC_DMA_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能ADC和DMA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 配置ADC引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 初始化ADC
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2; // 设置通道数量
ADC_Init(ADC1, &ADC_InitStructure);
// 配置DMA
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADCConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 2;
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_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// 关联DMA和ADC
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
// 启动ADC转换
ADC_SoftwareStartConv(ADC1);
}
int main(void)
{
uint16_t ADCConvertedValue[2];
while (1)
{
// 等待转换完成
while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
// 获取转换结果
ADCConvertedValue[0] = ADC1->DR;
ADCConvertedValue[1] = ADC1->DR;
}
}
USART 通信
- 特点
- 全双工 异步 单端 点对点
- 由于异步时序对时间要求比较严格 因此用软件模拟串口不合适
- 协议
- 波特率(bps) = f / (16 _ 分频) = 72m / (16 _ 468.75) = 9600
- 起始位(0)
- 数据位(1/0)(低位先行)
- 校验位(奇 0 偶 1 个 1 使得 1 永远为奇数个)(奇校验)(可选)
- 停止位(1)
- 流程
- rcc-usart rcc-gpio 配置 gpio 一个输入一个输出 配置 usart 配置接收中断 开启 usart 调用发送接收函数 获取发送接收标志位函数
- 代码
RCC_APB2PeriphClockCmd(usart1, 1)
- gpio 初始化 pin9 推挽输入 pin10 输出
USART_InitTypeDef usart
.USART_BaudRate = 9600
波特率.USART_HardwareFlowControl = USART_HardwareFlowControl_None
不使用流控.USART_Mode = USART_Mode_Rx | USART_Mode_Tx
发送和接收.USART_Parity = USART_Parity_No
不使用校验.USART_WordLength = USART_WordLength_8b
USART_Init(USART1, &usart)
- 这里可以选择使用查询或者中断的方法读取信息
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)
开启中断- 配置 nvic
USART_Cmd(USART1, ENABLE)
- 数据包
- hex 数据包
- 文本数据包
- 状态机程序
c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
void USART1_Init(void);
void USART1_SendChar(char ch);
int main(void)
{
USART1_Init();
while (1)
{
char str[] = "Hello, World!";
for (int i = 0; str[i] != '\0'; i++) {
USART1_SendChar(str[i]);
}
USART1_SendChar('\n');
USART1_SendChar('\r');
for (uint32_t i = 0; i < 500000; i++); // 延时
}
}
void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置USART1的TX引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1的RX引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1的波特率、字长、停止位和奇偶校验
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 USART1_SendChar(char ch)
{
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, (uint8_t)ch);
}
c
#include "stm32f10x.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
void USART1_Config(void);
void GPIO_Config(void);
void NVIC_Config(void);
void USART1_IRQHandler(void);
int main(void)
{
GPIO_Config();
USART1_Config();
NVIC_Config();
while (1)
{
}
}
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART1_Config(void)
{
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
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);
USART_Cmd(USART1, ENABLE);
}
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
uint8_t data = USART_ReceiveData(USART1);
USART_SendData(USART1, data);
}
}
c
void sendByte(uint8_t byte)
{
USAERT_SendData(USART1, byte);
while (!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
}
void sendArr(uint8_t *arr, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
sendByte(arr[i]);
}
void sendStr(char *str)
{
for (uint8_t i = 0; str[i]; i++)
sendByte(str[i]);
}
void sendNum(int num, uint8_t len)
{
for (uint8_t i = 0; i < len; i++)
sendByte(num / pow(10, len - i - 1) % 10 + '0');
}
// printf重定向到串口
void fputc(int ch, FILE *f)
{
sendByte(ch);
return ch;
}
void fhfa2()
{
char str[100];
sprintf(str, "hello, %d", 123);
sendStr(str);
}
c
extern int data
// 查询法
while(1)
{
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
data = USART_ReceiveData(USART1);
}
// 中断法
void USART1_IRQHandler()
{
if(USART_GetITStatus(USART1, USART_FLAG_RXNE))
{
data = USART_ReceiveData(USART1)
// USART_ClearITPendingBit(USART1, USART_FLAG_RXNE) //读取可不写
}
}
// 状态机
if(state == 0)
{
if(data == 0xFF)
{
// 收到包头
state = 1;
i = 0;
}
}
else if(state == 1)
{
// 包内容
rxArr[i++] = data;
if(i >= 4)
state = 2;
}
else if(state ==2)
{
if(data==0xFE)
state = 0 //收到包尾
}
变形
- RS232
- 接 232 芯片把 0-5v 转化成-15-15v
- RS485
- 接 485 芯片把信号转变成差分信号
- 半双工 因为发送和接收共用两根线
- 一主多从
IIC I2C 通信
- 特点
- 两根通信线
- 同步 半双工 含数据应答
- 支持挂载多设备(老师讲课学生听,点名了才能讲话)
- 协议
- 硬件 开漏输出+上拉电阻(4.7k)防止短路
- 软件
- 起始结束 高时钟下 下开上关
- 读写时 低变高读
- scl 拉低时改变 sda,scl 松手时读取 sda (高位先行)
- 应答 发完一个字节后 下一个时钟接收 1 位 0 表应答
- 第一个字节为指定(设备/寄存器)地址 7 位地址+1 位读/写
- mpu6050
- 流程
- rcc-gpio 时序基本单元 驱动从机 去指定寄存器拿数据
c
void iicInit()
{
// gpio 10 11 开漏输出
// 置高电平
}
void writeSCL(char v)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (Bit_Action)v);
// delay
}
void writeSDA(char v)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (Bit_Action)v);
// delay
}
char readSCL()
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10);
}
char readSDA()
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
}
void iicStart()
{
writeSDA(1);
writeSCL(1);
writeSDA(0);
writeSCL(0);
}
void iicStop()
{
writeSDA(0);
writeSCL(1);
writeSDA(1);
}
void iicSend(char byte)
{
for (char i = 0; i < 8; i++)
{
writeSDA(byte & (0x80 >> i));
writeSCL(1);
writeSCL(0);
}
}
char iicRCV()
{
char byte = 0x00;
writeSDA(1);
for (char i = 0; i < 8; i++)
{
writeSCL(1);
if (readSDA())
byte |= (0x80 >> i);
writeSCL(0);
}
return byte;
}
void iicSendAck(char ack)
{
writeSDA(ack);
writeSCL(1);
writeSCL(0);
}
char iicRCVAck()
{
char ack;
writeSDA(1);
writeSCL(1);
ack = readSDA();
writeSCL(0);
return ack;
}
c
// 指定地址写
void writeReg(char regAddr, char data)
{
iicStart();
iicSend(iicAddr);
iicRCVAck();
iicSend(regAddr);
iicRCVAck();
// 这里可以加循环
iicSend(data);
iicRCVAck();
iicStop();
}
// 指定地址读
void readReg(char regAddr)
{
char data;
iicStart();
iicSend(iicAddr);
iicRCVAck();
iicSend(regAddr);
iicRCVAck();
iicStart();
iicSend(iicAddr | 0x01);
iicRCVAck();
// 这里可以加循环
data = iicRCV();
iicSendAck(1);
iicStop();
return data;
}
硬件 iic
- 流程
- rcc-iic&gpio gpio 初始化 配置 iic 使能 iic
- 代码
RCC_APB1PeriphClockCmd(i2c2,1)
RCC_APB1PeriphClockCmd(gpiob,1)
- gpio10/11 复用开漏
I2C_InitTypeDef iic
.I2C_Mode = _I2C
模式.I2C_ClockSpeed = 50000
频率.I2C_DutyCycle = _2
占空比I2C_Ack = _Enable
ack.I2C_AcknowledgedAddress = _7bit
7 位地址.I2C_OwnAddress1 = 0x00
自己的地址
I2C_Init(I2C2, &iic)
I2C_Cmd(I2C2, 1)
c
#include "stm32f10x.h"
#include "stm32f10x_i2c.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
void I2C_Configuration(void);
void I2C_Write(uint8_t deviceAddr, uint8_t regAddr, uint8_t data);
uint8_t I2C_Read(uint8_t deviceAddr, uint8_t regAddr);
int main(void)
{
I2C_Configuration();
// 写入数据到I2C设备
I2C_Write(0x68, 0x6B, 0x00); // 向设备地址为0x68的I2C设备的寄存器地址0x6B写入数据0x00
// 从I2C设备读取数据
uint8_t data = I2C_Read(0x68, 0x75); // 从设备地址为0x68的I2C设备的寄存器地址0x75读取数据
while (1)
{
}
}
void I2C_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);
}
void I2C_Write(uint8_t deviceAddr, uint8_t regAddr, uint8_t data)
{
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, deviceAddr, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, regAddr);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2C1, data);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1, ENABLE);
}
uint8_t I2C_Read(uint8_t deviceAddr, uint8_t regAddr)
{
uint8_t data;
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, deviceAddr, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, regAddr);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, deviceAddr, I2C_Direction_Receiver);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
data = I2C_ReceiveData(I2C1);
I2C_NACKPositionConfig(I2C1, I2C_NACKPosition_Current);
I2C_AcknowledgeConfig(I2C1, DISABLE);
I2C_GenerateSTOP(I2C1, ENABLE);
return data;
}
c
// 指定地址写
void writeReg(char regAddr, char data)
{
I2C_GenerateSTART(I2C2, 1);
while(I2C_CheckEvent(I2C2, IEMMS)!=SUCCESS);
I2C_Send7bitAddress(I2C2, 从机地址, 读写位);
while(IEMTMS);
I2C_SendData(I2C2, regAddr)
while(IEMBT);
I2C_GenerateSTOP(I2C2,1);
}
// 指定地址读
void readReg(char regAddr)
{
char data;
I2C_GenerateSTART(I2C2, 1);
while(I2C_CheckEvent(I2C2, IEMMS)!=SUCCESS);
I2C_Send7bitAddress(I2C2, 从机地址, 读写位);
while(IEMTMS);
I2C_SendData(I2C2, regAddr)
while(IEMBT);
I2C_GenerateSTART(I2C2, 1);
while(I2C_CheckEvent(I2C2, IEMMS)!=SUCCESS);
I2C_Send7bitAddress(I2C2, 从机地址, 读写位);
while(IEMTMS);
I2C_AcknowledgeConfig(I2C2, 0);
I2C_GenerateSTOP(I2C2,1);
while(IEMBR);
data = I2C_ReceiveData(I2C2);
I2C_AcknowledgeConfig(I2C2, 1);
return data;
}
SPI 通信
- 特点
- 简单 快速 消耗大
- 同步 全双工
- 协议
- SCK 时钟 MOSI 主发从收 MISO 主收从发 SS 设备选择
- SCK 默认 0 上升沿(第一个沿)采样 下降沿移出 (模式 0)
- ss 0 起始 1 结束
- 时序 指令+数据
- 高位先行
- 软件 spi 翻转/读取 gpio 的电平
- 硬件 spi
- 传输流程
- 等 TXNE 置 1 后 send1 写入数据缓冲寄存器
- MOSI MISO SCK 会自动运行
- 等 RXNE 置 1 后读取 rcv1 写入 send2
- 程序流程 rcc-gpio&spi 初始化 gpio 复用推挽输出&上拉输入 配置 spi 使能
- code
RCC_APB2PeriphClockCmd(_SPI1, ENABLE)
SPI_InitTypeDef spi
.SPI_Mode = _Master
主机.SPI_Direction = _FullDuplex
全双工.SPI_DataSize = _8b
一个字节.SPI_FirstBit = _MSB
高位先行.SPI_BaudRatePrescaler = 128
分频系数.SPI_CPOL = _Low
默认低电平.SPI_CPHA = _1Edge
第一个沿采样 (模式 0).SPI_NSS = _soft
软件 ss.SPI_CRCPolynomial = 7
校验
SPI_Init(SPI1, &spi)
SPI_Cmd(SPI1, 1)
- 传输流程
c
void spiInit()
{
// rcc
// sck mosi ss 推挽输出
// miso 上拉输入
writeSS(1);
writeSCK(1);
}
void writeSCK(char b)
{
// gpioA5 write bit
}
void writeSS(char b) {}
void writeMOSI(char b) {}
char readMISO()
{
// gpioA6 read input data bit
}
void spiStart()
{
writeSS(0);
}
void spiWork(char byte)
{
char data;
for (char i = 0; i < 8; i++)
{
writeMOSI(byte & (0x80 >> i));
writeSCK(1);
if (readMISO())
data |= (0x80 >> i);
writeSCK(0);
}
return data;
}
char spiSwap(char byte)
{
for (char i = 0; i < 8; i++)
{
writeMOSI(byte & 0x80);
byte << 1;
writeSCK(1);
if (readMISO())
byte |= 0x01;
writeSCK(0);
}
return byte;
}
void readID(char *mid, short *did)
{
spiStart();
spiSwap(0x9F); // 指令码
*mid = spiSwap(0xff);
*did = spiSwap(0xff);
*did <<= 8;
*did |= spiSwap(0xff);
writeSS(1); // stop
}
void writeEnable()
{
spiStart();
spiSwap(enable); // 指令
writeSS(1);
}
void waitBusy()
{
int timeout;
spiStart();
spiSwap(readStatus);
while ((spiSwap(0xff) & 0x01) == 0x01)
{
if (++timeout > 100000)
break;
}
writeSS(1);
}
void writeDisk(int addr, char *arr, short count)
{
writeEnable();
spiStart();
spiSwap(0x02); // 指令码
spiSwap(addr >> 16); // 24位字节的高8位
spiSwap(addr >> 8); // 中8
spiSwap(addr); // 低8
for (char i = 0; i < count; i++)
{
spiSwap(arr[i]);
}
writeSS(1);
waitBusy();
}
void readDisk(int addr, char *arr, int count)
{
spiStart();
spiSwap(read_data); // 指令码
spiSwap(addr >> 16); // 24位字节的高8位
spiSwap(addr >> 8); // 中8
spiSwap(addr); // 低8
for (char i = 0; i < count; i++)
arr[i] = spiSwap(0xff);
writeSS(1);
}
c
#include "stm32f10x.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
void SPI_Configuration(void);
void GPIO_Configuration(void);
int main(void)
{
GPIO_Configuration();
SPI_Configuration();
while (1)
{
// 发送数据
SPI_I2S_SendData(SPI1, 0x55);
// 等待发送完成
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
// 延时
for (int i = 0; i < 1000000; i++);
}
}
void SPI_Configuration(void)
{
SPI_InitTypeDef SPI_InitStructure;
// 使能SPI1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// 配置SPI1
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
// 使能SPI1
SPI_Cmd(SPI1, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIOA的PA5、PA6、PA7为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
// swap
char spiSwap(char send)
{
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
SPI_I2S_SendData(SPI1,send);
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
return SPI_I2S_ReceiveData(SPI1);
}
CAN
- 特点半双工 异步 差分 多设备
- 0为显性电平 即拉开两线电压 1为隐形电平 即松开电压
- 帧格式
- 空闲1
- SOF起始 0
- ID *11
- SRR无意义
- IDE 0标准1扩展
- ID *18
- RTR 数据帧0 遥控帧1
- IDE(R1) 0标准1扩展
- R0 保留
- DLC 长度 0000-1000 *4
- Data *0-64 高位先行
- CRC校验 *15
- 1
- 1 ACK 接收方0表示收到
- 1
- EOF 1 *7
- 遥控帧相比没有数据段 错误帧略 过载帧略 帧间隔1 *3+
- 位填充
- 每五个相同电平追加一个反向电平
- 有利于再同步
- 与错误帧区分
- 防止被误认为空闲
c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_can.h"
void CAN_Config(void);
void NVIC_Config(void);
void SendData(uint8_t data);
int main(void)
{
NVIC_Config();
CAN_Config();
while (1)
{
SendData(0x55);
for (uint32_t i = 0; i < 0xFFFFF; i++); // 延时
}
}
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void CAN_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_4tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;
CAN_InitStructure.CAN_Prescaler = 5;
CAN_Init(CAN1, &CAN_InitStructure);
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
}
void SendData(uint8_t data)
{
CanTxMsg TxMessage;
TxMessage.StdId = 0x321;
TxMessage.RTR = CAN_RTR_DATA;
TxMessage.IDE = CAN_ID_STD;
TxMessage.DLC = 1;
TxMessage.Data[0] = data;
CAN_Transmit(CAN1, &TxMessage);
}
WDG
- IWDG 独立看门狗
IWDG_WriteAccessCmd( IWDG_WriteAccess_Enable );
寄存器可写IWDG_SetPrescaler( prv );
预分频器值IWDG_SetReload( rlv );
重装寄存器值IWDG_ReloadCounter();
把重装值放入计数器IWDG_Enable();
使能IWDG_ReloadCounter();
喂狗RCC_GetFlagStatus(RCC_FLAG_IWDGRST)
复位标志RCC_ClearFlag();
清除标志
- WWDG 窗口看门狗
c
#include "stm32f10x.h"
#include "stm32f10x_iwdg.h"
#include "stm32f10x_rcc.h"
void IWDG_Configuration(void)
{
// 开启独立看门狗时钟
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
// 设置看门狗预分频值
IWDG_SetPrescaler(IWDG_Prescaler_64);
// 设置看门狗重加载值,用于喂狗
IWDG_SetReload(2000);
// 使能看门狗
IWDG_Enable();
}
int main(void)
{
// 初始化看门狗
IWDG_Configuration();
while (1)
{
// 喂狗
IWDG_ReloadCounter();
}
}
c
#include "stm32f10x.h"
#include "stm32f10x_wwdg.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_exti.h"
#include "misc.h"
void WWDG_Configuration(void)
{
// 使能窗口看门狗时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
// 设置窗口看门狗的计数值
WWDG_SetCounter(0x7F);
// 设置窗口看门狗的窗口值
WWDG_SetWindowValue(0x50);
// 使能窗口看门狗中断
WWDG_EnableIT();
// 使能窗口看门狗
WWDG_Enable(0x7F);
}
int main(void)
{
// 初始化窗口看门狗
WWDG_Configuration();
while (1)
{
// 喂狗
WWDG_SetCounter(0x7F);
}
}
BKP&RTC
c
int main()
{
RCC_APB1PeriphClockCmd(pwr,1);
RCC_APB1PeriphClockCmd(bkp,1);
PWR_BackupAccessCmd(1);
// 写
BKP_WriteBackupRegister(BKP_DR1,0x1234);
// 读
BKP_ReadBackupRegister(BKP_DR1);
}
c
int main()
{
RCC_APB1PeriphClockCmd(pwr,1);
RCC_APB1PeriphClockCmd(bkp,1);
PWR_BackupAccessCmd(1);
if(BKP_ReadBackupRegister(BKP_DR1) != 0x1234)
{
RCC_LSEConfig(on);
while(RCC_GetFlagStatus(lserdy) != SET);
RCC_RTCCLKConfig(sourse_lse);
RCC_RTCCLKCmd(1);
RTC_WaitForSynchro();
RTC_WaitForLastTask();
// 分频
RTC_SETPrescler(32768-1);
RTC_WaitForLastTask();
// 初始时间戳
// rtc 数据存在bfc 所以不需要另外设置
RTC_SetCounter(1672588795);
RTC_WaitForLastTask();
BKP_WriteBackupRegister(BKP_DR1,0x1234)
}
else
{
RTC_WaitForSynchro();
RTC_WaitForLastTask();
}
while(1)
{
// 获取当前值
RTC_GetCounter()
// 使用time.h中的 mktime() localtime() 做时间戳到日期的相互转换
}
}
c
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rtc.h"
#include "stm32f10x_pwr.h"
void RCC_Configuration(void);
void GPIO_Configuration(void);
void RTC_Configuration(void);
void BPK_Configuration(void);
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
RTC_Configuration();
BPK_Configuration();
while (1)
{
}
}
void RCC_Configuration(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void RTC_Configuration(void)
{
RTC_InitTypeDef RTC_InitStructure;
RTC_StructInit(&RTC_InitStructure);
RTC_InitStructure.RTC_AsynchPrediv = 0x7F;
RTC_InitStructure.RTC_SynchPrediv = 0xFF;
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
RTC_Init(&RTC_InitStructure);
}
void BPK_Configuration(void)
{
PWR_BackupAccessCmd(ENABLE);
BKP_DeInit();
RCC_BackupResetCmd(ENABLE);
RCC_BackupResetCmd(DISABLE);
}
PWR 电源管理
- 睡眠 wfi wfe
- 停机 STOPMode
- 待机 STANDBYMode
c
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //开启PWR的时钟
void sleep_mode_open(void)
{
__WFI();// 进入睡眠模式
}
void stop_mode_open(void)
{
PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI); //进入停止模式
}
void standby_mode_open(void)
{
PWR_WakeUpPinCmd(ENABLE); //唤醒引脚使能,不需要gpio初始化,函数内部已实现
PWR_EnterSTANDBYMode(); //进入待机模式
}
Flash
c
#include "stm32f10x.h"
#include "stm32f10x_flash.h"
#define FLASH_START_ADDR 0x08000000 // Flash起始地址
#define FLASH_END_ADDR 0x0800FFFF // Flash结束地址
#define FLASH_PAGE_SIZE 1024 // Flash页大小
void FLASH_Write(uint32_t address, uint32_t data) {
FLASH_Unlock(); // 解锁Flash
FLASH_ErasePage(address); // 擦除指定地址所在的页
FLASH_ProgramWord(address, data); // 写入数据到指定地址
FLASH_Lock(); // 锁定Flash
}
uint32_t FLASH_Read(uint32_t address) {
return (*(__IO uint32_t*)address); // 从指定地址读取数据
}
int main(void) {
uint32_t writeData = 0x12345678; // 要写入的数据
uint32_t readData; // 用于存储读取到的数据
FLASH_Write(FLASH_START_ADDR, writeData); // 将数据写入Flash
readData = FLASH_Read(FLASH_START_ADDR); // 从Flash中读取数据
while (1) {
// 主循环
}
}