一、GPIO的使用

image.png
  • RCC配置
    1
    2
    3
    4
    RCC_AHBPeriphClockCmd() %外设时钟控制
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE) %GPIOA时钟控制
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE) %AFIO时钟控制
    RCC_APB1PeriphClockCmd() %APB1时钟控制

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
28
29
30
GPIO_DeInit(GPIOA) %GPIO复位
GPIO_AFIODeInit() %AFIO复位

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
GPIO_Init(GPIOA, GPIO_InitStructure) %GPIO初始化

GPIO_Mode_Out_PP %推挽输出
GPIO_Mode_Out_OD %开漏输出
GPIO_Mode_AF_OD %复用开漏输出
GPIO_Mode_AF_PP %复用推挽输出

GPIO_Mode_IPU %上拉输入
GPIO_Mode_IPD %下拉输入
GPIO_Mode_IN_FLOATING %浮空输入
GPIO_Mode_AIN %模拟输入

GPIO_Pin_8
GPIO_Speed_50MHz

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_8) %GPIO读取单个引脚
GPIO_ReadInputData() %GPIO读取整个引脚
GPIO_ReadOutputDataBit() %GPIO读取单个引脚输出数据
GPIO_ReadOutputData() %GPIO读取整个引脚输出数据

%GPIO写入%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
GPIO_SetBits(GPIOx, GPIO_Pin_8); %GPIOx写1
GPIO_ResetBits(GPIOx, GPIO_Pin_8); %GPIOx写0
GPIO_WriteBit(GPIOx, GPIO_Pin_8, Bit_RESET\Bit_SET);%GPIOx写01
GPIO_Write(GPIOx, PortVal);

GPIO重映射

  • 开启AFIO时钟,开启重映射, 参数1参考stmf10xx参考手册完全重映射与部分重映射映射的引脚不同,有些引脚有默认功能不能重映射,需要先重映射解除默认功能,下面的调试端口不能同时解除默认功能,使用PA15,PB3,PB4解除JTAG,保留SWD image.png
1
2
3
4
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE) %AFIO时钟控制
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE)
//GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE);
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
  • 初始化重映射之后的GPIO

二、系统中断

NVIC是内部用于中断排序的系统,stm32f10系列有68个可屏蔽中断通道,16个可屏蔽通道

中断优先级

抢占优先级可以进行嵌套,可以不等程序完成,直接让CPU执行中断程序。响应优先级是必须等程序执行完成,在响应中断程序。

NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队 image.png

EXTI外部中断

EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序 - 支持的触发方式:上升沿/下降沿/双边沿/软件触发 - 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断 - 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒 - 触发响应方式:中断响应/事件响应(转到外设处理中断)

image.png|650 AFIO只能选择一组GPIO口所以不能同时触发相同Pin的中断。EXTI9-5,EXTI15-10会触发同一个中断函数。20是触发外部中断

配置步骤 - 配置RCC,打开外设时钟,只用打开GPIO与AFIO时钟,EXTI与NVIC已经默认打开 - 配置GPIO,设置为输入模式

1
2
3
4
5
6
7
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
  • 配置AFIO,选择GPIO口
1
GPIO_EXTILineConfig( GPIO_PortSourceGPIOC, GPIO_PinSource1);
  • 配置EXTI,选择边沿触发方式,触发响应方式,中断响应或者事件响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
EXTI_InitTypeDef EXTI_InitStruct;

EXTI_InitStruct.EXTI_Line = EXTI_Linex;
// EXTI_Linex 使用的pin口
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;

// EXTI_Mode_Interrupt 中断模式
// EXTI_Mode_Event 事件模式

EXTI_InitStruct.EXTI_Trigger = ;
// EXTI_Trigger_Rising 上升沿触发
// EXTI_Trigger_Falling 下升沿触发
// EXTI_Trigger_Rising_Falling 上下沿都触发
EXTI_Init(&EXTI_InitStruct);
  • 配置NVIC,设置优先级
1
2
3
4
5
6
7
8
9
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_x); //设置分组方式,整个工程只设置1个
// NVIC_PriorityGroup_x
NVIC_InitTypeDef NVIC_InitStructure; //中断初始化
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //设置中断通道,使用哪个引脚使用哪个
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //设置响应优先级
NVIC_Init(&NVIC_InitStructure);

  • 中断函数
1
2
3
4
5
6
7
8
9
10
void EXTI15_10_IRQHandler(void) //每个通道的中断函数名确定的,从启动文件里找
{
if(EXTI_GetITStatus(EXTI_Line1) == SET) //判断通道标志位是否为1
{

}
EXTI_ClearITPendingBit(EXTI_Line14); //清除标志位

}

定时器

定时器介绍

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型 image.png

STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4

定时器结构图 image.png

基本定时器,接受系统主频时钟72MHZ,经过分频后,上升沿触发计数器计数,当计数器达到自动重装载寄存器的储存值的时候,会触发定时器中断(ui)。预分配器可设为0-65535,对应1-65536分频。基本定时器只能向上触发。

主模式触发功能是指当计数器达到自动重装载寄存器的储存值的时候,定时器进行事件更新(u),然后映射到触发输出TRGO,TRGO触发DAC,不需要cup参与 image.png

通用/高级定时器有三种计数模式 - 向上计数,从0增加到设定值触发中断 - 向下计数,从设定值增减小到0触发中断 - 中央对齐模式,从0增加到设定值触发中断,从设定值增减小到0再次触发中断

ETR可以作为外部时钟输入,被称为外部时钟模式2,经过极性选择,输入滤波等处理后,作为系统时钟,引脚定义可以看哪些引脚有ETR。

TRGI可以作为外部时钟输入,被称为外部时钟模式1,TRGI的输入有 - ETR,ITR1-ITR3,ITR1-ITR3是来自其他定时器的触发器输出TRG0,从而实现定时器的级联。 - CH1, CH2 - TL1FP1, TL1FP2

下面的功能是输入捕获,输出比较,二者不能同时使用,公用寄存器 image.png

  • 重复次数计数器,实现每隔几个周期再进行更新
  • 驱动三相无刷电路,互补PWM输出
  • 刹车输入

计数器时序图 image.png

  • CK_PSC 时钟信号
  • CNT_EN 计数器使能,为1计数器打开
  • CK_CNT 分频后的时钟信号
  • 计数器寄存器上升沿计数,达到FC重新装载
  • 预分频寄存器是用户写入的分配系数,当前计数未达到更新值前,就算改变了分频系数,下次计数才会生效。

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1) 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = (72MHZ / (PSC + 1) ) * (ARR + 1) image.png

  • 无预装值,在计数到33更改计数器重装载值为36,在36处触发中断 image.png
  • 有预装值,在计数到F2时计数器重装载值从F5更改为36,但还是在F5处触发中断,并下个周期更改为36

定时器中断

image.png - 开启RCC时钟

1
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启时钟

  • 选择时基时钟单元的时钟源(内部时钟源)

    1
    TIM_InternalClockConfig(TIM2); //内部时钟

  • 配置预分频器,自动重装载器,计数模式等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;

    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //配置采样分配,随便选一个就行
    // TIM_CKD_DIV1,配置采样分配,随便选一个就行
    // TIM_CKD_DIV2
    // TIM_CKD_DIV4
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
    // TIM_CounterMode_Up 向上计数
    // TIM_CounterMode_Down 向下计数
    // TIM_CounterMode_CenterAligned1 中央对其模式1
    // TIM_CounterMode_CenterAligned1 中央对其模式2
    // TIM_CounterMode_CenterAligned1 中央对其模式3
    TIM_TimeBaseInitStruct.TIM_Period = 10000 -1; //ARR自动重装载值
    //ARR自动重装载值 0x0000 and 0xFFFF
    TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1; //PSC预分频值
    //PSC预分频值 0x0000 and 0xFFFF
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; // 重复计数器的值只有TIM1有
    // 重复计数器的值只有TIM1有 0x0000 and 0xFFFF
    TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 上电后立即清除定时器中断标志位,防止上电进入中断
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);

  • 配置输出中断控制,允许更新中断输出到NVIC

    1
    2
    3
    4
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //允许更新中断输出到NVIC
    // TIMx
    // TIM_IT_Update 更新中断模式
    // ENABLE

  • 配置NVIC,打开定时器中断通道,分配优先级

    1
    2
    3
    4
    5
    6
    7
    8
    9
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_x); //设置分组方式,整个工程只设置1个
    // NVIC_PriorityGroup_x
    NVIC_InitTypeDef NVIC_InitStructure; //中断初始化
    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //设置中断通道,使用哪个引脚使用哪个
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //设置响应优先级
    NVIC_Init(&NVIC_InitStructure);

  • 使能定时器

    1
    TIM_Cmd(TIMx, ENABLE); // 使能定时器

  • 定时器中断函数,一般放在使用它的地方而不是在定时器函数中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void TIM2_IRQHandler(void)
    {
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {

    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }

    }

定时器外部时钟

  • 开启外部GPIO口

    1
    2
    3
    4
    5
    6
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

  • 开启RCC时钟

    1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启时钟

  • 选择外部时钟源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x02); //选择ETR通过外部时钟模式2的输入时钟
    //TIMx

    //TIM_ExtTRGPSC_OFF 不分频
    //TIM_ExtTRGPSC_DIV2 2分频
    //TIM_ExtTRGPSC_DIV4 4分频
    //TIM_ExtTRGPSC_DIV8 8分频

    //TIM_ExtTRGPolarity_Inverted 上升沿触发
    //TIM_ExtTRGPolarity_NonInverted 下升沿触发

    // 0x00 and 0x0F 设置采样频率,几次采样一致后再作为时钟源输入

  • 其他参考上面的

定时器输出PWM

### 原理 OC(Output Compare)输出比较 - 输出比较可以通过比较CNT(计数器寄存器)与CCR(捕获比较寄存器)寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形 - 每个高级定时器和通用定时器都拥有4个输出比较通道 - 高级定时器的前3个通道额外拥有死区生成和互补输出的功能

周期:(ARR+1) * PSC/72000000 频率:72000000/(ARR+1) * PSC image.png image.png

工作流程:: 当CNT>=CCR时oc1ref会输出信号,给CC1P寄存器写0,从上面输出,电平信号不反转,给CCIP寄存器写1,从下面输出,电平信号反转。CC1E控制输出使能,要不要输出。

输出控制寄存器模式: image.png

  • 冻结: CNT,CCR无效,PWM输出停止,保持为暂停时刻状态
  • CNT=CCR时,置高电平,用途不大,只能一次性使用
  • CNT=CCR时,置低电平,用途不大,只能一次性使用
  • CNT=CCR时,电平翻转,PWM占空比设置为50%时适合使用
  • CNT,CCR无效,PWM输出停止,保持为低电平状态
  • CNT,CCR无效,PWM输出停止,保持为高电平状态
  • PWM频率和占空比都可调模式,多用向上计数模式
  • PWM频率和占空比都可调模式

代码

image.png image.png|500

PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1) PWM占空比: Duty = CCR / (ARR + 1) PWM分辨率: Reso = 1 / (ARR + 1)

  • 开启外部GPIO口为复用推挽输出,看文档哪个口有定时器功能

    1
    2
    3
    4
    5
    6
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

  • 开启RCC时钟

    1
    RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM1, ENABLE); //开启时钟

  • 选择时基时钟单元的时钟源(内部时钟源)

    1
    TIM_InternalClockConfig(TIM2); //内部时钟

  • 配置预分频器,自动重装载器,计数模式等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //配置采样分配,随便选一个就行
    // TIM_CKD_DIV1,配置采样分配,随便选一个就行
    // TIM_CKD_DIV2
    // TIM_CKD_DIV4
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
    // TIM_CounterMode_Up 向上计数
    // TIM_CounterMode_Down 向下计数
    // TIM_CounterMode_CenterAligned1 中央对其模式1
    // TIM_CounterMode_CenterAligned1 中央对其模式2
    // TIM_CounterMode_CenterAligned1 中央对其模式3
    TIM_TimeBaseInitStructure.TIM_Period = 10000 -1; //ARR自动重装载值
    //ARR自动重装载值 0x0000 and 0xFFFF
    TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //PSC预分频值
    //PSC预分频值 0x0000 and 0xFFFF
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器的值只有TIM1有
    // 重复计数器的值只有TIM1有 0x0000 and 0xFFFF
    //TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 上电后立即清除定时器中断标志位,防止上电进入中断
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

  • 配置输出控制寄存器模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    TIM_OCInitTypeDef TIM_OCInitStucture
    TIM_OCStructInit(&TIM_OCInitStucture); //结构体赋默认值,防止使用高级定时器某些变量没赋值
    TIM_OCInitStucture.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式
    // TIM_OCMode_PWM1 PWM输出模式1
    TIM_OCInitStucture.TIM_OCNPolarity = TIM_OCNPolarity_High; //输出比较极性
    // TIM_OCNPolarity_High 输出高电平
    // TIM_OCNPolarity_Low 输出低电平
    TIM_OCInitStucture.TIM_OutputState = TIM_OutputState_Enable; //输出使能
    // TIM_OutputState_Enable 使能
    // TIM_OutputState_Disable 不使能
    TIM_OCInitStucture.TIM_Pulse = ; //CCR配置
    // 0x0000 and 0xFFFF
    TIM_OC1Init(TIM1, &TIM_OCInitStucture); //TIM1的通道1初始化,频率相同,占空比不同
    TIM_OC2Init(TIM1, &TIM_OCInitStucture); //TIM1的通道2初始化,
    TIM_OC3Init(TIM1, &TIM_OCInitStucture); //TIM1的通道3初始化,
    TIM_OC4Init(TIM1, &TIM_OCInitStucture); //TIM1的通道4初始化,

  • 使能定时器

    1
    2
    //TIM_CtrlPWMOutputs(TIM1, ENABLE); 高级定时器输出使能
    TIM_Cmd(TIM1, ENABLE); // 使能定时器

  • 单独配置CCR

    1
    2
    3
    4
    TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1); //通道1CCR配置
    TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2); //通道1CCR配置
    TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3); //通道1CCR配置
    TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4); //通道1CCR配置

定时器输入捕获

IC(Input Capture)输入捕获 - 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 - 每个高级定时器和通用定时器都拥有4个输入捕获通道 - 可配置为PWMI模式,同时测量频率和占空比 - 可配合主从触发模式,实现硬件全自动测量 image.png - 测频法适合高频信号 - 测周法适合低频信号, \(t = 1/f_c\), \(T = N/f_c\) , \(F = f_c/N\)
- 中界频率区别高低频频率的零界点

硬件电路

image.png - ICF配置滤波器的频率以及采样次数 - CC1P配置是上升沿还是下降沿触发,得到TT1P1信号 - CC1S是数据选择器,选择输入数据 - ICPS配置分配器 - CC1E使能输出 - 通过主从触发模式可以自动重装载CCR,TI1FP1,可以作为触发源触发定时器的从模式功能,只能在通道1和通道2使用 - 这里定时器的通道1与通道2的TI2FP1,TI2FP2可以选择交叉还是直连模式,交叉是通道1的TI2FP1接到通道2的TI1

主从触发模式

image.png

代码

image.png - 开启RCC与GPIO时钟

1
2
RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM1, ENABLE); //开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启时钟

  • 选择时基时钟单元的时钟源(内部时钟源)

    1
    TIM_InternalClockConfig(TIM2); //内部时钟

  • GPIO初始化,输入模式,上拉模式或者浮空输入模式

    1
    2
    3
    4
    5
    6
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

  • 配置预分频器,自动重装载器,计数模式等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //配置采样分配,随便选一个就行
    // TIM_CKD_DIV1,配置采样分配,随便选一个就行
    // TIM_CKD_DIV2
    // TIM_CKD_DIV4
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
    // TIM_CounterMode_Up 向上计数
    // TIM_CounterMode_Down 向下计数
    // TIM_CounterMode_CenterAligned1 中央对其模式1
    // TIM_CounterMode_CenterAligned1 中央对其模式2
    // TIM_CounterMode_CenterAligned1 中央对其模式3
    TIM_TimeBaseInitStructure.TIM_Period = 10000 -1; //ARR自动重装载值
    //ARR自动重装载值 0x0000 and 0xFFFF
    TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //PSC预分频值
    //PSC预分频值 0x0000 and 0xFFFF
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器的值只有TIM1有
    // 重复计数器的值只有TIM1有 0x0000 and 0xFFFF
    //TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 上电后立即清除定时器中断标志位,防止上电进入中断
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

  • 配置输入捕获单元

    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_ICInitStructture
    TIM_ICInitStructture.TIM_Channel = TIM_Channel_1; //配置输入捕获通道
    // TIM_Channel_1 输入通道1
    // TIM_Channel_2 输入通道2
    // TIM_Channel_3 输入通道3
    // TIM_Channel_4 输入通道4
    TIM_ICInitStructture.TIM_ICFilter = 0xF; //配置输入捕获滤波器
    // 0x0-0xF
    TIM_ICInitStructture.TIM_ICPolarity = TIM_ICPolarity_Rising; //配置输入极性,上升沿触发还是下降沿触发
    // TIM_ICPolarity_Rising 上升沿触发
    // TIM_ICPolarity_Falling 下降沿触发
    // TIM_ICPolarity_BothEdge 都触发
    TIM_ICInitStructture.TIM_ICPrescaler = TIM_ICPSC_DIV1; //分频器
    // TIM_ICPSC_DIV1 不分频
    // TIM_ICPSC_DIV2 2分频
    // TIM_ICPSC_DIV4 4分频
    // TIM_ICPSC_DIV8 8分频
    TIM_ICInitStructture.TIM_ICSelection = TIM_ICSelection_DirectTI; //配置数字选择器,选择是交叉还是直连通道
    // TIM_ICSelection_DirectTI 直连通道
    // TIM_ICSelection_IndirectTI 交叉通道
    // TIM_ICSelection_TRC
    TIM_ICInit(TIM3, &TIM_ICInitStructture);

  • 选择从模式触发源为TL1FP1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
    // TIM_TS_ITR0
    // TIM_TS_ITR1
    // TIM_TS_ITR2
    // TIM_TS_ITR3
    // TIM_TS_TI1F_ED
    // TIM_TS_TI1FP1
    // TIM_TS_TI2FP2
    // TIM_TS_ETRF

  • 配置从模式为reset

    1
    2
    3
    4
    5
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
    // TIM_SlaveMode_Reset
    // TIM_SlaveMode_Gated
    // TIM_SlaveMode_Trigger
    // TIM_SlaveMode_External1

  • 开启定时器

    1
    TIM_Cmd(TIM3, ENABLE); // 使能定时器; //内部时钟
    image.png

  • PWMI是使用了两个通道,通道1采用上升沿触发,通道2采用下降沿触发,下降沿触发时不会清零CNT,CNT计入CCR2,上升沿到来,CNT计入CCR1同时CNT清零。CCR2/CCR1即使占空比。

定时器编码器接口

编码器接口 - 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度 - 每个高级定时器和通用定时器都拥有1个编码器接口 - 两个输入引脚借用了输入捕获的通道1和通道2 image.png image.png image.png - 仅在TI1计数是,只在A相的上升沿或者下降沿进行计数,正向都向上计数,反向都向下计数 - 仅在TI2计数是,只在A相的上升沿或者下降沿进行计数 - TI1和TI2计数是,在A相的上升沿以及B相的上升沿进行计数或者,最常使用的模式

  • 当TI1FP1为上升沿有效时,TI1FP2为低电平,CCR计数器加一,对应正转。TI1FP2为高电平,CCR计数器减一,对应反转转。通道1设置为上升沿有效,通道2设置为上升沿有效,

代码

image.png - 开启GPIO与定时器时钟 - 配置GPIO - 配置时基单元,不分频,ACC为65536 - 配置输入捕获单元,滤波器与极性 - 配置编码器接口模式 - 启动定时器

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
	GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_ICInitTypeDef TIM_ICInitStructture;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //配置采样分配,随便选一个就行
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
TIM_TimeBaseInitStructure.TIM_Period = 65536 -1; //ARR自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC预分频值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器的值只有TIM1有
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

TIM_ICStructInit(&TIM_ICInitStructture);
TIM_ICInitStructture.TIM_Channel = TIM_Channel_1; //配置输入捕获通道
TIM_ICInitStructture.TIM_ICFilter = 0xF; //配置输入捕获滤波器
TIM_ICInitStructture.TIM_ICPolarity = TIM_ICPolarity_Rising; //配置输入极性,上升沿触发还是下降沿触发
TIM_ICInit(TIM3, &TIM_ICInitStructture);

TIM_ICInitStructture.TIM_Channel = TIM_Channel_2; //配置输入捕获通道
TIM_ICInitStructture.TIM_ICFilter = 0xF; //配置输入捕获滤波器
TIM_ICInitStructture.TIM_ICPolarity = TIM_ICPolarity_Rising; //配置输入极性,上升沿触发还是下降沿触发
TIM_ICInit(TIM3, &TIM_ICInitStructture);

TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
// TIMx

// TIM_EncoderMode_TI1 TI1计数
// TIM_EncoderMode_TI2 TI1计数
// TIM_EncoderMode_TI12 TI1与TI2都计数

// TIM_ICPolarity_Rising 通道不反向,配置通道1的触发模式,和配置输入捕获寄存器一样
// TIM_ICPolarity_Falling 通道反向

// TIM_ICPolarity_Falling 配置通道2的计数模式
// TIM_ICPolarity_Rising
TIM_Cmd(TIM3, ENABLE); // 使能定时器; //内部时钟

ADC

ADC(Analog-Digital Converter)模拟-数字转换器 - ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁 - 12位逐次逼近型ADC,1us转换时间 输入电压范围:03.3V,转换结果范围:04095 - 18个输入通道,可测量16个外部和2个内部信号源 - 规则组和注入组两个转换单元 - 模拟看门狗自动监测输入电压范围 ,自动监测某些传感器的值,达到条件申请中断,不需要主动if - STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道 image.png

ADC原理与结构

image.png|500 image.png - 规则通道可以一次性可以选择16个需要ADC转换的通道,转换完成后返回对应数字量,但规则通道只有16位,放一个数,需要配合DMA来使用。 - 注入通道,一次性可以选择4个需要ADC转换的通道,有4个对应寄存器存放对应值,使用较少 - ADC时钟只能选择6,8分频 - 转换完成标志位置1,可以申请中断 image.png image.png

四种转换模式

连续转换,单次转换,扫描模式,非扫描模式 - 连续转换,扫描模式,选取多个通道,转换每一个通道,标志位置不置1,一直转换 - 连续转换,非扫描模式,选取一个通道,一直转换通道,然后标志位不置1,一直转换 - 单次转换,扫描模式,选取多个通道,只能保留转换的最后一个通道, - 单次转换,非扫描模式,选取一个通道,只能转换一个通道,只转换一次

ADC触发方式

image.png

数据对齐

image.png

采样时间

image.png|550

校准

  • ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差
  • 建议在每次上电后执行一次校准
  • 启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期

代码

单次转换,非扫描模式

  • 开启GPIO,ADC的时钟,分频器
  • 配置GPIO模拟输入
  • 配置多路开关
  • 配置ADC转换器
  • 开启ADC
  • ADC校准
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 AD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
// ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);


ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续还是单次转换
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对其模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发控制源选择
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC独立工作模式或者双ADC模式
ADC_InitStructure.ADC_NbrOfChannel = 1; //扫描模式下的通道数
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式还是非扫描模式
ADC_Init(ADC1, &ADC_InitStructure);

ADC_Cmd(ADC1, ENABLE);

ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);

}


uint16_t AD_GatValue(void)
{
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
16
17
18
19
20
21
22
23
24
25
void AD_Init(void)
{
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 启动连续扫描
ADC_Init(ADC1, &ADC_InitStructure);

ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

}


uint16_t AD_GatValue(void)
{
return ADC_GetConversionValue(ADC1);

}

单次转换,非扫描模式来实现读取多个传感器

1
2
3
4
5
6
7
8
uint16_t AD_GatValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);

}

DMA

原理

DMA(Direct Memory Access)直接存储器存取 - DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源 - 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发
- STM32F103C8T6 DMA资源:DMA1(7个通道) image.png image.png|500 - DMA1有7个通道,每个通道可以设置访问地址,但只有一个DMA总线,只能分时复用,如果两个通道访问同一个地址,则会由仲裁器决定哪一个先访问。 - AHB从设备是CUP来配置DMA,主设备能写从设备,从设备不能写主设备,寄存器啥的都是从设备 image.png - 数据宽度:字节8位,半字16位,字32位 - 传输寄存器:每传输一次后计数器自动减一,减到0,会触发重装,可以设置是否重装 - M2M控制是软件触发还是硬件触发,软件触发多用于从存储器到存储器,不需要等待存储器数据采集到数据 - DMA启动有三个条件 1.启动开关控制,2.传输计数器的值大于0, 3.选择触发源 - 写传输寄存器时必须先关闭开关控制,在写入传输计数器,最后打开开关控制 image.png - 触发源需要开启DMA,用到DMA函数,例如ADC_DMACMD image.png

代码

  • 开启RCC时钟
  • 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
#include "MyDMA.h"
uint16_t MYDMA = 0;
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t size)
{

DMA_InitTypeDef DMAy_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMAy_InitStructure.DMA_PeripheralBaseAddr = AddrA; //起始地址
DMAy_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度
DMAy_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; //是否自增
DMAy_InitStructure.DMA_MemoryBaseAddr = AddrB; //存储器起始地址
DMAy_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //存储器数据宽度
DMAy_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器是否自增
DMAy_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
DMAy_InitStructure.DMA_BufferSize = size; //传输计数器
DMAy_InitStructure.DMA_M2M = DMA_M2M_Enable; //选择硬件还是软件触发
DMAy_InitStructure.DMA_Mode = DMA_Mode_Normal; //是否启用自动重装
DMAy_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级

DMA_Init(DMA1_Channel1, &DMAy_InitStructure);

DMA_Cmd(DMA1_Channel1, DISABLE);
MYDMA = size;
}


void MyDMA_Transfer(void)
{
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1, MYDMA);
DMA_Cmd(DMA1_Channel1, ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);
}

扫描模式+单次转换代码

1
2
3
4
5
6
7
8
#ifndef __AD_H
#define __AD_H
#include "stm32f10x.h"
extern uint16_t AD_Value[3];
void AD_Init(void);
void AD_GatValue(void);
#endif

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
#include "AD.h"
uint16_t AD_Value[3];

void AD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //单次转换模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //开启扫描模式
ADC_InitStructure.ADC_NbrOfChannel = 3;
ADC_Init(ADC1, &ADC_InitStructure);

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //起始地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //是否自增
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //存储器起始地址
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据宽度
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器是否自增
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向
DMA_InitStructure.DMA_BufferSize = 3; //传输计数器
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //是否启用自动重装
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //选择硬件还是软件触发
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE); //开启ADC的DMA通道
ADC_Cmd(ADC1, ENABLE);

ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);

//ADC_SoftwareStartConvCmd(ADC1, ENABLE);

}


void AD_GatValue(void) //主函数需要一直调用
{
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1, 3);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);


}

扫描模式+多次转换代码

1
2
3
4
5
6
7
#ifndef __AD_H
#define __AD_H
#include "stm32f10x.h"
extern uint16_t AD_Value[3];
void AD_Init(void);
#endif

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
#include "AD.h"
uint16_t AD_Value[3];

void AD_Init(void) //只需要调用初始会函数,需要的时候直接读取AD_Value的值就行
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); //规则组序列2的位置,配置为通道1
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换,使能,每转换一次规则组序列后立刻开始下一次转换
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式,使能,扫描规则组的序列,扫描数量由ADC_NbrOfChannel确定
ADC_InitStructure.ADC_NbrOfChannel = 3; //通道数,为4,扫描规则组的前4个通道
ADC_Init(ADC1, &ADC_InitStructure);

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设基地址,给定形参AddrA
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据宽度,选择半字,对应16为的ADC数据寄存器
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址自增,选择失能,始终以ADC数据寄存器为源
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //存储器基地址,给定存放AD转换结果的全局数组AD_Value
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据宽度,选择半字,与源数据宽度对应
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址自增,选择使能,每次转运后,数组移到下一个位置
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
DMA_InitStructure.DMA_BufferSize = 3; //转运的数据大小(转运次数),与ADC通道数一致
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //模式,选择循环模式,与ADC的连续转换一致
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级,选择中等
DMA_Init(DMA1_Channel1, &DMA_InitStructure);

DMA_Cmd(DMA1_Channel1, ENABLE); //DMA1的通道1使能
ADC_DMACmd(ADC1, ENABLE); //ADC1触发DMA1的信号使能
ADC_Cmd(ADC1, ENABLE);

ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

}

串口通信

串口通信相关知识

image.png - 全双工是两个设备可以同时通信 - 有单独的时钟线是同步的,异步没有时钟线,需要事先约定一个采样频率 - 单端信号需要公地,差分信号是两个引脚的电压差来通信的

电平标准

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
- TTL电平:+3.3V或+5V表示1,0V表示0 - RS232电平:-3-15V表示1,+3+15V表示0 - RS485电平:两线压差+2+6V表示1,-2-6V表示0(差分信号)

串口参数及时序

  • 波特率:串口通信的速率 bit/s
  • 起始位:标志一个数据帧的开始,固定为低电平 ,因为空闲状态是高电平,发送数据置0表示要发送数据
  • 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
  • 校验位:用于数据验证,根据数据位计算得来
  • 停止位:用于数据帧间隔,固定为高电平,恢复默认高电平状态 image.png

USART串口

硬件接线图

  • 简单双向串口通信有两根通信线(发送端TX和接收端RX)
  • TX与RX要交叉连接 当只需单向的数据传输时,可以只接一根通信线
  • 当电平标准不一致时,需要加电平转换芯片 image.png|500
  • USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
  • USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
  • 自带波特率发生器,最高达4.5Mbits/s
  • 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
  • 可选校验位(无校验/奇校验/偶校验)
  • 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
  • STM32F103C8T6 USART资源: USART1、 USART2、 USART3 image.png|600

中断控制:TXE发送寄存器空标志位,RXNE接受寄存器非空标志位

发送+接受字节程序

image.png - 发送器和接收器的波特率由波特率寄存器BRR里的DIV确定 - 计算公式:波特率 = fPCLK2/1 / (16 * DIV)

步骤: - 开启GPIO与USART时钟 - GPIO初始化,复用推挽输出,RX配置为输入 - 配置USART - 需要发送直接初始化就行,接受需要配置中断,开启中断,配置优先级

Serial.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h"
#include "stdio.h"
#include "stdarg.h"
void Serial_Init(void);
void Serial_SentByte(uint8_t Byte);
void Serial_SentArray(uint8_t *Array, uint16_t Length);
void Serial_SentString(char *String);
void Serial_SentNumber(uint32_t Number);
int fputc(int ch, FILE *f);
void Serial_Printf(char *format, ...);
uint8_t Serial_GeRxFlag(void);
uint8_t Serial_GeRxData(void);
#endif

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
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
#include "Serial.h"

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

void Serial_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure; //中断初始化

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate = 9600; //配置波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送或者接受模式
USART_InitStructure.USART_Parity = USART_Parity_No; //检验位设置
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //
USART_Init(USART1, &USART_InitStructure);

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);


NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置分组方式,整个工程只设置1个
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //设置中断通道,使用哪个引脚使用哪个
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //设置响应优先级
NVIC_Init(&NVIC_InitStructure);

USART_Cmd(USART1, ENABLE);
}


void Serial_SentByte(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}


void Serial_SentArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for(i = 0; i<Length; i++)
{
USART_SendData(USART1, Array[i]);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

}
}


void Serial_SentString(char *String)
{
uint16_t i;
for(i = 0; String[i]!= '\0'; i++)
{
USART_SendData(USART1, String[i]);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

}
}

uint32_t Serial_digit(uint16_t i)
{
uint32_t res = 1;
while (i--)
{
res = 10 * res;
}
return res;
}


uint16_t Serial_Pow(uint32_t Number)
{
uint32_t pow = 1;
uint16_t i = 0;

do
{
pow = pow * 10;
i++;
} while (Number / pow != 0);
return i;
}


void Serial_SentNumber(uint32_t Number)
{
uint8_t num, num_char;
uint16_t i, j = Serial_Pow(Number);
uint32_t pow;
//printf("% d\n", j);
for (i = 0; i < j; i++)
{
pow = Serial_digit(j - i - 1);
num = Number / pow;
Number = Number - num * pow;
num_char = num + 0x30;
USART_SendData(USART1, num_char);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
}


int fputc(int ch, FILE *f)
{
Serial_SentByte(ch);
return ch;
}


void Serial_Printf(char *format, ...)
{
char String[100];
va_list arg;
__va_start(arg, format);
vsprintf(String, format, arg);
__va_end(arg);
Serial_SentString(String);


}


uint8_t Serial_GeRxFlag(void)
{

if (Serial_RxFlag == 1)
{
Serial_RxFlag = 0;
return 1;
}
return 0;
}


uint8_t Serial_GeRxData(void)
{
return Serial_RxData;
}

void USART1_IRQHandler(void)
{

if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
{
Serial_RxData = USART_ReceiveData(USART1);
Serial_RxFlag = 1;
USART_ClearFlag(USART1, USART_FLAG_RXNE);
}
}

发送+接受数据包程序

image.png image.png

Serial.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h"
#include "stdio.h"
#include "stdarg.h"
extern uint8_t Serial_TxPacket[];
extern char Serial_RxPacket[100];
extern uint8_t Serial_RxFlag;
void Serial_Init(void);
void Serial_SentByte(uint8_t Byte);
void Serial_SentArray(uint8_t *Array, uint16_t Length);
void Serial_SentString(char *String);
void Serial_SentNumber(uint32_t Number);
int fputc(int ch, FILE *f);
void Serial_Printf(char *format, ...);
uint8_t Serial_GeRxFlag(void);
uint8_t Serial_GeRxData(void);
void Serial_SendPacket(void);
#endif

Serial.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#include "Serial.h"

uint8_t Serial_RxData;
uint8_t Serial_RxFlag = 0;
uint8_t Serial_TxPacket[4];
char Serial_RxPacket[100];


void Serial_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure; //中断初始化

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate = 9600; //配置波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送或者接受模式
USART_InitStructure.USART_Parity = USART_Parity_No; //检验位设置
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //
USART_Init(USART1, &USART_InitStructure);

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);


NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置分组方式,整个工程只设置1个
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //设置中断通道,使用哪个引脚使用哪个
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //设置响应优先级
NVIC_Init(&NVIC_InitStructure);

USART_Cmd(USART1, ENABLE);
}


void Serial_SentByte(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}


void Serial_SentArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for(i = 0; i<Length; i++)
{
USART_SendData(USART1, Array[i]);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

}
}


void Serial_SentString(char *String)
{
uint16_t i;
for(i = 0; String[i]!= '\0'; i++)
{
USART_SendData(USART1, String[i]);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

}
}

uint32_t Serial_digit(uint16_t i)
{
uint32_t res = 1;
while (i--)
{
res = 10 * res;
}
return res;
}


uint16_t Serial_Pow(uint32_t Number)
{
uint32_t pow = 1;
uint16_t i = 0;

do
{
pow = pow * 10;
i++;
} while (Number / pow != 0);
return i;
}


void Serial_SentNumber(uint32_t Number)
{
uint8_t num, num_char;
uint16_t i, j = Serial_Pow(Number);
uint32_t pow;
//printf("% d\n", j);
for (i = 0; i < j; i++)
{
pow = Serial_digit(j - i - 1);
num = Number / pow;
Number = Number - num * pow;
num_char = num + 0x30;
USART_SendData(USART1, num_char);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
}


int fputc(int ch, FILE *f)
{
Serial_SentByte(ch);
return ch;
}


void Serial_Printf(char *format, ...)
{
char String[100];
va_list arg;
__va_start(arg, format);
vsprintf(String, format, arg);
__va_end(arg);
Serial_SentString(String);


}

/*
uint8_t Serial_GeRxFlag(void)
{

if (Serial_RxFlag == 1)
{
Serial_RxFlag = 0;
return 1;
}
return 0;
}
*/

uint8_t Serial_GeRxData(void)
{
return Serial_RxData;
}


void Serial_SendPacket(void)
{
Serial_SentByte(0xFF);
Serial_SentArray(Serial_TxPacket, 4);
Serial_SentByte(0xFE);
}



void USART1_IRQHandler(void)
{
static uint8_t RxState = 0;
static uint8_t PrxState = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
uint8_t RxData = USART_ReceiveData(USART1);
switch (RxState)
{
case 0:
{
if(RxData == '@' && Serial_RxFlag == 0) //这里加入Serial_RxFlag防止传的太快,导致错位
{
RxState = 1;
PrxState = 0;
}
else RxState = 0;
break;
}

case 1:
{
if(RxData == '\r')
{
RxState = 2;
}
else
{
Serial_RxPacket[PrxState] = RxData;
PrxState ++;
}
}

case 2:
{
if (RxData == '\n')
{
RxState = 0;
Serial_RxPacket[PrxState] = '\0';
Serial_RxFlag = 1;
}
break;
}

}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}

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
34
35
36
37
38
39
40
int main(void)
{
OLED_Init();
Serial_Init();
LED_Init();
OLED_ShowString(1, 6, "TEXT");
while (1)
{

if(Serial_RxFlag == 1)
{
OLED_ShowString(4, 1, " ");
OLED_ShowString(4, 1, Serial_RxPacket);

if (strcmp(Serial_RxPacket, "LED_ON") == 0)
{
LED0_ON();
Serial_SentString("LED_ON_OK\r\n");
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "LED_ON_OK");
}
else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)
{
LED0_OFF();
Serial_SentString("LED_ON_OFF\r\n");
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "LED_OFF_OK");
}
else
{
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "ERROR");
}
Serial_RxFlag = 0;

}

}

}

IIC串口

硬件与软件原理

  • I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线
  • 两根通信线:SCL(Serial Clock)、SDA(Serial Data)
  • 同步,半双工
  • 带数据应答
  • 支持总线挂载多设备(一主多从、多主多从) image.png image.png image.png image.png image.png image.png image.png image.png

软件I2C代码

MyI2C.h

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef __MYI2C_H
#define __MYI2C_H
#include "stm32f10x.h"
#include "Delay.h"
void MYI2C_Init(void);
void MYI2C_Start(void);
void MYI2C_Stop(void);
void MYI2C_SendBit(uint8_t Byte);
uint8_t MYI2C_ReceiveByte(void);
void MYI2C_SendACK(uint8_t ACKBit);
uint8_t MYI2C_ReceiveACK(void);
#endif

MyI2C.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
#include "MyI2C.h"

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)
{
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 |GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_WriteBit(GPIOB, GPIO_Pin_10 | GPIO_Pin_11, Bit_SET);

}


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(1);
}


void MYI2C_SendBit(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;
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_SendACK(uint8_t ACKBit)
{
MYI2C_W_SDA(ACKBit);
MYI2C_W_SCL(1);
MYI2C_W_SCL(0);

}


uint8_t MYI2C_ReceiveACK(void)
{
uint8_t ACKBit;
MYI2C_W_SDA(1);
MYI2C_W_SCL(1);
ACKBit = MYI2C_R_SDA();
MYI2C_W_SCL(0);
return ACKBit;
}

硬件I2C代代码

image.png|500 - 发送数据时当数据寄存器为空 TEX为1 - 接受数据时当数据寄存器为非空 RXNE为1 image.png|600 - GPIO为复用开漏输出模式 image.png - 10位地址帧率头是11110 image.png

代码

  • 开启I2C外设与GPIO口时钟
  • GPIO口初始化为复用开漏输出模型
  • I2C配置
  • 使能IIC