地平线4遥测数据小工具_u8g2篇

[复制链接]
查看1832 | 回复10 | 2024-1-7 22:06:53 | 显示全部楼层 |阅读模式

本帖最后由 小灰灰 于 2024-1-7 22:15 编辑

本帖最后由 小灰灰 于 2024-1-7 22:06 编辑

挖个坑

收到开发板有一段时间了,这几天终于闲下来一点,为了快乐的玩地平线4,于是打算在显示器下面放个小屏幕,显示一些地平线4的遥测数据。手头有的小屏幕有ssd1306和st7735驱动的,鉴于u8g2移植比较简单,所以先用u8g2实现一下,lvgl嘛...就之后在说了。


本章涉及内容

  • i2c
  • FreeRTOS
  • u8g2
  • gpio

项目说明

项目使用SDK中的Project_basic作为模板

组件

编辑CMakeLists.txt

使用 sdk_add_include_directories添加需要引入的组件

当然,也可以手动添加编译参数,就是没什么必要罢了

如本此使用的u8g2

sdk_add_include_directories(components/csrc)

sdk组件

编辑proj.conf

按照需求更改(增加)

比如本此使用的

set(CONFIG_FREERTOS 1)
set(CONFIG_POSIX 1)
set(CONFIG_LWIP 1)
set(CONFIG_WIFI6 1)

set(CONFIG_PARTITION 1)
set(CONFIG_BFLB_MTD 1)
set(CONFIG_EASYFLASH4 1)

FLASH烧录配置

具体把固件放在什么位置

其中需要改动的地方就是,hroizon4_showdata是我自己的项目名称,根据编译输出位置自己改下就好

[FW]
filedir = ./build/build_out/horizon4_showdata_$(CHIPNAME).bin

i2c

ai-m61-32s有两组i2c控制器,两组貌似可以任意选择支持i2c(SDA SCL)的引脚

例如这样:

使用i2c0控制器,引脚使用GPIO_PIN_0和GPIO_PIN_1

bflb_gpio_init(gpio, GPIO_PIN_0, GPIO_FUNC_I2C0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);
bflb_gpio_init(gpio, GPIO_PIN_1, GPIO_FUNC_I2C0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);

又或者

使用i2c1控制器,引脚使用GPIO_PIN_32和GPIO_PIN_33

bflb_gpio_init(gpio, GPIO_PIN_32, GPIO_FUNC_I2C1 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);
bflb_gpio_init(gpio, GPIO_PIN_33, GPIO_FUNC_I2C1 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);

具体引脚配置参考m61的规格说明书

初始化引脚后,初始化i2c

#define I2C0_FREQ 400000

struct bflb_device_s *i2c0 = bflb_device_get_by_name("i2c0");
bflb_i2c_init(i2c0, I2C0_FREQ):

i2c_msg

i2c通信时使用bflb_i2c_msg_s传递参数(设备地址、传输模式、缓冲区、缓冲区大小)

#define ADDR 0x30
uint8_t buf[256] = {0};

struct bflb_i2c_msg_s msg = {
    .addr = ADDR,
    .flags = 0,
    .buffer = buf,
    .length = 0
}

flags的参数,可以在sdk的bflb_i2c.h中找到

这部分我自己在sdk里加了个

define I2C_M_WRITE 0x0000

问就是差一行逼疯强迫症

#define I2C_M_READ    0x0001 /* Read data, from slave to master */
#define I2C_M_TEN     0x0002 /* Ten bit address */
#define I2C_M_DMA     0x0004 /* Enable dma mode */
#define I2C_M_NOSTOP  0x0040 /* Message should not end with a STOP */
#define I2C_M_NOSTART 0x0080 /* Message should not begin with a START */

小补充:

关于i2c的dma和中断部分,暂时不太需要,等需要的时候再补上来


FreeRTOS

FreeRTOS需要自行配置,项目模板Project_basic中的基本够用

配置FreeRTOS可用的最大内存大为100KB

#define configTOTAL_HEAP_SIZE ((size_t)100 * 1024)

添加一些函数,比如下面这个,获取当前task的剩余栈大小

#define INCLUDE_uxTaskGetStackHighWaterMark 1

task

FreeRTOS使用task为基本执行单位

基本上当成线程(按照我的理解更接近协程)用就可以,执行时可能涉及到一些临界资源,自行加锁即可

以下算是最基本的使用方式

创建一个task并执行,如下

void draw_task(void *parameters);

TaskHandle_t draw_task_handle;

int main()
{
    xTaskCreate(draw_task,         // task对应的函数
                "draw_task",       // task名称,可用于寻找task或者debug等
                256,               // 栈空间大小
                &wifi_status,      // 需要传递的变量,对应draw_task的(void *parameters),多个参数用数组就可以
                7,                 // 优先级,默认配置应该是15级具体看 Freertos.h中的配置
                &draw_task_handle);    // 句柄

    vTaskStartScheduler();         // 开始执行
  return 0;
}

delay

在task中,如果需要delay函数,应该使用FreeRTOS提供的vTaskDelay函数

如果有需要,可以在FreeRTOS.h中配置延迟时间

   vTaskDelay(2000 / portTICK_PERIOD_MS);    // 延迟2000ms

u8g2

u8g2需要实现至少两个函数,两个函数类型如下

typedef uint8_t (*u8x8_msg_cb)(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);

我自己的这里实现如下

其中struct screen是我为了实现多个oled显示屏加的(反正都写了,干脆多个好了

typedef struct screen{
    struct bflb_device_s *__u8g2_i2c;
    struct bflb_i2c_msg_s __u8g2_i2c_msg;
    uint32_t __idx;
    int32_t error_code;    // i2c错误代码,因为我这里的线有些许问题,传输容易出错
    uint8_t *__buf;
    u8g2_t *__u8g2;        // u8g2和u8x8地址是一样的(参考定义
    struct screen* next;
};

uint8_t bl618_u8x8_byte_hw_i2c_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{   
    struct screen* instance = get_u8g2_instance(u8x8);

    uint8_t *data;
    switch (msg)
    {
    case U8X8_MSG_BYTE_INIT: 
        if (instance->__u8g2_i2c == NULL)
        {
            u8g2_i2c_init(instance);
        }
        break;
    case U8X8_MSG_BYTE_START_TRANSFER:
        instance->__idx = 0;
        break;
    case U8X8_MSG_BYTE_SEND:
        data = (uint8_t *)arg_ptr;
        while (arg_int > 0)
        {
            instance->__buf[instance->__idx++] = *data;
            data++;
            --arg_int;
        }
        break;
    case U8X8_MSG_BYTE_END_TRANSFER:
        instance->__u8g2_i2c_msg.length = instance->__idx;
        if (instance->error_code = (bflb_i2c_transfer(instance->__u8g2_i2c, &instance->__u8g2_i2c_msg, 1)) != 0)
        {
            printf("u8g2 transmit error code: %d\r\n", instance->error_code);
            // csrc_u8x8_byte_hw_i2c(u8x8, U8X8_MSG_BYTE_INIT, 0, NULL);
            u8g2_i2c_init(instance); 
        };
        break;
    case U8X8_MSG_BYTE_SET_DC:

        break;
    default:
        return 0;
    }
    return 1;
}

uint8_t bl618_u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    switch (msg)
    {
    case U8X8_MSG_DELAY_100NANO:
        for (int i = 0;i < 10;++i);
        break;
    case U8X8_MSG_DELAY_10MICRO:
        bflb_mtimer_delay_us(10 * arg_int);
        break;
    case U8X8_MSG_DELAY_MILLI:
        bflb_mtimer_delay_ms(arg_int);
        break;
    case U8X8_MSG_GPIO_MENU_SELECT:
        break;
    case U8X8_MSG_GPIO_MENU_NEXT:
        break;
    case U8X8_MSG_GPIO_MENU_PREV:
        break;
    case U8X8_MSG_GPIO_MENU_HOME:
        break;
    default:
        break;
    }
    return 1;
}

两个函数一个是负责i2c发送,另一个是实现按键功能(暂时不需要)

如果想只写一个函数,那就只写一个也可以,反正类型一样

初始化和绘制

u8g2常用的绘制方式有两种

以下代码的u8g2_Setup_xxxx部分,如果只需要一个屏幕,那就直接填函数进去就可以

方法一

    u8g2_t __oled;
    u8g2_t *oled = &__oled;

    u8g2_Setup_ssd1306_i2c_128x32_univision_f (oled, U8G2_R1, get_u8g2_hw_i2c_cb(oled, 0x78, NULL), get_u8g2_gpio_and_delay(oled));

    u8g2_InitDisplay(oled);
    u8g2_SetPowerSave(oled, 0);
    u8g2_SetContrast(oled, 100);

    while (true)
    {
        u8g2_ClearBuffer(oled);
    // 绘制函数们
        u8g2_DrawUTF8(oled, 0, 15, "Hello World");
        u8g2_SendBuffer(oled);
    }

方法二

    u8g2_t __oled;
    u8g2_t *oled = &__oled;

    u8g2_Setup_ssd1306_i2c_128x32_univision_f (oled, U8G2_R1, get_u8g2_hw_i2c_cb(oled, 0x78, NULL), get_u8g2_gpio_and_delay(oled));


    u8g2_InitDisplay(oled);
    u8g2_SetPowerSave(oled, 0);
    u8g2_SetContrast(oled, 100);

    while (true)
    {
        u8g2_FirstPage(oled);
        do 
        {
            // 绘制函数们
            u8g2_DrawUTF8(oled, 0, 15, "Hello world");
        } while (u8g2_NextPage(oled));
    }


测试一下

话说markdown怎么上传图片啊...

main函数代码如下,简单写了下测试屏幕可以正常驱动,

下一步就是WiFi部分了(有空了就去写

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include "board.h"
#include "log.h"

#include "bflb_gpio.h"
#include "bflb_irq.h"
#include "bflb_uart.h"
#include "bflb_l1c.h"
#include "bflb_mtimer.h"
#include "bflb_i2c.h"
#include "bflb_uart.h"

#include "easyflash.h"
#include "bflb_flash.h"
#include "bflb_mtd.h"

#include "bl618_u8g2.h"

#include "wifi_event.h"

// #define BL618

#define DBG_TAG "MAIN"

struct bflb_device_s *gpio;

uint32_t wifi_status = 0;

TaskHandle_t draw_task_handle;
void draw_task(void *parameter)
{
    u8g2_t __oled;
    u8g2_t *oled = &__oled;

    // u8g2_Setup_ssd1306_i2c_128x64_noname_f(oled, U8G2_R0, bl618_u8x8_byte_hw_i2c_cb, bl618_u8x8_gpio_and_delay);
    u8g2_Setup_ssd1306_i2c_128x32_univision_f(oled, U8G2_R1, get_u8g2_hw_i2c_cb(oled, 0x78, NULL), get_u8g2_gpio_and_delay(oled));


    u8g2_InitDisplay(oled);
    u8g2_SetPowerSave(oled, 0);
    u8g2_SetContrast(oled, 100);


    int fps = 0;
    char fps_str[10] = {0};
    char wifi_status_str[10] = {0};

    uint64_t start_time = 0;
    u8g2_SetFont(oled, u8g2_font_wqy12_t_chinese3);
    while (bl618_u8x8_get_i2c_error_code(oled) >= 0)
    {

        start_time = bflb_mtimer_get_time_us();
        itoa(fps++, fps_str, 10);
        itoa(*(uint32_t *)parameter, wifi_status_str, 10);
        u8g2_FirstPage(oled);
        do
        {
            u8g2_DrawUTF8(oled, 0, 15, fps_str);
            u8g2_DrawUTF8(oled, 0, 31, wifi_status_str);
        } while (u8g2_NextPage(oled));

        // u8g2_ClearBuffer(oled);

        // u8g2_SetFont(oled, u8g2_font_wqy12_t_chinese3);
        // {
        //     u8g2_DrawUTF8(oled, 0, 1 * 16 - 1, fps_str);
        //     u8g2_DrawUTF8(oled, 0, 2 * 16 - 1, wifi_status_str);
        // }
        // u8g2_SendBuffer(oled);

        // printf("stack remain: %d  [in cycle]\r\n", uxTaskGetStackHighWaterMark(draw_task_handle));
        // bflb_mtimer_delay_us(33333 - (bflb_mtimer_get_time_us() - start_time));
        // vTaskDelay((33333 - (bflb_mtimer_get_time_us() - start_time)) / (portTICK_PERIOD_MS / 1000));
        vTaskDelay((33333 - (bflb_mtimer_get_time_us() - start_time)) / 1000 / portTICK_PERIOD_MS);
    }
}

TaskHandle_t wifi_task_handle;
void wifi_task(void *parameter)
{
    vTaskDelay(1000 / portTICK_PERIOD_MS);

    *(uint32_t *)parameter = wifi_connect(SSID, PASS);
    printf("wifi status: %d\r\n", *(uint32_t *)parameter);

    while (true)
    {
        if (NULL != draw_task_handle)
        {
            eTaskState draw_state = eTaskGetState(draw_task_handle);
            if (draw_state == eBlocked)
            {
                vTaskResume(draw_task_handle);
            }
            printf("task statue: %d\r\n", draw_state);
        }
        printf("beacon\r\n");
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

void gpio_init()
{
    gpio = bflb_device_get_by_name("gpio");

    bflb_gpio_init(gpio, GPIO_PIN_27, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    bflb_gpio_init(gpio, GPIO_PIN_29, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    bflb_gpio_reset(gpio, GPIO_PIN_27);
    bflb_gpio_reset(gpio, GPIO_PIN_29);
}

int main(void)
{
    board_init();
    bflb_mtd_init(); // 这一行不要忘记
    easyflash_init();
    ;
    // uart_init();
    gpio_init();

    xTaskCreate(draw_task,
                "draw_task",
                2048,
                &wifi_status,
                7,
                &draw_task_handle);

    // wifi_start_firmware_task();

    xTaskCreate(wifi_task,
                "wifi_task",
                2048,
                &wifi_status,
                7,
                wifi_task_handle);

    vTaskStartScheduler();

    printf("end scheduler\r\n");

    while (true)
    {
        bflb_mtimer_delay_ms(10000);
    }

    return 0;
}

完整代码 https://github.com/iuhiuhoaix/horizon4_showdata

回复

使用道具 举报

1084504793 | 2024-1-7 22:12:32 | 显示全部楼层
回复

使用道具 举报

lazy | 2024-1-7 22:36:13 | 显示全部楼层
赞一下
回复

使用道具 举报

wukong50 | 2024-1-8 07:51:07 | 显示全部楼层
回复

使用道具 举报

wukong50 | 2024-1-8 08:14:40 | 显示全部楼层
赞一下
回复

使用道具 举报

WT_0213 | 2024-1-8 08:31:02 | 显示全部楼层
回复

使用道具 举报

爱笑 | 2024-1-8 08:50:50 | 显示全部楼层
厉害了小灰灰
用心做好保姆工作
回复 支持 反对

使用道具 举报

干簧管 | 2024-1-8 09:45:03 | 显示全部楼层
回复

使用道具 举报

曹县 | 2024-1-8 09:45:28 | 显示全部楼层
回复

使用道具 举报

san | 2024-1-8 10:35:18 | 显示全部楼层
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则