本帖最后由 小灰灰 于 2024-1-7 22:15 编辑
本帖最后由 小灰灰 于 2024-1-7 22:06 编辑
挖个坑
收到开发板有一段时间了,这几天终于闲下来一点,为了快乐的玩地平线4,于是打算在显示器下面放个小屏幕,显示一些地平线4的遥测数据。手头有的小屏幕有ssd1306和st7735驱动的,鉴于u8g2移植比较简单,所以先用u8g2实现一下,lvgl嘛...就之后在说了。
本章涉及内容
项目说明
项目使用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