[作品] 46“万里”树莓派小车——PicoW学习(PWM、定时器)

lb8820265   2023-11-1 22:27 楼主

    使用Pico W做底盘,需要至少有以下外设:GPIO,PWM,定时器,编码器读取和WiFi。GPIO,PWM,UART,定时器,这几个外设都有例程可以参考,移植起来也比较容易,这里一起介绍,顺便介绍一下工程移植过程。

目标

    创建呼吸灯工程,设置两个个引脚为PWM输出连接LED,在定时器中断中改变PWM输出占空比,并通过UART输出定时器的间隔。

PWM简介

    在写代码之前需要大致了解该芯片的PWM外设,PicoW主控芯片是RP2040,所以如果要详细的了解相关外设,可阅读芯片手册,在帖子最后有下载。PWM在数据手册的523页,所有的30个GPIO都可以用来输出PWM,一共16个PWM通道,由8个2通道的slices组成,也就是一个slices可以输出给两个引脚,可设置不同的占空比等。

image-20231101222738-1.png  

image-20231101222738-2.png  

    PWM有一个不断增加计数器,通过TOP寄存器的值来周期,通过设置比较的值来控制占空比。普通模式(free-running模式)如下图。

image-20231101222738-3.png  

    PWM的频率公式如下。

image-20231101222738-4.png  

    fsys:PWM时钟,使用的CLK SYS,默认是等于PLL SYS=125Mhz

    TOP:周期设置寄存器,默认65535

    CSR_PH_CORRECT:相位校正模式,默认为0(普通模式)

    DIV_INT:是时钟分配寄存器,1~255,默认为1

    DIV_FRAC:FRAC是fractional的缩写,也就是小数,0~15,默认0

新建工程

    使用前面介绍的方法打开工具“pico project generato”,勾选“HW TImer”,发现并没有PWM相关的模块可以勾选,这里我们尝试选择小车工程会需要的外设如下。

image-20231101222738-5.png  

    将例程中的“.viscode”文件夹复制到生成的工程中。生成的文件与大致的功能如下。

image-20231101222738-6.png  

打开工程

    打开“Pico - Visual Studio Code”软件,在软件中打开刚刚生成的工程文件夹。

image-20231101222738-7.png  

    会自动弹出选择编译工具,这两个任选一个都行,如果打开的时候错过了,可以点击最下方状态栏的“No Kit Selected”重新选择。

image-20231101222738-8.png  

配置工程

    重点关注“CMakeLists.txt”文件,这是CMake的配置文件。在“CMakeLists.txt”文件找到target_link_libraries,该指令的作用为将目标文件与库文件进行链接。

可以看到已有两个库文件,就是我们在工具中勾选的,从例程中得知,PWM的库文件名叫做“hardware_pwm”,因此添加该库文件如下。

image-20231101222739-9.png  

    还有一个add_executable指令,该指令的含义是使用“Test_Timer_PWM.c ”源文件生成“Test_Timer_PWM”可执行文件。

    可以有多个该指令,生成多个可执行文件,可配合“target_link_libraries”指令链接不同的库,生成不同的可执行文件。

    这里增加一个“peripherals.c”文件用来写外设的配置代码,如下。

image-20231101222739-10.png  

    新建“peripherals.c”与“peripherals.h”文件在根目录。

编写代码

    PWM初始化如下:

void PWM_init(){
    gpio_set_function(PIN_PWM_1, GPIO_FUNC_PWM);//设置引脚为PWM功能
    gpio_set_function(PIN_PWM_2, GPIO_FUNC_PWM);
    uint slice_num = pwm_gpio_to_slice_num(PIN_PWM_1);//根据GPIO获取对应的slice号
    pwm_config config = pwm_get_default_config();//获取默认的设置
    pwm_config_set_clkdiv(&config, 4.f);//配置该slice的PWM的频率,4分频clk_sys
    pwm_init(slice_num, &config, true);//配置并启动该PWM
}

    定时器初始化如下:

void Timer_init(){
    //配置定时器,-50表示50ms定时,负数表示从进入该函数开始计算,repeating_timer_callback是回调函数名
    add_repeating_timer_ms(-50, repeating_timer_callback, NULL, &timer);
}

    定时器回调函数如下:

bool repeating_timer_callback(struct repeating_timer *t) {
    static absolute_time_t t_from;
    absolute_time_t t_to;
    int64_t t_delta;
    static int fade = 0;
    static bool going_up = true;
    t_to=get_absolute_time();//获取上电开始的绝对时间
    t_delta=absolute_time_diff_us(t_from,t_to);//获取时间差,转化为us
    t_from=t_to;//保存上次的值
    printf("time_diff_us: %8d\n", (int)t_delta);
    if (going_up) {
        ++fade;
        if (fade > 255) {
            fade = 255;
            going_up = false;
        }
    } else {
        --fade;
        if (fade < -255) {
            fade = -255;
            going_up = true;
        }
    }
    pwm_set_gpio_level(PIN_PWM_1, fade * fade);//最大值65535,数值的平方更适合呼吸灯
    pwm_set_gpio_level(PIN_PWM_2, 65535-fade * fade);
}

    运行后,使用串口助手连接仿真器生成的串口,可以看到返回的数值都是50000,说明定时器非常的精准。

image-20231101222739-11.png   

    使用LED灯连接Pin14和Pin15另一端连接GND就可以看到灯交替闪烁了。使用示波器观察引脚频率,结合前面的PWM频率计算公式,理论上应该是125M/65536/4=476.84Hz,示波器实测的频率为476.9Hz,如下。

image-20231101222739-12.png   

RP2040数据手册:
rp2040-datasheet.pdf (5.07 MB)
(下载次数: 3, 2023-11-1 22:33 上传)
代码源文件:
Test_Timer_PWM.zip (731.83 KB)
(下载次数: 0, 2023-11-1 22:43 上传)
本帖最后由 lb8820265 于 2023-11-1 22:45 编辑
QQ:252669569

回复评论 (2)

这玩意咋不用arduino或者micropython玩玩,很方便,比C简单

点赞  2023-11-3 21:38

这个介绍很详细,开拓了眼界,希望对自己的项目有启发

点赞  2023-11-7 02:30
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复