06:(寄存器开发)定时器二

定时器二

1、通用定时器

1.1、输出比较功能

stm32f103c8t6的通用定时器为TIM2,TIM3,TIM4。而通用定时器拥有基本定时器的所有功能,并且增加的如下的功能:

(1)多种时钟源选择

(2)向上计数(加),向下计数(减),向上/向下(先加后减)。当然我们使用的时候更喜欢向上计数

(3)输出比较,用于测量波形的周期/测量脉冲的宽度

(4)输入捕获,用于PWM波形的生成

(5)支持针对定位的增量(正交)编码器和霍尔传感器电路。

下图为通用定时器的结构框图(时钟来源,输入捕获,输出比较,从模式):

实验:使用TIM2的输出比较CH1输出PWM波形实现呼吸灯。实物连接如下图所示:

①TIM2.c文件的代码如下:

c 复制代码
#include "stm32f10x.h"                

/**
 * 通用定时器的输出比较模式输出PWM波形的初始化
 */
void TIM2_Init(void)
{
    /* 1、开启时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;//开启GPIOA的时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;//开启TIM2的时钟
    
    /* 2、配置GPIOA0(TIM2_CH1)引脚输出模式:复用推挽输出(MODE0 = 11,CNF0 = 10)*/
    GPIOA->CRL |= GPIO_CRL_MODE0;
    GPIOA->CRL &= ~GPIO_CRL_CNF0_0;
    GPIOA->CRL |= GPIO_CRL_CNF0_1;

    /* 3、定时器TIM2的配置 */
    /* 3.0、选择时钟源,默认的是内部时钟源ABP1*/
    
    /* 3.1、预分频器的设置 */
    TIM2->PSC = 7200 - 1;//每隔0.1ms来一个脉冲,计数一次

    /* 3.2、自动重装载寄存器的设置 */
    TIM2->ARR = 100 - 1;//一个周期的时间为10ms

    /* 3.3、计数器的计数方向:默认向上计数*/
    TIM2->CR1 &= ~TIM_CR1_DIR;//向上计数

    /* 4、配置输出比较 */
    /* 4.1、配置捕获比较寄存器的值 */
    TIM2->CCR1 = 50;

    /* 4.2、配置通道CH1的输出比较模式: TIMx_CCMR1_CC1S = 00(输出模式)
        TIMx_CCMR1_OC1M = 110(PWM1模式)
    */
    TIM2->CCMR1 &= ~TIM_CCMR1_CC1S;//CH1配置为输出模式
    TIM2->CCMR1 &= ~TIM_CCMR1_OC1M_0;
    TIM2->CCMR1 |= TIM_CCMR1_OC1M_1;
    TIM2->CCMR1 |= TIM_CCMR1_OC1M_2;

    /* 4.3、使能通道1:TIMx_CCER_CC1E = 1 */
    TIM2->CCER |= TIM_CCER_CC1E;//使能通道CH1

    /* 5、使能计数器 */
    TIM2->CR1 |= TIM_CR1_CEN;
}

/**
 * 更改占空比函数
 */
void Set_PWMDuty(uint8_t Data)
{
    TIM2->CCR1 = Data;
}

②主函数文件的代码如下:

c 复制代码
#include "stm32f10x.h"                
#include "TIM2.h"
#include "OLED.h"
#include "Delay.h"

int main(void)
{
    uint8_t Data = 0;
    TIM2_Init();
    Set_PWMDuty(Data);
	while(1)
	{
        for(uint8_t i = 0; i<100; i++)
        {
            Set_PWMDuty(++Data);
            Delay_ms(10);
        }
        for(uint8_t i = 0; i<100; i++)
        {
            Set_PWMDuty(--Data);
            Delay_ms(10);
        }
	}
}

实物效果展示如下:

呼吸灯

1.2、输入捕获功能

此功能可以捕获输入通道上输入信号的上升沿或下降沿,多用于测量PWM的周期/频率,也可以测量占空比,只要测量出连续的一个上升沿和一个下降沿的时间间隔,然后除以周期即可。

测量的方法如下:①只要测量出连续的两个上升沿或连续的两个下降沿的时间间隔。②在1s时间内捕获了N个上升沿,那么则1s内有N-1个周期,计算出周期,那么就能计算出频率。

通用定时器能产生中断的部位如下:

1.2.1、测量PWM波形的周期和频率

实验:使用TIM3的输入捕获CH1通道测量出TIM2产生的PWM波形的周期与频率
①TIM3.c文件的代码如下;

c 复制代码
#include "stm32f10x.h"                 

/**
 * 通用定时器TIM3的初始化,使用输出捕获通道CH1测量TIM2产生的PWM波形的频率和周期
 */
void TIM3_Init(void)
{
/* 1、开启时钟 */
    /* 1.1、开启定时器TIM3和CH1通道引脚(PA6)的时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;//开启GPIOA的时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;//开启TIM3的时钟

/* 2、配置CH1通道引脚为浮空输入模式:MODE6 = 00,CNF6 = 01 */
    GPIOA->CRL &= ~GPIO_CRL_MODE6;
    GPIOA->CRL |= GPIO_CRL_CNF6_0;
    GPIOA->CRL &= ~GPIO_CRL_CNF6_1;

/* 3、配置基本定时器TIM3 */
    /* 3.1、选择时钟源 */
    //默认为内部时钟源,所以无需代码配置

    /* 3.2、配置预分频PSC,分频系数为72 */
    TIM3->PSC = 72 - 1;//则一个脉冲的时间为1us

    /* 3.3、配置重装在值,为了防止溢出,配置最大*/
    TIM3->ARR = 65536 - 1;

    /* 3.4、配置计数器为向上计数:TIMx_CR1_DIR = 0 */
    TIM3->CR1 &= ~TIM_CR1_DIR;

/* 4、配置输入捕获相关寄存器 */
    /* 4.1、TIMx_CH1引脚连到TI1输入:TIMx_CR2_TI1S = 0 */
    TIM3->CR2 &= ~TIM_CR2_TI1S;

    /* 4.2、输入滤波器的配置,配置为不滤波:TIMx_CCMR1_IC1F = 0000 */
    TIM3->CCMR1 &= ~TIM_CCMR1_IC1F;
    
    /* 4.3、将CH1通道配置为输入捕获模式,且IC1映射到TI1::TIMx_CCMR1_CC1S = 01 */
    TIM3->CCMR1 |= TIM_CCMR1_CC1S_0;
    TIM3->CCMR1 &= ~TIM_CCMR1_CC1S_1;//IC1映射在TI1上

    /* 4.4、配置为捕获上升沿:TIMx_CCER_CC1P = 0 */
    TIM3->CCER &= ~TIM_CCER_CC1P;

    /* 4.5、输入捕获中的预分频的配置(不分频):TIMx_CCMR1_IC1PSC = 00 */
    TIM3->CCMR1 &= ~TIM_CCMR1_IC1PSC;

    /* 4.6、开启中断请求:TIMx_DIER_CC1IE = 1 */
    TIM3->DIER |= TIM_DIER_CC1IE;

    /* 4.7、使能捕获/比较寄存器:TIMx_CCER_CC1E = 1*/
    TIM3->CCER |= TIM_CCER_CC1E;

/* 5、配置NVIC */
    NVIC_SetPriorityGrouping(4);
    NVIC_SetPriority(TIM3_IRQn,0);
    NVIC_EnableIRQ(TIM3_IRQn);

/* 6、使能计数器:TIMx_CR1_CEN = 1*/
    TIM3->CR1 |= TIM_CR1_CEN;
}

/**
 * 中断服务函数:在捕获到第一个上升沿,让计数器里面的值变为0
 * 当捕获到第二个上升沿时,计数器里面的值拍照到捕获/比较寄存器(CCR)里面(硬件自动完成的)
 */
uint16_t t = 0;
void TIM3_IRQHandler(void)
{
    static uint8_t flag = 0;//第一个一个标志位,用来判断是否为第一个上升沿
    if(TIM3->SR & TIM_SR_CC1IF)
    {
        /* 清除标志位:TIMx_SR_CC1IF*/
        TIM3->SR &= ~TIM_SR_CC1IF;
        flag++;
        /* 判断是否为第一次上升沿,若是那么就清除计数器里面的值*/
        if(flag == 1)
        {
            TIM3->CNT = 0;//将计数器里面的值变为0
            TIM3->CCR1 = 0;//将捕获/比较寄存器(CCR)里面的值也变为0
        }
        else if(flag == 2)
        {
            t = TIM3->CCR1;//将计数器里面的值拍照到捕获/比较寄存器(CCR)里面
            TIM3->CNT = 0;//将计数器里面的值变为0
            TIM3->CCR1 = 0;//将捕获/比较寄存器(CCR)里面的值也变为0
            flag = 0;//让flag = 0;
        }
    }
}

/**
 * 对将捕获/比较寄存器(CCR)里面的值进行处理,获取PWM波形的周期(us)
 */
double Get_PWMCycle(void)
{
    return t ;//返回为us
}

/**
 * 对将捕获/比较寄存器(CCR)里面的值进行处理,获取PWM波形的频率(Hz)
 */
double Get_PWMFrequency(void)
{
    return 1000000.0 / t;//返回为Hz
}

TIM2.c文件的代码如下:

c 复制代码
#include "stm32f10x.h"                


/**
 * 通用定时器的输出比较模式输出PWM波形的初始化
 */
void TIM2_Init(void)
{
    /* 1、开启时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;//开启GPIOA的时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;//开启TIM2的时钟
    
    /* 2、配置GPIOA0(TIM2_CH1)引脚输出模式:复用推挽输出(MODE0 = 11,CNF0 = 10)*/
    GPIOA->CRL |= GPIO_CRL_MODE0;
    GPIOA->CRL &= ~GPIO_CRL_CNF0_0;
    GPIOA->CRL |= GPIO_CRL_CNF0_1;

    /* 3、定时器TIM2的配置 */
    /* 3.0、选择时钟源,默认的是内部时钟源ABP1*/
    
    /* 3.1、预分频器的设置 */
    TIM2->PSC = 7200 - 1;//每隔0.1ms来一个脉冲,计数一次

    /* 3.2、自动重装载寄存器的设置 */
    TIM2->ARR = 100 - 1;//一个周期的时间为10ms

    /* 3.3、计数器的计数方向:默认向上计数*/
    TIM2->CR1 &= ~TIM_CR1_DIR;//向上计数

    /* 4、配置输出比较 */
    /* 4.1、配置捕获比较寄存器的值 */
    TIM2->CCR1 = 50;

    /* 4.2、配置通道CH1的输出比较模式: TIMx_CCMR1_CC1S = 00(输出模式)
        TIMx_CCMR1_OC1M = 110(PWM1模式)
    */
    TIM2->CCMR1 &= ~TIM_CCMR1_CC1S;//CH1配置为输出模式
    TIM2->CCMR1 &= ~TIM_CCMR1_OC1M_0;
    TIM2->CCMR1 |= TIM_CCMR1_OC1M_1;
    TIM2->CCMR1 |= TIM_CCMR1_OC1M_2;

    /* 4.3、使能通道1:TIMx_CCER_CC1E = 1 */
    TIM2->CCER |= TIM_CCER_CC1E;//使能通道CH1

    /* 5、使能计数器 */
    TIM2->CR1 |= TIM_CR1_CEN;
}

/**
 * 更改占空比函数
 */
void Set_PWMDuty(uint8_t Data)
{
    TIM2->CCR1 = Data;
}

③主函数文件的代码如下:

c 复制代码
#include "stm32f10x.h"                
#include "TIM2.h"
#include "TIM3.h"
#include "OLED.h"

int main(void)
{ 
    uint8_t Data = 0;
    OLED_Init();
    OLED_ShowString(1,1,"Cycle:00000us");
    OLED_ShowString(2,1,"Freq:0000Hz");

    TIM2_Init();//TIM2的初始化,产生了一个周期为10ms的PWM波形
    TIM3_Init();//TIM3的初始化,开启CH1输出捕获
    Set_PWMDuty(Data);
    
	while(1)
	{
       OLED_ShowNum(1,7,Get_PWMCycle(),5);
       OLED_ShowNum(2,6,Get_PWMFrequency(),4);
        
       for(uint8_t i = 0; i<100; i++)
       {
           Set_PWMDuty(++Data);
           Delay_ms(10);
       }
       for(uint8_t i = 0; i<100; i++)
       {
           Set_PWMDuty(--Data);
           Delay_ms(10);
       }
	}
}

实物演示效果如下:

测量PWM周期和频率

1.2.2、定时器的从模式

使用TIM3的输入捕获CH1通道测量出TIM2产生的PWM波形的周期与频率这个实验,捕获到第一个上升沿的时候我们使用中断的方式,在中断函数里面通过CPU执行代码让计数器里面的值变为0。这样的方式测量出来的一个周期的CRR的值是有误差的。

因为在第一个上升沿来的时候触发中断,在执行中断函数的时候,PWM波形不断的"走",当执行到清零计数器的代码的时候,PWM波形已经走了一会了。所以不是在检测到PWM波形的上升沿的同一时间就立马把计数器里面的数据清零。但是,将计数器中的数据拍照到CRR中是由硬件执行的,在检测到上升沿时,硬件立马会把计数器中的数据拍照到CRR寄存器中。所以,使用上面的实验的方法,测量出来的周期一般比实际周期小。

为了解决这一问题:使用定时器的从模式。从模式的功能都是由硬件自动完成的,无需CPU执行代码。

如上图所示:TRGI为从模式触发信号,其中触发信号的来源如图中蓝色框中的8种,如下图为8中信号来源。
从模式的工作方式:

实验:通过定时器从模式自动测量PWM波的周期和频率和PWM波的占空比。

从模式的触发信号选择TI1FP1,从模式的工作模式为复位模式。将IC1配置为检测上升沿,且IC1映射在TI1FP1。将IC2配置为检测下降沿,且IC2映射在TI1FP2。即来一个上升沿时,通过从模式硬件自动启动复位(计数器清零),来一个下降沿计数器里面的值拍照到捕获/比较寄存器2中CCR2。再来一个上升沿将计数器里面值拍照到CCR1中。则一个周期 = CCR1里面的数值,占空比 = CCR2/CCR1

①TIM3.c文件的代码如下:

c 复制代码
#include "stm32f10x.h"                 

/**
 * 通用定时器TIM3的初始化,使用输出捕获通道CH1测量TIM2产生的PWM波形的频率和周期
 */
void TIM3_Init(void)
{
/* 1、开启时钟 */
    /* 1.1、开启定时器TIM3和CH1通道引脚(PA6)的时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;//开启GPIOA的时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;//开启TIM3的时钟

/* 2、配置CH1通道引脚为浮空输入模式:MODE6 = 00,CNF6 = 01 */
    GPIOA->CRL &= ~GPIO_CRL_MODE6;
    GPIOA->CRL |= GPIO_CRL_CNF6_0;
    GPIOA->CRL &= ~GPIO_CRL_CNF6_1;

/* 3、配置基本定时器TIM3 */
    /* 3.1、选择时钟源 */
    //默认为内部时钟源,所以无需代码配置

    /* 3.2、配置预分频PSC,分频系数为72 */
    TIM3->PSC = 72 - 1;//则一个脉冲的时间为1us

    /* 3.3、配置重装在值,为了防止溢出,配置最大*/
    TIM3->ARR = 65536 - 1;

    /* 3.4、配置计数器为向上计数:TIMx_CR1_DIR = 0 */
    TIM3->CR1 &= ~TIM_CR1_DIR;

/* 4、配置输入捕获相关寄存器 */
    /* 4.1、TIMx_CH1引脚连到TI1输入:TIMx_CR2_TI1S = 0 */
    TIM3->CR2 &= ~TIM_CR2_TI1S;

    /* 4.2、输入滤波器的配置,配置为不滤波:TIMx_CCMR1_IC1F = 0000 */
    TIM3->CCMR1 &= ~TIM_CCMR1_IC1F;

    /* 4.3、将CH1通道配置为输入捕获模式,且IC1映射到TI1:TIMx_CCMR1_CC1S = 01 
            将CH2通道配置为输入捕获模式,且IC2映射到TI1:TIMx_CCMR1_CC2S = 10*/
    TIM3->CCMR1 |= TIM_CCMR1_CC1S_0;
    TIM3->CCMR1 &= ~TIM_CCMR1_CC1S_1;//IC1映射在TI1FP1上
    TIM3->CCMR1 &= ~TIM_CCMR1_CC2S_0;
    TIM3->CCMR1 |= TIM_CCMR1_CC2S_1;//IC2映射在TI1上

    /* 4.5、配置IC1为捕获上升沿:TIMx_CCER_CC1P = 0 
            配置IC2为捕获下降沿:TIMx_CCER_CC2P = 0*/
    TIM3->CCER &= ~TIM_CCER_CC1P;
    TIM3->CCER |= TIM_CCER_CC2P;

    /* 4.6、输入捕获中IC1的预分频的配置(不分频):TIMx_CCMR1_IC1PSC = 00
            输入捕获中IC2的预分频的配置(不分频):TIMx_CCMR1_IC2PSC = 00 */
    TIM3->CCMR1 &= ~TIM_CCMR1_IC1PSC;
    TIM3->CCMR1 &= ~TIM_CCMR1_IC2PSC;

    /* 4.7、捕获/比较寄存器1使能:TIMx_CCER_CC1E = 1
            捕获/比较寄存器2使能:TIMx_CCER_CC2E = 1*/
    TIM3->CCER |= TIM_CCER_CC1E;
    TIM3->CCER |= TIM_CCER_CC2E;

/* 5、配置从模式 */
    /* 5.1、选择从模式的触发信号为TI1FP1:TIMx_SMCR_TS = 101 */
    TIM3->SMCR |= (TIM_SMCR_TS_0|TIM_SMCR_TS_2);
    TIM3->SMCR &= ~TIM_SMCR_TS_1;

    /* 5.2、配置从模式的工作模式:复位模式 TIMx_SMCR_SMS = 100 */
    TIM3->SMCR |= TIM_SMCR_SMS_2;
    TIM3->SMCR &= ~(TIM_SMCR_SMS_1|TIM_SMCR_SMS_0);

/* 6、使能计数器:TIMx_CR1_CEN = 1*/
    TIM3->CR1 |= TIM_CR1_CEN;
}

/**
 * 对将捕获/比较寄存器(CCR1)里面的值进行处理,获取PWM波形的周期(us)
 */
double Get_PWMCycle(void)
{
    return TIM3->CCR1;//返回为us
}

/**
 * 对将捕获/比较寄存器(CCR)里面的值进行处理,获取PWM波形的频率(Hz)
 */
 
double Get_PWMFrequency(void)
{
    return 1000000.0 / TIM3->CCR1;//返回为Hz
}

/**
 * 对将捕获/比较寄存器(CCR1)和(CCR2)里面的值进行处理,获取PWM波形占空比(%)
 */
double Get_PWMDutycycle(void)
{
    return ((TIM3->CCR2) * 100) / TIM3->CCR1;//返回为%
}

②TIM2.c文件的代码如下:

c 复制代码
#include "stm32f10x.h"                

/**
 * 通用定时器的输出比较模式输出PWM波形的初始化
 */
void TIM2_Init(void)
{
    /* 1、开启时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;//开启GPIOA的时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;//开启TIM2的时钟
    
    /* 2、配置GPIOA0(TIM2_CH1)引脚输出模式:复用推挽输出(MODE0 = 11,CNF0 = 10)*/
    GPIOA->CRL |= GPIO_CRL_MODE0;
    GPIOA->CRL &= ~GPIO_CRL_CNF0_0;
    GPIOA->CRL |= GPIO_CRL_CNF0_1;

    /* 3、定时器TIM2的配置 */
    /* 3.0、选择时钟源,默认的是内部时钟源ABP1*/
    
    /* 3.1、预分频器的设置 */
    TIM2->PSC = 7200 - 1;//每隔0.1ms来一个脉冲,计数一次

    /* 3.2、自动重装载寄存器的设置 */
    TIM2->ARR = 100 - 1;//一个周期的时间为10ms

    /* 3.3、计数器的计数方向:默认向上计数*/
    TIM2->CR1 &= ~TIM_CR1_DIR;//向上计数

    /* 4、配置输出比较 */
    /* 4.1、配置捕获比较寄存器的值 */
    TIM2->CCR1 = 50;

    /* 4.2、配置通道CH1的输出比较模式: TIMx_CCMR1_CC1S = 00(输出模式)
        TIMx_CCMR1_OC1M = 110(PWM1模式)
    */
    TIM2->CCMR1 &= ~TIM_CCMR1_CC1S;//CH1配置为输出模式
    TIM2->CCMR1 &= ~TIM_CCMR1_OC1M_0;
    TIM2->CCMR1 |= TIM_CCMR1_OC1M_1;
    TIM2->CCMR1 |= TIM_CCMR1_OC1M_2;

    /* 4.3、使能通道1:TIMx_CCER_CC1E = 1 */
    TIM2->CCER |= TIM_CCER_CC1E;//使能通道CH1

    /* 5、使能计数器 */
    TIM2->CR1 |= TIM_CR1_CEN;
}

/**
 * 更改占空比函数
 */
void Set_PWMDuty(uint8_t Data)
{
    TIM2->CCR1 = Data;
}

③主函数文件的代码如下:

c 复制代码
#include "stm32f10x.h"                
#include "TIM2.h"
#include "TIM3.h"
#include "OLED.h"
#include "Delay.h"

int main(void)
{ 
    uint8_t Data = 0;
    OLED_Init();
    OLED_ShowString(1,1,"Cycle:00000us");
    OLED_ShowString(2,1,"Freq:0000Hz");
    OLED_ShowString(3,1,"Duty:000%");

    TIM2_Init();//TIM2的初始化,产生了一个周期为10ms的PWM波形
    TIM3_Init();//TIM3的初始化,开启CH1输出捕获
    Set_PWMDuty(Data);
    
	while(1)
	{
       OLED_ShowNum(1,7,Get_PWMCycle(),5);
       OLED_ShowNum(2,6,Get_PWMFrequency(),4);
       OLED_ShowNum(3,6,Get_PWMDutycycle(),3);
        
       for(uint8_t i = 0; i<100; i++)
       {
           Set_PWMDuty(++Data);
           OLED_ShowNum(3,6,Get_PWMDutycycle(),3);//显示一下占空比
           Delay_ms(20);
       }
       for(uint8_t i = 0; i<100; i++)
       {
           Set_PWMDuty(--Data);
           OLED_ShowNum(3,6,Get_PWMDutycycle(),3);
           Delay_ms(20);
       }
	}
}

实物演示效果如下:

占空比

2、高级定时器

高级定时器有2个分别为TIM1和TIM8,而stm32f103c8t6只有一个高级定时器(TIM1)。高级定时器有通用定时器的所有功能外,还具备了以下的功能:
(1)重复计数器
(2)死区时间可编程的互补输出
(3)刹车输入信号

高级定时器的结构图如下:

如上图所示:①计数器CNT溢出1次时,重复计数器减1,当重复计数器溢出时(从0减到重复计数器的重装值),才会产生UI和U信号,若开启中断,才会执行中断。
重复计数器值为N,则CNT计数器溢出N+1次才会产生一次更新事件。

②死区时间可编程的互补输出:而互补输出一般情况下用于电机驱动,互补信号:频率周期相等,相位相差180°

③刹车输入信号:通过BKIN引脚输入/事件来控制定时器的比较输出。

高级定时器能产生中断的部位:

❄【注】①使用高级定时器的捕获/比较寄存器的通道输出波形,则要使能TIMx_BDTR_MOE位
②计数器使用的是向下计数+开启UP中断(计数器溢出中断),则在使能计数器前,将状态寄存器TIMx_SR_UIF位置0。否则一上电就会进入一次中断。(因为向下计数,初始前计数器里面的值为0,定时器初始化后,重装载寄存器里面的值会转载到计数器里面,计数器里面的值有0变为N,相当于一次溢出,产生一个更新事件,若开启了中断,则会立马进入中断复位函数)

相关推荐
Uu_05kkq2 小时前
【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
c语言·数据结构·算法
枯无穷肉3 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名6774 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普4 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
嵌入式大圣4 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
A懿轩A4 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
云山工作室4 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费4 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
1 9 J5 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法
qq_397562316 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机