Skip to content

STM32

STM32

引脚定义

环境搭建

  • 系统启动相关
  • 标准库函数
  • 库函数配置
  • main

GPIO 输入输出

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 外部中断

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) 初始化 EXTI
    • NVIC_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 定时器

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

输出比较

  • 概念
    • 将计数器的值放到比较器里比较 就能输出 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);
}

输入捕获

pwm2

  • 电平跳变时,读取 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 模数转换

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) 开启 adc
    • ADC_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 直接寄存器存取

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 通信

uart

  • 特点
    • 全双工 异步 单端 点对点
    • 由于异步时序对时间要求比较严格 因此用软件模拟串口不合适
  • 协议
    • 波特率(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

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)

spi

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) {
        // 主循环
    }
}