登录发现更多内容
首页
分类
发帖
账号
自动登录
找回密码
密码
登录
立即注册
立即登录
立即注册
其他登录
QQ
微信
首页
Portal
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
产品教程
BBS
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
开发资料
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
样品购买
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
IoT云平台
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
GitHub
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
技术博客
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
搜索
搜索
热搜:
LoRa
ESP8266
安信可
本版
帖子
用户
请
登录
后使用快捷导航
没有账号?
立即注册
每日签到
任务
广播
导读
排行榜
设置
我的收藏
退出
10
0
0
首页
Ai-M61系列
›
地平线4遥测数据小工具_u8g2篇
1
2
/ 2 页
下一页
返回列表
地平线4遥测数据小工具_u8g2篇
[ 复制链接 ]
发布帖子
小灰灰
注册会员
2
主题
0
回帖
177
积分
注册会员
注册会员, 积分 177, 距离下一级还需 23 积分
注册会员, 积分 177, 距离下一级还需 23 积分
积分
177
私信
10人留言
楼主
Ai-M61系列
5331
10
2024-1-7 22:06:53
[i=s] 本帖最后由 小灰灰 于 2024-1-7 22:15 编辑 [/i]
> 本帖最后由 小灰灰 于 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** ```cmake 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 ```c 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 ```c 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的规格说明书]([https://docs.ai-thinker.com/_media/ai-m61-32s-kit_v1.1.1_product_specification_cn.pdf](https://docs.ai-thinker.com/_media/ai-m61-32s-kit_v1.1.1_product_specification_cn.pdf)) 初始化引脚后,初始化i2c ```c #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`**传递参数(设备地址、传输模式、缓冲区、缓冲区大小) ```c #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 > > 问就是差一行逼疯强迫症 ```c #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 ```c #define configTOTAL_HEAP_SIZE ((size_t)100 * 1024) ``` 添加一些函数,比如下面这个,获取当前task的剩余栈大小 ```c #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中配置延迟时间 ```c vTaskDelay(2000 / portTICK_PERIOD_MS); // 延迟2000ms ``` --- ## u8g2 u8g2需要实现至少两个函数,两个函数类型如下 ```c typedef uint8_t (*u8x8_msg_cb)(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); ``` 我自己的这里实现如下 > 其中struct screen是我为了实现多个oled显示屏加的(反正都写了,干脆多个好了 ```c 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部分,如果只需要一个屏幕,那就直接填函数进去就可以** #### 方法一 ```c 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); } ``` #### 方法二 ```c 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部分了(有空了就去写 ```c #include
#include
#include
#include
#include
#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](https://github.com/iuhiuhoaix/horizon4_showdata)
点赞
0
收藏
0
淘帖
0
────
0
人觉得很赞
────
本主题由 管理团队 于 2026-7-3 10:22 移动
回复
使用道具
举报
10 回复
电梯直达
正序浏览
倒序浏览
正序浏览
沙发
1084504793
回复
使用道具
举报
2024-1-7 22:12:32
赞
回复
评论
使用道具
举报
板凳
lazy
回复
使用道具
举报
2024-1-7 22:36:13
赞一下
回复
评论
使用道具
举报
地板
wukong50
回复
使用道具
举报
2024-1-8 07:51:07
赞
回复
评论
使用道具
举报
5
#
wukong50
回复
使用道具
举报
2024-1-8 08:14:40
赞一下
回复
评论
使用道具
举报
6
#
WT_0213
回复
使用道具
举报
2024-1-8 08:31:02
赞
回复
评论
使用道具
举报
7
#
爱笑
回复
使用道具
举报
2024-1-8 08:50:50
厉害了小灰灰
回复
评论
使用道具
举报
用心做好保姆工作
8
#
干簧管
回复
使用道具
举报
2024-1-8 09:45:03
赞
回复
评论
使用道具
举报
9
#
曹县
回复
使用道具
举报
2024-1-8 09:45:28
回复
评论
使用道具
举报
10
#
san
回复
使用道具
举报
2024-1-8 10:35:18
赞
回复
评论
使用道具
举报
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
立即登录
手机登录
点评
高级模式
本版积分规则
回帖并转播
回帖后跳转到最后一页
下一页 »
1
2
/ 2 页
下一页
今日推荐
Ai-Thinker Skills 正式发布!快来试试!
九章开发板疑问
🔌手把手!1T1R 毫米波测距演示怎么跑?接入与 Python 环境配置
ONELAB开发套件常见问题Q&A:距离、角度、速度、距离门设置
【九章MCP验证板】开发环境配置教程
AI-WV02-32S 铁板烧焊接翻车
不用写代码,先跑通这两个开源例程,感受毫米波能做什么
【体验】Ai-WV02-32S 这不就跑起来了么
【问题】Ai-WV02-32S 短路
【B站/视频号直播预告】莫工带你跑通 AI→MCP 协议→硬件执行全
热帖排行
Ai-Thinker Skills 正式发布!快来试试!
九章开发板疑问
🔌手把手!1T1R 毫米波测距演示怎么跑?接入与 Python 环境配置
ONELAB开发套件常见问题Q&A:距离、角度、速度、距离门设置
【九章MCP验证板】开发环境配置教程
TB-04模块 从机收不到主机信息
AI-WV02-32S 铁板烧焊接翻车
<Ai-WB2-12F>这个模组请教个问题-FTP下载
统计信息
会员数: 31170 个
话题数: 42256 篇
首页
分类
我的