q1215200171 发布的帖子
-
【全志&OpenHarmony】全志两款支持OpenHarmony开发板在开放原子开源基金会OpenHarmony见面会实况分享
2021年12月28日,OpenAtom OpenHarmony开源见面会首站在江苏南京圆满举行。本次活动为OpenHarmony城市和高校全年巡回活动的首发站,以“融合行业需求,夯实关键技术”为主题,精彩呈现OpenHarmony 2021年度的共建成果及未来发展规划。
本次会议特别设立了Dev-Board-SIG分论坛,各家共建单位详尽阐述了OpenHarmony开发板目前的开发概况和未来规划。目前,在众家共建单位的努力下,小组已经有40余块开发板完成或计划完成基于OpenHarmony 3.0 LTS版本的移植适配工作,给OpenHarmony主线代码演进提供了源源不断的硬件基础。
全志也在本次见面会的Dev-Board-SIG分论坛上展出了两款支持OpenHarmony的开发板,分别为哪吒D1开发板、XR806开源鸿蒙开发板,除了本次大会上展出的开发板,全志T507等多个芯片平台的开发版也在适配OpenHarmony系统。
哪吒D1开发板
目前,主干代码里已经支持部分友商的芯片,计划在2022年支持ARM、MIPS、RISC-V等构架及多数主流芯片,全志D1也在即将支持的RISC-V框架芯片范围之内。因为它的CPU是RISC-V架构的,使用的是阿里平头哥的C906核心。中科院软件所于佳耕团队和全志一同进行OpenHarmony系统的移植,并已经完成适配工作。作为全志在线基于全志D1芯片定制的AIoT开发板,是全球首款支持64bit RISC-V指令集并支持Linux系统的可量产开发板。可以应用于科研教学、产品项目预研、开发爱好者DIY等。
XR806开源鸿蒙开发板
XR806开源鸿蒙开发板是在Dev-Board-SIG开发板展区展出的第二块全志的板子,也是全志首款适配OpenHarmony系统的开发板,XR806开发板使用的是全志自研的XR806芯片,支持wifi和ble,可以应用于智能家居、工业控制等场景的无线互联,同时也使用的是Arm-Star ARMv8-M MCU,sip了ddr和flash。高集成度的特点,可以帮助我们的客户和开发者最大化地降低硬件设计的复杂度,降低bom成本。同时支持efuse和security,留给了开发者更大的软件开发和设计空间。目前,XR806开发板代码已经通过XTS认证合入主仓。
仓库地址:https://gitee.com/moldy-potato-chips/devboard_device_allwinner_xr806千行百业加速数字化转型的当下,OpenHarmony将坚持”融合行业需求 夯实关键技术”的重要信念,全志也会持续输出优质产品,并进行OpenHarmony的适配工作,与开放原子开源基金会和众友商共建OpenHarmony繁荣生态,共享共赢新未来。
-
XR806开发板驱动6轴MPU6050 陀螺仪+加速度计及数据上传至上位机
一.开发环境
- ubuntu16.04虚拟机
- MPU6050陀螺仪
- 匿名上位机
二.软件
(1).I2C引脚
使用Xr806硬件I2C,需要在文件夹找到“/home/a/xr806_openharmony/device/xradio/xr806/xr_skylark/project/common/board/xr806_OHOS/borad_config.c"板级配置文件,可以看到共有两个I2C端口可用,i2c0和i2c1,这里是使用i2c0,引脚为A12,A13,复用通道F9。找好端口号,插上MPU6050。
(2).12C协议
鸿蒙已自带多种I2C协议,MPU6050需要写寄存器地址,必须选用能写HAL_I2C_Master_Transmit_Mem_IT()函数,写寄存器大小设为8位,但是连续写协议,发送数据长度设为1。(3).上位机协议
1:在调试过程中可以将某些标志位、寄存器、变量实时发回上位机,并在DEBUG页面显示。 2:通讯格式为:0x88 + 0xAD + len + num + DATA + SUM, len为num与DATA的总长度,num表示要改变哪个显示 状态,例如num=0x01即是要改变第一个LED,num=0x07即是改变第一个数字输出显示。当要改变LED时, DATA只需一字节,DATA=0x00表示关闭LED,大于0x00表示点亮LED;当要改变数字输出时,DATA需要两字 节, 表示 一个uint16数字,高字节在前。SUM为从0x88开始到SUM前一字节的和校验,uint8格式。 例如:发送 0x88 + 0xAD + 0x02 + 0x01 + 0x01 + 0x39 表示点亮第一个LED 发送 0x88 + 0xAD + 0x03 + 0x07 + 0x00 + 0x05 + 0x44 表示在第一个数字输出位置显示 5 。
3.源码
#include <stdio.h> #include "ohos_init.h" #include "kernel/os/os.h" #include "/home/zfy/xr806_openharmony/base/iot_hardware/peripheral/interfaces/kits/iot_gpio.h" //(8) #include "driver/chip/hal_i2c.h" static OS_Thread_t g_main_thread,g_main_thread2; #define GPIO_ID_PA21 21 #define I2C_SPEED (200000) #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #define ACCEL_XOUT_H 0x3B #define ACCEL_XOUT_L 0x3C #define ACCEL_YOUT_H 0x3D #define ACCEL_YOUT_L 0x3E #define ACCEL_ZOUT_H 0x3F #define ACCEL_ZOUT_L 0x40 #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 #define GYRO_XOUT_H 0x43 #define GYRO_XOUT_L 0x44 #define GYRO_YOUT_H 0x45 #define GYRO_YOUT_L 0x46 #define GYRO_ZOUT_H 0x47 #define GYRO_ZOUT_L 0x48 #define PWR_MGMT_1 0x6B #define WHO_AM_I 0x75 #define SlaveAddress 0x68 unsigned int i2c_id = 0; void mpu6050_send_data(short aacx,short aacy,short aacz,short gyrox,short gyroy,short gyroz,short p,short r,short y) { char str_buff[5]; unsigned char head_buff[]={0x88,0xAF,0x1C}; unsigned char end_buff[]={0x00,0x00,0x00,0x00}; unsigned char check_sum[1]={0}; unsigned char data[24]; check_sum[0]+=0x88+0xAF+0x1C; data[0]=aacx>>8&0xFF; data[1]=aacx&0xFF; data[2]=aacy>>8&0xFF; data[3]=aacy&0xFF; data[4]=aacz>>8&0xFF; data[5]=aacz&0xFF; data[6]=gyrox>>8&0xFF; data[7]=gyrox&0xFF; data[8]=gyroy>>8&0xFF; data[9]=gyroy&0xFF; data[10]=gyroz>>8&0xFF; data[11]=gyroz&0xFF; data[12]=0; data[13]=0; data[14]=0; data[15]=0; data[16]=0; data[17]=0; data[18]=r>>8&0xFF; data[19]=r&0xFF; data[20]=p>>8&0xFF; data[21]=p; data[22]=y>>8&0xFF; data[23]=y&0xFF; int i=0; for(i=0;i<24;i++){ check_sum[0]+=data[i]; } // printf("%s",head_buff); // printf("%s",data); // printf("%s",end_buff); // printf("%s",check_sum); for(int i=0;i<3;i++){ printf("%c",head_buff[i]);} for(int i=0;i<24;i++){ printf("%c",data[i]);} for(int i=0;i<4;i++){ printf("%c",end_buff[i]);} for(int i=0;i<1;i++){ printf("%c",check_sum[i]);} //uart_send_str(data,24); } short Acc[3],Gyro[3]; void GetData(unsigned char REG_Address,short data[3]) { unsigned char H[1]={0},L[1]={0}; //HAL_I2C_Master_Receive_Mem_IT(i2c_id, SlaveAddress, REG_Address+1, I2C_MEMADDR_SIZE_16BIT, L, 1); //return ((int)(H[0]<<8)+L[0]); for(int i=0;i<3;i++){ HAL_I2C_Master_Receive_Mem_IT(i2c_id, SlaveAddress, REG_Address+(i*2), I2C_MEMADDR_SIZE_8BIT, H, 1); HAL_I2C_Master_Receive_Mem_IT(i2c_id, SlaveAddress, REG_Address+(i*2+1), I2C_MEMADDR_SIZE_8BIT, L, 1); data[i]=(( short)(H[0]<<8))|L[0]; } } static void MainThread2(void *arg) { printf("LED test start\r\n"); IoTGpioInit(GPIO_ID_PA21); IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT); IoTI2cInit(i2c_id,I2C_SPEED); const unsigned char data[]={0x00}; const unsigned char data1[]={0x07}; const unsigned char data2[]={0x06}; const unsigned char data3[]={0x18}; const unsigned char data4[]={0x01}; printf("i2c test start\r\n"); int a=HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, PWR_MGMT_1, I2C_MEMADDR_SIZE_8BIT, data, 1); HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, PWR_MGMT_1, I2C_MEMADDR_SIZE_8BIT, data1, 1); OS_MSleep(1); HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, SMPLRT_DIV, I2C_MEMADDR_SIZE_8BIT, data2, 1); OS_MSleep(1); HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, GYRO_CONFIG, I2C_MEMADDR_SIZE_8BIT, data3, 1); OS_MSleep(1); HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, ACCEL_CONFIG, I2C_MEMADDR_SIZE_8BIT, data4, 1); OS_MSleep(1); while (1) { IoTGpioSetOutputVal(GPIO_ID_PA21, 1); OS_MSleep(10); IoTGpioSetOutputVal(GPIO_ID_PA21, 0); OS_MSleep(10); GetData(ACCEL_XOUT_H,Acc); GetData(GYRO_XOUT_H,Gyro); GetData(GYRO_XOUT_H,Gyro); mpu6050_send_data(Acc[0],Acc[1],Acc[2],Gyro[0],Gyro[1],Gyro[2],0,0,0); } } void LEDMain(void) { printf("LED Test Start\n"); if (OS_ThreadCreate(&g_main_thread2, "MainThread2", MainThread2, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } static void MainThread(void *arg) {int i=0; while (1) { printf("hello world:%d\n",i); LOS_Msleep(1000); i++; } } void HelloTestMain(void) { printf("Wifi Test Start\n"); if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } //SYS_RUN(HelloTestMain); SYS_RUN(LEDMain);
三、效果
文章转自极术社区:https://aijishu.com/a/1060000000288462
作者:zhai -
【FAQ】全志D1芯片FAQ汇总(你不知道的和你想知道的的这里都有)
01、【FAQ】全志D1芯片 如何解决Audiocodec使用S24_LE格式进行录音,软件分析波形异常的问题?
02、【FAQ】全志D1芯片 如何在 Linux Device Tree 中配置预留内存?
03、【FAQ】全志D1芯片 如何解决Gstreamer播放1080视频显示异常问题(重影)?
04、【FAQ】全志D1芯片 如何对D1主频进行调节?
05、【FAQ】全志D1芯片 如何解决Gstreamer:fb UI旋转(直接修改内核参数)后,sunxifbsink显示异常问题?
06、【FAQ】全志D1芯片 Tina 如何查看通过 procd init 脚本启动的应用输出到 stdout/stderr 的打印信息?
07、【FAQ】全志D1芯片 如何移植 rtl8821cu wifi 驱动到 Linux-5.4内核?
08、【FAQ】全志D1芯片 XR829扫卡失败问题排查
09、【FAQ】全志D1芯片 mp4(Xvid)视频文件播放花屏问题
10、【FAQ】全志D1芯片 如何解决在创建视频解码器后,未送入视频帧数据之前,cpu被占满的问题?
11、【FAQ】全志D1芯片 如何在休眠唤醒过程中通过-sunxi_dump-读写外设寄存器?
12、【FAQ】全志D1芯片 minigui如何显示鼠标?
13、【FAQ】全志D1芯片 uart测试用例(支持自发自收,板间收发,数据校验,收发时间统计)
14、【FAQ】全志D1芯片 如何在tina使用tplayerdemo 进行rtsp拉流说明?
15、【FAQ】全志全系列芯片 APST平台无法下载或者更新工具
16、【FAQ】全志 F系列/R系列/V系列 RTOS平台cache操作接口介绍
17、【FAQ】全志系列芯片如何把flash擦成空片?
18、【FAQ】持续更新...... -
【XR806开发板试用】TCP通信测试 && Ping 命令测试
1.工程准备
由于要使用wifi功能,直接从wlan_demo复制一份出来,然后修改。
源文件只留下 main.c 就可以了。BUILD.gn文件
import("//device/xradio/xr806/liteos_m/config.gni") static_library("app_mying") { configs = [] sources = [ "main.c", ] cflags = board_cflags include_dirs = board_include_dirs include_dirs += [ "//kernel/liteos_m/kernel/arch/include", "//utils/native/lite/include", "//foundation/communication/wifi_lite/interfaces/wifiservice", ] }
2. XR806 SDK
仔细看下xr806工程库的结构,
xr806的xr_skylark路径下属于芯片原生驱动库!
该路径下面有各种功能参考示例,如trustzone、net、ping、json等。
因此,直接参考xr_skylark\include下的内容,来实现一些简单的功能。
3.实现的功能
0)连接WiFi;
根据自己的SSID和PSK修改,代码直接copy原来的。1)ping服务器,进行联通性测试;
ping一下自己的服务器,根据需要进行修改。include下面有ping/ping.h文件。很简单的一个结构体(如下所示),看情况就是给个地址,设置一下参数,然后就可以实现ping命令的功能了。
struct ping_data { ip_addr_t sin_addr; /* server addr */ u32_t count; /* number of ping */ u32_t data_long; /* the ping packet data long */ u32_t interval; /* Wait interval seconds between sending each packet, default 1s */ u32_t timeout; /* Time to wait for a response, in seconds */ u32_t deadline; /* Specify a timeout, in seconds, ping thread will stop if timeout */ u32_t ttl; /* ttl ping only. Set the IP Time to Live. */ int run_flag; /* run flag, 0:stop 1:start */ ![CB84C5B5-489A-4f24-BD4D-67909D582B90.png](/assets/uploads/files/1640308813559-cb84c5b5-489a-4f24-bd4d-67909d582b90.png) }; s32_t ping(struct ping_data *data);
2)作为TCP客户端,连接server,发数据;
通过TCP连接服务器,发数据。
设置服务器信息
【地址】(比如:192.168.1.100)
【端口号】(比如5679)
net路径下面有lwip库,借此实现网络通信功能。代码里,通过宏定义的方式,将lwip_xxx改成了与linux下的soket API一样的接口。
参考正常的TCP_Client程序就行了。
3)读取一下xr_skylark里的cjson版本信息。
发现有cjson库,然后就随便测试一下。4.程序示例
#include <stdio.h> #include <string.h> #include <stdlib.h> #include "ohos_init.h" #include "driver/chip/hal_gpio.h" #include "kernel/os/os.h" #include "wifi_device.h" #include "cjson/cJSON.h" #include "net/ping/ping.h" #include "net/lwip-2.1.2/lwip/sockets.h" #include "net/lwip-2.1.2/lwip/tcp.h" #include "net/lwip-2.1.2/lwip/inet.h" #include "net/lwip-2.1.2/lwip/ip_addr.h" #define WIFI_DEVICE_CONNECT_AP_SSID "ChinaNet-111" #define WIFI_DEVICE_CONNECT_AP_PSK "111666111" #define GPIO_OUTPUT_PORT GPIO_PORT_A static OS_Thread_t g_main_thread; static void gpio_output_init(void) { GPIO_InitParam param; param.driving = GPIO_DRIVING_LEVEL_1; param.mode = GPIOx_Pn_F1_OUTPUT; param.pull = GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_OUTPUT_PORT, GPIO_PIN_21, ¶m);//led灯对应IO } static void gpio_output_ctl(uint8_t level) { HAL_GPIO_WritePin(GPIO_OUTPUT_PORT, GPIO_PIN_21, level ? GPIO_PIN_HIGH : GPIO_PIN_LOW); } void wifi_connect(void) { const char ssid_want_connect[] = WIFI_DEVICE_CONNECT_AP_SSID; const char psk[] = WIFI_DEVICE_CONNECT_AP_PSK; printf("\n=========== Connect Test Start ===========\n"); if (WIFI_SUCCESS != EnableWifi()) { printf("Error: EnableWifi fail.\n"); return; } printf("EnableWifi Success.\n"); if (WIFI_STA_ACTIVE == IsWifiActive()) printf("Wifi is active.\n"); OS_Sleep(1); if (WIFI_SUCCESS != Scan()) { printf("Error: Scan fail.\n"); return; } printf("Wifi Scan Success.\n"); OS_Sleep(1); WifiScanInfo scan_results[30]; unsigned int scan_num = 30; if (WIFI_SUCCESS != GetScanInfoList(scan_results, &scan_num)) { printf("Error: GetScanInfoList fail.\n"); return; } WifiDeviceConfig config = { 0 }; int netId = 0; int i; for (i = 0; i < scan_num; i++) { if (0 == strcmp(scan_results[i].ssid, ssid_want_connect)) { memcpy(config.ssid, scan_results[i].ssid, WIFI_MAX_SSID_LEN); memcpy(config.bssid, scan_results[i].bssid, WIFI_MAC_LEN); strcpy(config.preSharedKey, psk); config.securityType = scan_results[i].securityType; config.wapiPskType = WIFI_PSK_TYPE_ASCII; config.freq = scan_results[i].frequency; break; } } if (i >= scan_num) { printf("Error: No found ssid in scan_results\n"); return; } printf("GetScanInfoList Success.\n"); if (WIFI_SUCCESS != AddDeviceConfig(&config, &netId)) { printf("Error: AddDeviceConfig Fail\n"); return; } printf("AddDeviceConfig Success.\n"); if (WIFI_SUCCESS != ConnectTo(netId)) { printf("Error: ConnectTo Fail\n"); return; } printf("ConnectTo Success\n"); OS_Sleep(3); WifiLinkedInfo get_linked_res; if (WIFI_SUCCESS != GetLinkedInfo(&get_linked_res)) { printf("Error: GetLinkedInfo Fail\n"); return; } printf("GetLinkedInfo Success.\n"); printf("ssid: %s\n", get_linked_res.ssid); printf("bssid: "); for (int j = 0; j < WIFI_MAC_LEN; j++) { printf("%02X", get_linked_res.bssid[j]); } printf("\n"); printf("rssi: %d\n", get_linked_res.rssi); unsigned char get_mac_res[WIFI_MAC_LEN]; if (WIFI_SUCCESS != GetDeviceMacAddress(get_mac_res)) { printf("Error: GetDeviceMacAddress Fail\n"); return; } printf("GetDeviceMacAddress Success.\n"); for (int j = 0; j < WIFI_MAC_LEN - 1; j++) { printf("%02X:", get_mac_res[j]); } printf("%02X\n", get_mac_res[WIFI_MAC_LEN - 1]); } struct ping_data ping_t; //ping命令参数设置 void ping_init() { ip_addr_t server_ip; inet_aton("129.204.63.27", &server_ip); ping_t.sin_addr = server_ip; ping_t.count = 0xF; ping_t.data_long = 512; ping_t.timeout = 30; ping_t.run_flag = 1; } //TCP SOCKET int s; void tcp_test_init() { //socket create! s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //address info! struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(5679); inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr); //connect! if(connect(s,(void *)&server_addr,sizeof(server_addr)) < 0) { printf("connect tcp_server failed! \r\n"); } else { printf("connect tcp_server successfuly! \r\n"); } // send data send(s, "xr806\n", 7, 0); } static void MainThread(void *arg) { gpio_output_init(); wifi_connect(); ping_init(); ping(&ping_t); tcp_test_init(); char buf[32]; int cnt = 0; while(1) { sprintf(buf,"XR806:%s : %d \r\n",cJSON_Version(), cnt++); //向服务器发送数据 send(s,buf, sizeof(buf), 0); printf("%s:Hello XR806 \r\n",__func__); gpio_output_ctl(1); OS_Sleep(1); gpio_output_ctl(0); OS_Sleep(1); } } void WifiTestMain(void) { printf("Wifi Test Start\r\n"); if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(WifiTestMain);
5.结果展示
通过网络调试工具,建立TCP服务器,接收到了xr806发来的数据。
串口打印信息[net INF] netif (IPv4) is up [net INF] address: 192.168.1.110 [net INF] gateway: 192.168.1.1 [net INF] netmask: 255.255.255.0 [net INF] msg <network IPv6 state> GetLinkedInfo Success. ssid: ChinaNet-111 bssid: 5475956E3374 rssi: 110 GetDeviceMacAddress Success. 9C:9E:49:BA:5B:01 PING 129.204.63.27 520 bytes of data. Request timeout for icmp_seq=1 512 bytes from 129.204.63.27: icmp_seq=2 time=43 ms 512 bytes from 129.204.63.27: icmp_seq=3 time=43 ms 512 bytes from 129.204.63.27: icmp_seq=4 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=5 time=43 ms 512 bytes from 129.204.63.27: icmp_seq=6 time=45 ms 512 bytes from 129.204.63.27: icmp_seq=7 time=46 ms 512 bytes from 129.204.63.27: icmp_seq=8 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=9 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=10 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=11 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=12 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=13 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=14 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=15 time=43 ms --- 129.204.63.27 ping statistics --- 15 packets transmitted, 14 received, 6% packet loss, time 672ms rtt min/avg/max/mdev = 43/43/46/3 ms connect tcp_server successfuly! MainThread:Hello XR806 MainThread:Hello XR806
文章转自极术社区:https://aijishu.com/a/1060000000286996
作者:Pingyang @Pingyang -
【XR806开发板试用】通过MQTT实现手机远程实现PWM控灯
一、例程编译、烧录确认
首先按照全志在线文档平台的点灯教程确保能正常编译、烧录和点灯:https://xr806.docs.aw-ol.com/...
确保例程没问题后,我们再改造例程,实现我们想要的功能二、代码编写
我们将led工程复制一份改文件夹名为mydemo,目录结构如下mydemo ├── BUILD.gn └── src └── main.c
然后BUILD.gn内容增加头文件引用,部分配置如下:
static_library("app_mydemo") { ... include_dirs += [ "//kernel/liteos_m/kernel/arch/include", "//base/iot_hardware/peripheral/interfaces/kits", ".", "//utils/native/lite/include", "//foundation/communication/wifi_lite/interfaces/wifiservice", "//device/xradio/xr806/xr_skylark/project" ] }
接下来是main.c,内容如下:
#include <stdio.h> #include <string.h> #include "ohos_init.h" #include "kernel/os/os.h" #include "iot_gpio.h" #include "wifi_device.h" #include "common/framework/net_ctrl.h" #include "net/mqtt/MQTTClient-C/MQTTClient.h" #include "driver/chip/hal_pwm.h" static OS_Thread_t g_main_thread; static OS_Thread_t g_mqtt_thread; #define PWM_OUTPUT_CHL PWM_GROUP1_CH2 #define PWM_OUTPUT_MODE PWM_CYCLE_MODE #define WIFI_DEVICE_CONNECT_AP_SSID "ssid"//这里填你家路由器的SSID #define WIFI_DEVICE_CONNECT_AP_PSK "pwm"//这里填你家路由器的PWD #define MQTT_DEMO_CLIENT_ID "12345" #define MQTT_DEMO_HOST_NAME "broker-cn.emqx.io"//这个是免费调试用的MQTT服务器地址 #define MQTT_DEMO_PORT "1883" #define MQTT_DEMO_USERNAME "12345678" #define MQTT_DEMO_PASSWORD "12345678" #define MQTT_RESP_TOPIC "/to/master/light" //手机发出来的TOPIC #define MQTT_RECV_TOPIC "/to/slave/light" //设备发出来的TOPIC #define MQTT_DEMO_BUF_SIZE (2*1024) static MQTTPacket_connectData mqtt_demo_connectData = MQTTPacket_connectData_initializer; static Client mqtt_demo_client; static Network mqtt_demo_network; static int max_duty_ratio = 0; static int mqtt_demo_publish(char *topic, char *msg) ; static int mqtt_demo_init(void) { char *send_buf; char *recv_buf; mqtt_demo_connectData.clientID.cstring = MQTT_DEMO_CLIENT_ID; mqtt_demo_connectData.keepAliveInterval = 30; // 30s mqtt_demo_connectData.cleansession = 0; mqtt_demo_connectData.MQTTVersion = 4; //Version of MQTT 3.1.1 send_buf = malloc(MQTT_DEMO_BUF_SIZE); if (send_buf == NULL) { printf("no memory\n"); return -1; } recv_buf = malloc(MQTT_DEMO_BUF_SIZE); if (recv_buf == NULL) { free(send_buf); printf("no memory\n"); return -1; } /* init network */ NewNetwork(&mqtt_demo_network); /* init mqtt client object */ MQTTClient(&mqtt_demo_client, &mqtt_demo_network, 6000, (unsigned char *)send_buf, MQTT_DEMO_BUF_SIZE, (unsigned char *)recv_buf, MQTT_DEMO_BUF_SIZE); /* set username and password */ mqtt_demo_connectData.username.cstring = MQTT_DEMO_USERNAME; mqtt_demo_connectData.password.cstring = MQTT_DEMO_PASSWORD; return 0; } static int mqtt_demo_connect(char *host_name, char *host_port) { int ret = -1; ret = ConnectNetwork(&mqtt_demo_network, host_name, atoi(host_port)); if (ret != 0) { printf("mqtt connect faild, ret:%d, host:%s, port:%s\n", ret, host_name, host_port); goto exit; } ret = MQTTConnect(&mqtt_demo_client, &mqtt_demo_connectData); if (ret != 0) { printf("mqtt connect faild, ret:%d\n", ret); mqtt_demo_network.disconnect(&mqtt_demo_network); goto exit; } printf("mqtt connected\n"); exit: return ret; } static void mqtt_demo_msg_cb(MessageData *data) { printf("get a message, topic: %.*s, msg: %.*s\n", data->topicName->lenstring.len, data->topicName->lenstring.data, data->message->payloadlen, (char *)data->message->payload); if(!strncmp(data->topicName->lenstring.data, "/to/slave/light", 15) && data->message->payloadlen) { char *payload = data->message->payload; char str[8] = ""; int max_len = data->message->payloadlen > 3 ? 3 : data->message->payloadlen; strncpy(str, payload, max_len); int duty = atoi(str); HAL_Status status = HAL_PWM_ChSetDutyRatio(PWM_OUTPUT_CHL, duty * max_duty_ratio / 100); if (status != HAL_OK) printf("%s(): %d, PWM set duty ratio error\n", __func__, __LINE__); if(duty) { mqtt_demo_publish(MQTT_RESP_TOPIC, "light on"); } else { mqtt_demo_publish(MQTT_RESP_TOPIC, "light off"); } } } static int mqtt_demo_subscribe(char *topic) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTSubscribe(&mqtt_demo_client, topic, 0, mqtt_demo_msg_cb); if (ret != 0) printf("mqtt subscribe faild ret:%d\n", ret); } return ret; } static int mqtt_demo_unsubscribe(char *topic) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTUnsubscribe(&mqtt_demo_client, topic); if (ret != 0) printf("mqtt unsubscribe faild, ret:%d\n", ret); } return ret; } static int mqtt_demo_publish(char *topic, char *msg) { int ret = -1; MQTTMessage message; memset(&message, 0, sizeof(message)); message.qos = 0; message.retained = 0; /* disable retain the message in server */ message.payload = msg; message.payloadlen = strlen(msg); ret = MQTTPublish(&mqtt_demo_client, topic, &message); if (ret != 0) printf("mqtt publish faild, ret:%d\n", ret); return ret; } static int mqtt_demo_disconnect(void) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTDisconnect(&mqtt_demo_client); if (ret != 0) printf("mqtt disconnect fail, ret:%d\n", ret); mqtt_demo_network.disconnect(&mqtt_demo_network); } return ret; } static void mqtt_demo_deinit(void) { if (mqtt_demo_client.buf) { free(mqtt_demo_client.buf); mqtt_demo_client.buf = NULL; } if (mqtt_demo_client.readbuf) { free(mqtt_demo_client.readbuf); mqtt_demo_client.readbuf = NULL; } } static void mqtt_task(void *arg) { int ret; int reconnect_times = 0; mqtt_demo_init(); ret = mqtt_demo_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT); if (ret != 0) goto exit; ret = mqtt_demo_subscribe(MQTT_RECV_TOPIC); if (ret != 0) goto exit; mqtt_demo_publish(MQTT_RESP_TOPIC, "light ready"); while (1) { ret = MQTTYield(&mqtt_demo_client, 300); if (ret != 0) { printf("mqtt yield err, ret:%d\n", ret); reconnect: printf("mqtt reconnect\n"); mqtt_demo_disconnect(); ret = mqtt_demo_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT); if (ret != 0) { reconnect_times++; if (reconnect_times > 5) goto exit; OS_MSleep(5000); //5s goto reconnect; } } } exit: mqtt_demo_unsubscribe(MQTT_RECV_TOPIC); mqtt_demo_disconnect(); mqtt_demo_deinit(); OS_ThreadDelete(&g_mqtt_thread); } static void net_cb(uint32_t event, uint32_t data, void *arg) { uint16_t type = EVENT_SUBTYPE(event); switch (type) { case NET_CTRL_MSG_NETWORK_UP: printf("NET_CTRL_MSG_NETWORK_UP\n"); if (!OS_ThreadIsValid(&g_mqtt_thread)) { OS_ThreadCreate(&g_mqtt_thread, "connect_to_server_task", mqtt_task, (void *)NULL, OS_THREAD_PRIO_APP, (8 * 1024)); } break; case NET_CTRL_MSG_NETWORK_DOWN: break; default: break; } } static void MainThread(void *arg) { printf("MainThread start\r\n"); HAL_Status status = HAL_ERROR; PWM_ClkParam clk_param; PWM_ChInitParam ch_param; clk_param.clk = PWM_CLK_HOSC; clk_param.div = PWM_SRC_CLK_DIV_1; status = HAL_PWM_GroupClkCfg(PWM_OUTPUT_CHL, &clk_param); if (status != HAL_OK) printf("%s(): %d, PWM group clk config error\n", __func__, __LINE__); ch_param.hz = 1000; ch_param.mode = PWM_OUTPUT_MODE; ch_param.polarity = PWM_HIGHLEVE; max_duty_ratio = HAL_PWM_ChInit(PWM_OUTPUT_CHL, &ch_param); if (max_duty_ratio == -1) printf("%s(): %d, PWM ch init error\n", __func__, __LINE__); printf("max_duty_ratio=%d\n", max_duty_ratio); status = HAL_PWM_ChSetDutyRatio(PWM_OUTPUT_CHL, 0); if (status != HAL_OK) printf("%s(): %d, PWM set duty ratio error\n", __func__, __LINE__); status = HAL_PWM_EnableCh(PWM_OUTPUT_CHL, PWM_OUTPUT_MODE, 1); if (status != HAL_OK) printf("%s(): %d, PWM ch enable error\n", __func__, __LINE__); if (WIFI_SUCCESS != EnableWifi()) { printf("Error: EnableWifi fail\n"); return; } OS_Sleep(1); if (WIFI_SUCCESS != Scan()) { printf("Error: Scan fail.\n"); return; } OS_Sleep(3);//这里为了方便用延时,实际用回调更好,否则3秒可能不够 const char ssid_want_connect[] = WIFI_DEVICE_CONNECT_AP_SSID; const char psk[] = WIFI_DEVICE_CONNECT_AP_PSK; WifiScanInfo scan_results[30]; unsigned int scan_num = 30; if (WIFI_SUCCESS != GetScanInfoList(scan_results, &scan_num)) { printf("Error: GetScanInfoList fail.\n"); return; } WifiDeviceConfig config = { 0 }; int netId = 0; int i; for (i = 0; i < scan_num; i++) { printf("ssid: %s ", scan_results[i].ssid); printf("securityType: %d\n", scan_results[i].securityType); if (0 == strcmp(scan_results[i].ssid, ssid_want_connect)) { memcpy(config.ssid, scan_results[i].ssid, WIFI_MAX_SSID_LEN); memcpy(config.bssid, scan_results[i].bssid, WIFI_MAC_LEN); strcpy(config.preSharedKey, psk); config.securityType = scan_results[i].securityType; config.wapiPskType = WIFI_PSK_TYPE_ASCII; config.freq = scan_results[i].frequency; break; } } if (i >= scan_num) { printf("Error: No found ssid in scan_results\n"); return; } if (WIFI_SUCCESS != AddDeviceConfig(&config, &netId)) { printf("Error: AddDeviceConfig Fail\n"); return; } printf("Config Success\n"); if (WIFI_SUCCESS != ConnectTo(netId)) { printf("Error: ConnectTo Fail\n"); return; } observer_base *net_ob; net_ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK, NET_CTRL_MSG_ALL, net_cb, NULL); if (net_ob == NULL) return; if (sys_ctrl_attach(net_ob) != 0) return; while (1) { OS_MSleep(500); } } void LEDMain(void) { if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(LEDMain);
三、配置上层的BUILD.gn,并编译
部分内容如下:group("ohosdemo") { deps = [ #"hello_demo:app_hello", #"iot_peripheral:app_peripheral", #"wlan_demo:app_WlanTest", "mydemo:app_mydemo", ] }
然后执行hb build -f,编译成功(若编译失败见论坛有解决方案)
然后烧录到开发板上四、运行实例
烧录完成后,开发板重新上电,输出如下日志==================================================================== Hello! OpenHarmony! System tag : OpenHarmony 1.1.2_LTS ==================================================================== use default flash chip mJedec 0x0 [FD I]: mode: 0x10, freq: 96000000Hz, drv: 0 [FD I]: jedec: 0x0, suspend_support: 1 mode select:e wlan information =================================================== firmware: version : R0-XR_C07.08.52.65_02.84 May 27 2021 11:41:33-Y02.84 buffer : 8 driver: version : XR_V02.05 mac address: in use : 1c:98:c9:bc:50:01 in use : 1c:98:c9:bc:50:02 ==================================================================== wlan mode:a [VFS INF] LittleFS mount success. platform information =============================================== XR806 SDK v1.2.0 Dec 13 2021 13:06:05 heap space [0x2238d0, 0x24bc00), size 164656 cpu clock 160000000 Hz HF clock 40000000 Hz sdk option: XIP : enable INT LF OSC : enable SIP flash : enable mac address: efuse : 80:74:84:21:38:8e in use : 1c:98:c9:bc:50:01 ==================================================================== hiview init success.MainThread start max_duty_ratio=40000 console init success [net INF] no need to switch wlan mode 0 [net INF] msg <wlan scan success> ssid: TP-LINK_A668 securityType: 2 Config Success [net INF] no need to switch wlan mode 0 en1: Trying to associate with a4:c7:4b:71:f9:84 (SSID='XXXXXX' freq=2462 MHz) en1: Associated with a4:c7:4b:71:f9:84 en1: WPA: Key negotiation completed with a4:c7:4b:71:f9:84 [PTK=CCMP GTK=CCMP] en1: CTRL-EVENT-CONNECTED - Connection to a4:c7:4b:71:f9:84 completed [id=0 id_str=] [net INF] msg <wlan connected> [net INF] netif is link up [net INF] start DHCP... [net INF] IPv6 addr state change: 0x0 --> 0x1 [net INF] msg <> [net INF] netif (IPv4) is up [net INF] address: 192.168.3.48 [net INF] gateway: 192.168.3.1 [net INF] netmask: 255.255.255.0 [net INF] msg <network IPv6 state> NET_CTRL_MSG_NETWORK_UP WAR drop=1117, fctl=0x00d0. mqtt connected get a message, topic: /to/slave/light, msg: 0
可以看到,开发板成功连接路由器,并接入了MQTT服务器
五、手机测试
本人的手机是Iphone,也不会手机APP开发,于是我们下载了一个叫MQTTTool的第三方APP验证点灯,其界面如下:Host和Port填对,由于是免费开放的MQTT服务器,其他参数任意,设置好以后点Connect即可连接上MQTT服务器。然后底部切换至Subscribe栏,订阅主题:
若手机比设备先连接服务器并订阅相关主题,那么你会在订阅栏收到topic为/to/master/light的消息:light ready,这是因为设备连上服务器就会发布该topic。
接下来是发布主题,我们往/to/slave/light主题发送一个0~100的数字,就会让设备以这个数字为占空比控制LED灯亮灭,如0表示灭灯,50表示50%的占空比驱动LED灯,100表示100%的占空比驱动LED灯。同时设备收到LED控制指令后,若占空比不为0,则发送消息light on,否则发送消息light off
部分日志如下:
WAR drop=1117, fctl=0x00d0. mqtt connected get a message, topic: /to/slave/light, msg: 0 [net INF] IPv6 addr state change: 0x0 --> 0x1 [net INF] msg <> get a message, topic: /to/slave/light, msg: 100 get a message, topic: /to/slave/light, msg: 100 get a message, topic: /to/slave/light, msg: 50
以上就是一个手机远程给LED进行PWM调光的应用实例
本帖转自极术社区:https://aijishu.com/a/1060000000284320
作者:ctspot -
小巧精湛!基于D1s的全开源芒果派-哪吒MQ正式开售!
还记得之前的哪吒新品三连发吗?今天就来为你送上打开第二款新品宝箱的钥匙。
没错,这块上线即售空的新板子,就是之前预告过的哪吒新品三连发中的其中一块全新开发板:
芒果派-哪吒MQ
(MangoPi-Nezha MQ)芒果派-哪吒MQ(MangoPi-Nezha MQ)是芒果派(MangoPi)针对全志D1s设计的小型RISCV-Linux原型板,一体化极简设计,可以应用于屏显类AIoT产品。
量产版的型号主要有以下两种:
- MPi-MQ1:基础版,无NAND FLASH,无WiFi
- MPi-MQ1W:WiFi版,无NAND FLASH,有WiFi
可以看到两个型号最主要的区别就体现在WiFi模块之上,而WiFi型号的板子早已抢购一空,库存仅剩下基础版(悄悄透露,文末有下一批板子的消息),当然买到基础版的小伙伴也不用担心,基础版本虽然不带WiFi,但WiFi周围器件/ipex等都是齐的,还配备了MPi-MQ-GW2:双网口扩展板(PoP安装)的配件,通过这些都可以自行焊接实现WiFi功能。
参数配置
芒果派-哪吒MQ搭载的D1s是全志针对智能解码市场推出的高性价比AIoT芯片。它使用64bit RISC-V架构的阿里平头哥C906处理器,区别于D1芯片,D1s内置了64M DDR2,支持Linux系统,可以支持H.265,、H.264、MPEG-1/2/4、JPEG等全格式视频解码,支持ADC/DAC/I2S/PCM/DMIC/OWA等多种音频接口。
芒果派-哪吒MQ底板规格参数
- 主控D1s(D1芯片内置64MB运存)
- USB-OTG Type-C形式(fel方式刷机、接U盘、键盘、摄像头、usb网卡等)
- USB-HOST Type-C形式(接U盘、键盘、摄像头、usb网卡等)
- 22Pin扩展排针 x2(全Pin引出)
- 可焊接Nand/Nor FLASH
- TF卡槽
- 板载基于RTL8189的WiFi
- 15P通用树莓派DSI FPC排座
- 40P通用RGB FPC排座(内含4线电阻触摸接口)
- 6P通用电容触摸FPC排座
- 板载MIC*1
- 24Pin DVP接口
- BOOT按键、复位按键
- 4x4cm迷你尺寸,4个固定装配脚
得益于D1s芯片加持,DSI接口的强大功能,也是赋予了麻雀点屏神器的头衔,板载WiFi以及双Type-C的接口也显示出紧跟潮流的设计理念,紧跟潮流的同时也保留了芒果派一贯的优良传统,独立BOOT按键、超高集成度......都是麻雀虽小,五脏俱全的最佳佐证。
kicad画的PCB渲染图秉持着开源的理念,麻雀作为一款全开源的产品,不仅会在近日于官网处开源板子的所有相关参数资源,在硬件方面,设计走的是kicad路线,近期也会上传到github。
系统适配
目前系统适配的还是全志官方的Tina Linux,麻雀主要跑Linux+LVGL或QT,当然debian也是可以在麻雀上跑起来的。除了最基本的Tina Linux,芒果派也针对麻雀进行了xboot和RTT-Smart的适配。
xboot针对麻雀进行了适配,对于裸机感兴趣的可以用xboot。xboot不仅仅是一款功能强大、可移植性强、代码复用率高的嵌入式系统bootloader,而且还是一款SOC片上系统应用软件执行引擎,无需复杂的操作系统,就可以直接执行。
xboot基本特性
- 支持文件系统
- 支持lua虚拟机
- 支持各种协议栈
- 支持矢量图形库,矢量字体
- 支持各种现代GUI控件,以及动效
- 多平台支持
- 各种总线驱动,UART,I2C,SPI等等
- 各种设备驱动,GPIO,PWM,IRQ,CLK,LED,BUZZER,VIBRATOR,WATCHDOG,RNG,FRAMEBUFFER,RTC等
- 支持用lua编写应用软件,包含高等级API,可直接操作各种硬件抽象接口
- 应用软件平台无关,一次编写,到处运行
RTT那边针对麻雀适配了RTT-Smart(RT-Thread Smart),适合于经常使用RTOS的人。RT-Thread Smart 定位于成为一个专业的面向实时应用场合的高性能混合微内核操作系统。填补传统 RTOS 和大型操作系统 Linux 之间的空白,在实时性、成本、安全性、启动速度等方面取得最佳的平衡。
RT-Thread Smart七大优点
- 启动速度最快可在几百毫秒以内
- 抢占式调度内核,任务响应性能相比 Linux 更加优秀
- 支持POSIX API 规范,极大程度降低 Linux 开源软件的移植成本
- OS占用内存空间以及Flash空间极小,可最大化节约物料成本
- 支持Windows下开发应用程序,开发环境更加友好
- 系统和应用分离,方便应用程序和系统单独发布、单独升级
- 重大组件和服务运行在用户态,操作系统更加轻量、安全
芒果派-哪吒MQ Pro
作为芒果派-哪吒MQ基础款的升级版,除了外观方面保密,其他属性都已悉数公开,芒果派-哪吒MQ Pro正式上线时间也初定在十二月底。
- 主控D1芯片
- 512M or 1GB 运存
- USB-OTG Type-C形式(fel方式刷机、接U盘、键盘、摄像头、usb网卡等)
- USB-HOST Type-C形式(接U盘、键盘、摄像头、usb网卡等)
- 40Pin扩展排针
- TF卡槽
- 板载基于AP6212/RTL8723的WiFi/BT
- Mini HDMI接口
- 15P通用树莓派DSI FPC排座
- 24Pin DVP接口
- 列表外观保密
从参数方面来看,外观保密的原因是进行了大改,在小麻雀开售后,b站也有很多小伙伴留言想直接跑发行版(debian/ubuntu)的,也就是说很多人需要一款rv架构的linux小电脑,那使用D1作为主控,目的就会为了将内存打算做到512~1G,带上HDMI,同时保留小巧的属性。重点实现了一体化设计,简单易用,那样可以实现更广的人群的覆盖。
芒果派-哪吒MQ上线情况
已经有不少小伙伴收到了第一批发售的板子,有人点起了灯,有人跑起了LVGL demo,还有人将自己开发的系统移植到了麻雀上,看到这里是不是都后悔自己没能买到第一波的麻雀板子。
没抢到板子的小伙伴不要着急,下一批的物料已经在准备之中了,预计生产的数量也是远远超出上一次,焊接厂排期也是在今年开工,所以在不久的将来就会有一批全新、大量的板子上架,全志在线也会和芒果派持续合作,输出更小巧精湛,高性价比的板子。芒果派 哪吒MQ MangoPi Nezha MQ 麻雀 哪吒mini 全志D1s 开发板:
https://item.taobao.com/item.htm?id=638644511420
五寸电阻屏/电容屏:
https://item.taobao.com/item.htm?id=587925184119
芒果派 哪吒MQ 文档
https://mangopi.org/zh/mangopi_mq微信公众号推文直通车:https://mp.weixin.qq.com/s/QPhxg84J1BvJJ8ODq4njUw
-
【XR806开发板试用】系列之二 - I2C外设使用及控制OLED屏显示
前言
XR806硬件上支持SPI,I2C等其他外设接口,且DDR和FLASH,满足常见应用场景的开发,适合开发者进行方案评估、DIY或小规模产品研发使用。本篇文章,将使用到I2C接口,去控制OLED屏幕的显示。OLED屏幕规格: 0.96英寸 主控SSD1306 I2C接口 地址 0x3C XR806外设:I2C1
创建工程
参考device/xradio/xr806/ohosdemo目录下的wlan_demo,拷贝wlan_demo为xr806_oled,并同步修改ohosdemo和xr806_oled目录下的BUILD.gn。
主要修改如下:
1、device/xradio/xr806/ohosdemo/BUILD.gn
group("ohosdemo") { deps = [ #"hello_demo:app_hello", #"iot_peripheral:app_peripheral", #"wlan_demo:app_WlanTest", "xr806_oled:app_oled", #增加app_oled目标编译 ] }
2、device/xradio/xr806/ohosdemo/xr806_oled/BUILD.gn
static_library("app_oled") { configs = [] sources = [ "main.c", ] cflags = board_cflags include_dirs = board_include_dirs include_dirs += [ ".", "thirdparty/ssd1306/ssd1306", "//utils/native/lite/include", "//foundation/communication/wifi_lite/interfaces/wifiservice", ] deps = [ "thirdparty/ssd1306/ssd1306:oled_ssd1306", ] }
注意:
- static_library代表生成静态库(.a)文件,其中包含main.c的静态库必须是app_打头,如app_hello,否则虽然可以编译成功,但无法生效;
- ~~xr806_oled/BUILD.gn中静态库app_oled的命名,需要和ohosdemo/BUILD.gn中的一致;
- thirdparty/ssd1306/ssd1306:oled_ssd1306 为依赖的开源库
工程编译
创建工程后,如果非首次编译,执行以下命令便可以编译:hb build
编译如果遇到以下错误:
[OHOS ERROR] /* [OHOS ERROR] * [OHOS ERROR] * Automatically generated file; DO NOT EDIT. [OHOS ERROR] * XR806 SDK Configuration [OHOS ERROR] * [OHOS ERROR] */ [OHOS ERROR] /* [OHOS ERROR] * [OHOS ERROR] * Automatically generated file; DO NOT EDIT. [OHOS ERROR] * XR806 SDK Configuration [OHOS ERROR] * [OHOS ERROR] */ [OHOS ERROR] { [OHOS ERROR] "magic" : "AWIH", [OHOS ERROR] "version" : "0.5", [OHOS ERROR] "image" : {"max_size": "1532K"}, [OHOS ERROR] "section" :[ [OHOS ERROR] {"id": "0xa5ff5a00", "bin" :"boot_40M.bin", "cert": "null", "flash_offs": "0K", "sram_offs": "0x00230000", "ep": "0x00230101", "attr":"0x1"}, [OHOS ERROR] {"id": "0xa5fe5a01", "bin" :"app.bin", "cert": "null", "flash_offs": "32K", "sram_offs": "0x00201000", "ep": "0x00201101", "attr":"0x1"}, [OHOS ERROR] {"id": "0xa5fd5a02", "bin" :"app_xip.bin", "cert": "null", "flash_offs": "99K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x2"}, [OHOS ERROR] {"id": "0xa5fa5a05", "bin" :"wlan_bl.bin", "cert": "null", "flash_offs": "1170K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, [OHOS ERROR] {"id": "0xa5f95a06", "bin" :"wlan_fw.bin", "cert": "null", "flash_offs": "1173K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, [OHOS ERROR] {"id": "0xa5f85a07", "bin" :"sys_sdd_40M.bin", "cert": "null", "flash_offs": "1198K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, [OHOS ERROR] {} [OHOS ERROR] ] [OHOS ERROR] } [OHOS ERROR] [OHOS ERROR] make[2]: *** [../../../../project/project.mk:520:image] 错误 255 [OHOS ERROR] make[2]: 离开目录“/home/algo/openharmony/xr806/device/xradio/xr806/xr_skylark/project/demo/audio_demo/gcc” [OHOS ERROR] make[1]: *** [../../../../project/project.mk:493:__build] 错误 2 [OHOS ERROR] make[1]: 离开目录“/home/algo/openharmony/xr806/device/xradio/xr806/xr_skylark/project/demo/audio_demo/gcc” [OHOS ERROR] make: *** [Makefile:164:build] 错误 2 [OHOS ERROR] you can check build log in /home/algo/openharmony/xr806/out/xr806/wifi_skylark/build.log [OHOS ERROR] /home/algo/.local/bin/ninja -w dupbuild=warn -C /home/algo/openharmony/xr806/out/xr806/wifi_skylark failed, return code is 1
执行以下命令后,再次编译即可:
cp device/xradio/xr806/xr_skylark/project/demo/audio_demo/image/xr806/image_auto_cal.cfg device/xradio/xr806/xr_skylark/project/demo/audio_demo/image/xr806/image.cfg
编译后生成的镜像,便可以烧录验证。
注:以上基础工程是基于wlan_demo,oled屏幕显示需要使用I2C外设和移植oled库
库移植
其实XR806本身自带了OLED主控为SSD1306的驱动(采用的是SPI接口方式),移植基于I2C接口的库也相对简单,可以参考开源库harmonyos-ssd1306,将其中的I2C相关头文件和API替换为XR806 OpenHarmony中的相关头文件和API,编译通过即可。其中涉及到BUID.gn的修改如下:
static_library("oled_ssd1306") { sources = [ "ssd1306.c", "ssd1306_fonts.c", ] include_dirs = [ ".", "//kernel/liteos_m/kernel/arch/include", "//utils/native/lite/include", "//base/iot_hardware/peripheral/interfaces/kits", ] }
开源库主要修改如下:
#include "iot_i2c.h" #include "iot_errno.h" /** * @brief Defines I2C data transmission attributes. */ typedef struct { /** Pointer to the buffer storing data to send */ unsigned char *sendBuf; /** Length of data to send */ unsigned int sendLen; /** Pointer to the buffer for storing data to receive */ unsigned char *receiveBuf; /** Length of data received */ unsigned int receiveLen; } IotI2cData; static uint32_t ssd1306_SendData(uint8_t* data, size_t size) { uint32_t id = SSD1306_I2C_IDX; IotI2cData i2cData = {0}; i2cData.sendBuf = data; i2cData.sendLen = size; return IoTI2cWrite(id, SSD1306_I2C_ADDR, i2cData.sendBuf, i2cData.sendLen); }
ssd1306.h头文件定义SSD1306_I2C_IDX为1
显示程序
程序部分参考了上面提到的OLED库,完整的测试程序,可以参考harmonyos-ssd1306里的example./* * Copyright (c) 2021-2031, AlgoIdeas * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-12-13 AlgoIdeas the first version */ #include <stdio.h> #include "ohos_init.h" #include "kernel/os/os.h" #include "ssd1306.h" #define OLED_I2C_BAUDRATE 100000 static OS_Thread_t g_main_thread; static void DrawChinese(void) { const uint32_t W = 12, H = 12, S = 16; uint8_t fonts[][24] = { { /*-- ID:0,字符:"您",ASCII编码:C4FA,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/ 0x14,0x00,0x24,0x00,0x2F,0xF0,0x71,0x20,0xA5,0x40,0x29,0x20,0x33,0x10,0x20,0x00, 0x54,0x40,0x52,0xA0,0x90,0x90,0x0F,0x80, },{ /*-- ID:1,字符:"好",ASCII编码:BAC3,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/ 0x20,0x00,0x27,0xE0,0x20,0x40,0xF8,0x80,0x48,0x80,0x48,0xA0,0x57,0xF0,0x50,0x80, 0x30,0x80,0x28,0x80,0x4A,0x80,0x81,0x00, },{ /*-- ID:2,字符:"鸿",ASCII编码:BAE8,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/ 0x00,0x40,0x80,0x80,0x5D,0xE0,0x09,0x20,0xC9,0xA0,0x09,0x60,0x29,0x00,0xCD,0xF0, 0x58,0x10,0x43,0xD0,0x40,0x10,0x40,0x60, },{ /*-- ID:3,字符:"蒙",ASCII编码:C3C9,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/ 0x09,0x00,0x7F,0xE0,0x09,0x00,0x7F,0xF0,0x80,0x10,0x7F,0xE0,0x0C,0x40,0x32,0x80, 0xC7,0x00,0x0A,0x80,0x32,0x70,0xC6,0x20 } }; ssd1306_Fill(Black); for (size_t i = 0; i < sizeof(fonts)/sizeof(fonts[0]); i++) { ssd1306_DrawRegion(i * H + 32, 26, W, H, fonts[i], sizeof(fonts[0]), S); } ssd1306_UpdateScreen(); sleep(1); } static void MainThread(void *arg) { IoTI2cInit(SSD1306_I2C_IDX, OLED_I2C_BAUDRATE); usleep(20*1000); printf("ssd1306_Init.\n"); ssd1306_Init(); ssd1306_Fill(Black); ssd1306_SetCursor(22, 27); ssd1306_DrawString("Hello XR806!", Font_7x10, White); uint32_t start = HAL_GetTick(); ssd1306_UpdateScreen(); uint32_t end = HAL_GetTick(); printf("ssd1306_UpdateScreen, time cost: %d ms.\n", end - start); usleep(2000*1000); while (1) { DrawChinese(); } } void OledMain(void) { if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(OledMain);
运行效果
最终OLED显示:您好鸿蒙
参考资料
【XR806开发板试用】系列之一 - Linux环境下Ubuntu完全开发流程
https://xr806.docs.aw-ol.com/
https://aijishu.com/a/1060000000256653本贴转自极术社区:https://aijishu.com/a/1060000000284333
作者:H2O2_H2O2 -
【XR806开发板试用】Linux环境下Ubuntu完全开发流程
前言
为了让极术社区开发者体验搭载安谋科技STAR-MC1处理器的面向IoT领域的全志XR806开发板,极术社区联合全志在线开发者社区共同推出XR806开发板免费试用活动。极术社区特准备了200块XR806开发板作为2022年社区新年活动,申请的人数有600多,手快有手慢无,有幸申请到一块XR806开发板。该开发板目前支持鸿蒙L0轻量级设备(OpenHarmony-v1.1.2-LTS),之前没有接触过,值得开发体验一番。
环境准备
系统:Ubuntu 20.04.3 LTS
Python: Python 3.8.10
编译链:gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2串口调试工具:
CuteCom - http://cutecom.sourceforge.net/或在安装好Wine环境下,可以使用在Windows上的串口调试工具,如经典的putty等
开发流程
官方参考:https://xr806.docs.aw-ol.com/...,本文将完全在Ubuntu环境下开发,基本流程和官方一致。一. 代码下载
mkdir xr806 cd xr806 repo init -u ssh://git@gitee.com/openharmony-sig/manifest.git -b OpenHarmony_1.0.1_release --no-repo-verify -m devboard_xr806.xml repo sync -c -j8 repo forall -c 'git lfs pull'
二. 环境配置
1.OpenHarmony相关工具
Ubuntu环境配置可参考OpenHarmony官方:获取源码及Ubuntu编译环境准备,因XR806主要采用gcc编译,环境配置重点关注下安装Python3和安装hb(暂可以不安装llvm)。注:在下载完成代码后,可以在项目根目录下,执行以下命令安装OpenHarmony编译系统工具 - hb(也可参考OpenHarmony官方):
pip3 install build/lite
2.GCC编译链安装
解压环境准备小节中下载的gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
默认解压到~/tools目录mkdir -p ~/tools tar -jxvf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 ~/tools
注:~表示你的/home/用户名目录,
项目编译链配置默认~/tools目录下,可以不用修改编译链路径,当然在也可以解压到其他目录三. 固件编译
请详见官方参考:https://xr806.docs.aw-ol.com/...首次编译工程,需要对原生库进行配置,否则无法编译通过,配置步骤如下:
cd device/xradio/xr806/xr_skylark cp project/demo/audio_demo/gcc/defconfig .config make menuconfig make build_clean make lib -j cd - hb set hb build -f
注:首次编译可能会遇到异常,请参考官方固件编译说明
四. 固件下载
固件下载请参考:https://xr806.docs.aw-ol.com/...,Ubuntu环境下,与Windows相同,
编译生成的固件在device/xradio/xr806/xr_skylark/out,名称为xr_system.img1.设备识别
将XR806开发板,插入PC的USB接口,用lsusb命令查看,会多出一个设备,设备标识:ID 10c4:ea60 Silicon Labs CP210x UART Bridge,如下图所示:
algo@algoideas:~/openharmony/xr806$ lsusb Bus 002 Device 002: ID 8087:8000 Intel Corp. Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 002: ID 8087:8008 Intel Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 006: ID 138a:0017 Validity Sensors, Inc. VFS 5011 fingerprint sensor Bus 003 Device 002: ID 1ea7:0064 SHARKOON Technologies GmbH 2.4G Mouse Bus 003 Device 008: ID 10c4:ea60 Silicon Labs CP210x UART Bridge Bus 003 Device 005: ID 5986:0268 Acer, Inc Integrated Camera Bus 003 Device 004: ID 8087:07dc Intel Corp. Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub algo@algoideas:~/openharmony/xr806$ ls /dev/ttyUSB* /dev/ttyUSB0
新增加的设备节点名称为:ttyUSB0
2.工具配置
Linux下烧录工具文件位于device/xradio/xr806/xr_skylark/tools下,工具名为phoenixMC,工具配置文件为settings.ini,如下图所示:
主要修改:strComDev iBaud 和 strImagePath[comm] strComDev = /dev/ttyUSB0 iBaud = 3000000 [log] strLogFile = ./log/log.txt [firmware] strImagePath = ../out/xr_system.img strEtfImagePath = xr_system_etf.img
3.固件烧录
执行以下命令便可以烧录:
cd device/xradio/xr806/xr_skylark/tools ./phoenixMC
烧录部分过程如下:
注:Linux下烧录,固件路径长度有限制,使用路径时,目前使用的是相对路径,或如用其他路径,请拷贝固件到其他路径长度较短的目录下
五. 串口调试
打开CuteCom工具,设置好波特率为115200,及相关参数,并点击Open,此时按下开发板的Reset键即可,看到XR806的串口日志输出,当然也可以输入命令进行交互。
串口设置及XR806日志输出如图所示:
问题与总结
作为一名开发者,XR806在Linux环境下Ubuntu开发,整个开发的流程很顺利,本文未涉及到应用程序,仅涉及到固件相关开发,应用开发烧录一致,应用开发将在下一篇文章介绍,或参考官方相关文章。
Linux环境下开发,主要遇到以下问题:
Linux下烧录波特率非实际配置的3000000,对比Windows环境下,固件烧录慢很多,烧录提示如下:Baud should be one of the values below: 110 300 600 1200 2400 4800 9600 19200 38400 57600 115200 230400 460800 921600
参考资料:
https://xr806.docs.aw-ol.com/
https://aijishu.com/a/1060000000256653本贴转自极术社区:https://aijishu.com/a/1060000000282581
作者:H2O2_H2O2 -
LicheeRV 入门开发一帖通
目录
- 板卡系列介绍
- 开箱教程
- 上手点灯
- 外设功能验证
- Debian镜像体验
- BSP SDK 开发指南
- WAFT 开发指南
1. 板卡系列介绍
LicheeRV系列是Lichee系列下的RV子系列,主要为RISC-V内核的Linux SBC产品。
目前有以下几款产品:Nezha CM, HDMI Dock, 86-Panel
购买地址为:https://item.taobao.com/item.htm?id=663345415205
交流QQ群为:488268051板卡相关开发资料已上传到百度云:
链接:https://pan.baidu.com/s/1QJTaDw6kkTM4c_GAlmG0hg
提取码:wbef2. 开箱教程
LicheeRV产品默认使用TF卡启动,不管你购买的哪款产品,请先备好tf卡与读卡器。烧录软件
全志镜像的卡烧录工具下载地址:https://dl.sipeed.com/shareURL/LICHEE/D1/SDK/
下载 PhoenixCard.rar 解压运行其中的主程序即可系统镜像
默认系统镜像已上传到百度盘,会持续更新系统镜像分为 Tina与Debian两种,Tina为专用小linux镜像,Debian为桌面级镜像
板级配置
在上述百度盘的board目录下为板级配置文件,如果底包(系统镜像)的后缀与实际板卡不符,需要再使用此fex覆盖板级配置来正确显示。烧录步骤
打开烧录软件,按顺序点击下图按键进行烧录对应镜像如果烧录的镜像后缀与板子实际型号不符,下载对应的 boot_package_XXX.fex 来覆盖板级配置
覆盖指令为:
sudo dd if=boot_package_XXX.fex of=/dev/sdX bs=1K seek=16400
前面的镜像烧录,建议使用USB3.0的读卡器烧录,此时烧录100MB的Tina镜像约用时半分钟,烧录4GB的Debian镜像,约用时10分钟。
开机启动
将上面烧录好的镜像卡,插入核心板的tf卡槽里,接好系统串口(86 panel板载了USB转串口,可以直接插对应C口),上电启动,可以在串口以115200波特率看到系统启动信息,Tina启动时间约10s,Debian启动时间久些,约2~3分钟。BusyBox v1.27.2 () built-in shell (ash) __ ___ _ __ _ / |/ /__ _(_)_ __ / / (_)__ __ ____ __ / /|_/ / _ `/ /\ \ / / /__/ / _ \/ // /\ \ / /_/ /_/\_,_/_//_\_\ /____/_/_//_/\_,_//_\_\ ---------------------------------------------- Maix Linux (Neptune, 5C1C9C53) ----------------------------------------------
以上是Tina系统进入系统终端的logo打印,出现该字样说明系统启动成功
如果你需要使用ssh登录,则默认Tina的用户名密码为 root,tina
Debian的用户名密码为 sipeed,licheepi如果你只购买了LICheRV D1核心板,需要传输大文件的话,可以使用adb进行文件传输,adb push/pull 即可。
3.点灯教程
当我们成功进入系统后,就可以进行基础的点灯操作啦!
(注:该教程不适用于86-panel,因为对应引脚连接了外设,86panel用户可以拆下核心板来操作实验)核心板的螺丝固定焊盘旁有一颗LED,查看原理图:https://dl.sipeed.com/shareURL/LICHEE/D1/HDK/Lichee_RV/2_Schematic
可知该LED连接的是PC1,换算该IO的数字标号为:2*32+1=65,或者查看IO复用情况表:
cat /sys/kernel/debug/pinctrl/2000000.pinctrl/pinmux-pins ... pin 64 (PC0): device 2008000.ledc function ledc group PC0 pin 65 (PC1): UNCLAIMED pin 66 (PC2): UNCLAIMED pin 67 (PC3): UNCLAIMED pin 68 (PC4): UNCLAIMED pin 69 (PC5): UNCLAIMED pin 70 (PC6): UNCLAIMED pin 71 (PC7): UNCLAIMED
我们先导出该GPIO:
echo 65 > /sys/class/gpio/export cd /sys/class/gpio/export/gpio65
然后再将该IO置为输出状态,即可操作其电平:
echo out>direction echo 1 > value #LED点亮 echo 0 > value #LED熄灭
至此我们就成功在RISC-V 64 D1上点灯啦~
你也可以对 串行RGB LED WS2812 进行花式点灯:
cd /sys/class/leds/ echo 255 > sunxi_led0r/brightness;echo 0 > sunxi_led0g/brightness;echo 0 > sunxi_led0b/brightness; echo 0 > sunxi_led0r/brightness;echo 255 > sunxi_led0g/brightness;echo 0 > sunxi_led0b/brightness; echo 0 > sunxi_led0r/brightness;echo 0 > sunxi_led0g/brightness;echo 255 > sunxi_led0b/brightness;
4. 外设功能验证
4.1 音频功能
录音设备查看
root@MaixLinux:~# arecord -l **** List of CAPTURE Hardware Devices **** card 0: audiocodec [audiocodec], device 0: SUNXI-CODEC 2030000.codec-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0 card 1: snddmic [snddmic], device 0: 2031000.dmic-dmic-hifi dmic-hifi-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0 card 2: sndhdmi [sndhdmi], device 0: 2034000.daudio-audiohdmi-dai 20340a4.hdmiaudio-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0
播放设备查看
root@MaixLinux:~# aplay -l **** List of PLAYBACK Hardware Devices **** card 0: audiocodec [audiocodec], device 0: SUNXI-CODEC 2030000.codec-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0 card 2: sndhdmi [sndhdmi], device 0: 2034000.daudio-audiohdmi-dai 20340a4.hdmiaudio-0 [] Subdevices: 1/1 Subdevice 0: subdevice 0
录放音测试:
arecord -D hw:1,0 -f S16_LE -t wav -d 3 t.wav aplay -D hw:0,0 t.wav
另外可以使用alsamixer 进行音量调整
4.2 USB功能
默认内核支持外挂U盘的驱动,插上U盘后可以使用 fdisk -l 查看到新增的 /dev/sda
如果U盘没有被格式化,可以使用mkfs.vfat指令来格式化U盘,再使用mount指令挂载
默认Tina固件里的 /dev/mmcblk0p8 分区即可使用上述方式格式化后挂载,来提升可用空间4.3 有线网络
LicheeRV-86 Panel 支持百兆网络,使用套餐附送的网线尾线接上网线后,执行以下指令来连接有线网络ifconfig eth0 up udhcpc -ieth0
4.4 无线网络
LicheeRV 底板默认使用XR829或者RTL8723BS wifi模块,可以使用以下指令进行联网操作:先配置热点信息:
vim /etc/wifi/wpa_supplicant.conf network={ ssid="Sipeed_2.4G" psk="XXXX" } 配置完成后重启
,ifconfig wlan0 up; udhcpc -iwlan0 即可连上对应的wifi。
连上网络后,你就可以使用ssh远程登录板卡,或者使用scp来进行文件传输啦~4.5 屏显触摸
LicheeRV系列支持以下显示屏:SPI屏 1.14寸屏(TODO) RGB屏 4.3寸 480x272;5.0寸 800x480; RGB+SPI屏 4.0寸 480x480(st7701s); 4.0寸 720x720(nv3052c) MIPI屏 8.0寸 1280x720(ILI9881C)
Tina下可以通过以下指令测试屏幕显示:
fbviewer xxx.jpg
如果需要调试屏幕驱动,可以使用以下指令查看屏幕驱动信息:cat /sys/class/disp/disp/attr/sys screen 0: de_rate 300000000 hz, ref_fps:60 mgr0: 480x480 fmt[rgb] cs[0x204] range[full] eotf[0x4] bits[8bits] err[0] force_sync[0] unblank direct_show[false] iommu[1] dmabuf: cache[0] cache max[0] umap skip[0] overflow[0] lcd output backlight( 50) fps:59.5 esd level(0) freq(60) pos(0) reset(0) 480x 480 err:0 skip:184 irq:230715 vsync:0 vsync_skip:0 BUF enable ch[1] lyr[0] z[16] prem[N] a[globl 255] fmt[ 0] fb[ 480, 480; 480, 480; 480, 480] crop[ 0, 0, 480, 480] frame[ 0, 0, 480, 480] addr[ffe00000, 0, 0] flags[0x 0] trd[0,0]
屏幕彩条测试:echo 1 > /sys/class/disp/disp/attr/colorbar
如果你购买的是86面板套餐,可以使用 ts_test进行触摸测试
(注意触摸驱动有瑕疵,ts_test测试时松开后,光标会不动,但是终端仍会正常打印信息)4.6 视频播放
最终我们可以尝试在LicheeRV上播放BadApple啦~
Tina镜像中内置了ffmpeg软件包,ffmpeg是强大的多媒体库,可以用于录屏或者播放
录屏指令:ffmpeg -f fbdev -framerate 10 -i /dev/fb0 record.avi
播放指令(分别是扬声器播放音频和hdmi播放音频):ffmpeg -i /mnt/UDISK/badapple_640480_xvid.mp4 -pix_fmt bgra -f fbdev /dev/fb0 -f alsa hw:0,0 ffmpeg -i /mnt/UDISK/badapple_640480_xvid.mp4 -pix_fmt bgra -f fbdev /dev/fb0 -f alsa hw:2,0
这里由于是CPU软解,所以测试最高分辨率约为720x540, 再高会变卡
4.7 麦克风阵列
如果你使用的是dock板,那么还可以外接麦克风阵列版进行声场成像演示:
直接执行debian系统下内置的micarr_0609指令即可
有麦克风阵列相关的二次开发需求,可以联系support@sipeed.com5. Debian镜像体验
对于只接触过桌面级系统的开发者,推荐使用Debian镜像,可在上面的网盘里下载
LicheeRV_Debian_86_480p 为 480p的86盒板卡的debian镜像
LicheeRV_Debian_hdmi 为 dock的hdmi输出的debian镜像
如果是其他板卡或者屏幕,请自行使用对应的fex覆盖板级配置。
烧录完成后,插卡启动,稍等2分钟左右,屏幕上就会显示登录界面输入用户名 sipeed,密码 licheepi,即可进入桌面 (使用USB HOST口外接键鼠输入)
进入桌面后可以进行一些基础操作
接下来让我们尝试在Debian下跑一下Hello World:
另有720P高清屏的效果对比,有米的同学可以考虑入手:
6.BSP SDK 开发指南
为了方便用户自行开发,矽速整理发布了 LicheeRV 的bsp开发docker镜像,大家使用该镜像可以快速开始D1的系统级开发。
在网盘中下载对应的docker文件后,解压到tar文件,
docker import licheerv_d1_compile.tar licheerv_d1_compile:lastest
然后即可run该容器,用户名为nihao,密码为sipeed123
进入容器后的基础编译操作为:cd ~/sdk/tina-d1-open_new/ source build/envsetup.sh lunch //选1 make menuconfig //去掉里面的 alsa-plugin选项,否则编译不过 make -j96 #按实际核数编译 pack # 打包
SDK内置了一些版型的dts,你可以自行选择编辑:
device/config/chips/d1/configs/nezha/board_xxx.dts其他SDK的开发说明,可以参见全志开发平台上下载的相关文档
https://open.allwinnertech.com/
也可以加全志交流QQ群:498263967如果需要自己下载SDK开发,参考全志在线相关网页:https://d1.docs.aw-ol.com/en/
7.WAFT 开发指南
TODO转载自Sipeed社区:https://bbs.sipeed.com/thread/1300
-
回复: 【单板仅需99】D1哪吒计算条上线!为智能家居提供高性价比的RISC-V算力
淘宝已上线,Sipeed Lichee RV 86 Panel 智能家居 中控开发板 支持Linux WAFT
https://item.taobao.com/item.htm?id=663345415205
你的86 @jordonwu -
【FAQ】Wi-Fi/BT MAC地址定制
问题背景
很多Wi-Fi/BT模组默认出厂是不带MAC地址的,整机厂需要根据需求,烧写特定的MAC地址。MAC地址通路
Linux-4.9后,全志平台模组MAC地址定制流程如下系统启动后,引导程序会加载env中定义的key,并传递给cmdline和内核dts。 如果安全存储中没有mac/wifi_mac/bt_mac这几个key,或者值解析失败,我们将尝试从私有分区加载并解析这些key。在内核空间中,addr_mgt驱动程序读取cmdline或dts中与Mac相关的键,对其进行解析并导出到其他驱动程序以使用。 为了让用户空间可以访问这些地址值,创建了sysfs来保存地址值。
配置
uboot env
请确保env.cfg中有如下配置项存在:
dts
dts配置参考如下
其中,type_addr_xx表示mac地址的来源,值含义如下- 0: 不指定类型
- 1: 使用烧写的mac地址
- 2: 使用chipid生成的mac地址
- 3: 使用sysfs写入的地址
- 其他: 不提供地址
烧写
使用全志烧号工具DragonSN或DragonKey烧写mac/wifi_mac/bt_mac到私有分区或secure storge中。合法的mac地址格式为xx:xx:xx:xx:xx:xx, x 是16禁止值,0-9,a-f。使用
内核空间
Linux-4.9
- 读取Wi-Fi MAC地址
int get_wifi_custom_mac_address(char *addr_str)
- 读取BT MAC地址
int get_bt_custom_mac_address(char *addr_str)
- 读取以太网 MAC地址
int get_eth_custom_mac_address(char *addr_str)
Linux-5.4
int get_custom_mac_address(int fmt, char *name, char *addr)
fmt: 0为str,1为16进制值
name: “wifi”、“bt”、“eth”用户空间
可以通过sysfs文件节点访问对应值,linux-4.9下主要节点如下:root@venus-a1:/sys/class/addr_mgt# ls -l total 0 -rw-r--r-- 1 root root 4096 2019-01-15 17:22 addr_bt -rw-r--r-- 1 root root 4096 2019-01-15 17:22 addr_eth -r--r--r-- 1 root root 4096 2019-01-15 17:22 addr_type -rw-r--r-- 1 root root 4096 2019-01-15 17:22 addr_wifi
linux-5.4下主要节点如下:
console:/ # ls -l /sys/class/addr_mgt/ total 0 -rw-r--r-- 1 bluetooth net_bt_admin 4096 2020-12-22 19:33 addr_bt -rw-r--r-- 1 root root 4096 2020-12-23 13:10 addr_eth -rw-r--r-- 1 root root 4096 2020-12-23 13:10 addr_wifi -r--r--r-- 1 root root 4096 2020-12-23 13:10 summary
-
【FAQ】全志D1芯片 XR829扫卡失败问题排查
【问题背景】
硬件:D1 + Wi-Fi模组(XR829)
软件:melis-v3.0
说明:该FAQ旨在记录【问题简述】
WiFi初始化指令执行后无法扫描到WiFi模组,即扫卡失败【问题分析】
1、首先排查硬件问题,其次再找软件bug
(1)同样的模组在跑其它软件时,可以正常工作;排除模组的硬件问题
(2)定位软件bug,扫卡失败发生WiFi初始化阶段12、解软件bug
WiFi模组引脚图:
(1)WiFi初始化时会对模组进行上电
可能时上电时序不对导致扫卡失败,检查REG_ON引脚时序
REG_ON:执行WiFi初始化指令后,引脚时序应该是10ms高电平-》20ms低电平-》高电平
(2)上电时序没有问题的话,检查sdio驱动问题,因为扫卡是通过是sdio下发cmd5
使用其它sdio设备,看其是否可以正常工作;使用sd卡测试,可以正常工作,说明不是sdio的驱动问题检查指令引脚是否发出cmd5——SDIO CMD:在扫卡阶段检查该引脚是否有cmd5命令输入
cmd5命令格式如下:
检查扫卡频率是否正常——SDIO CLK:在扫卡阶段,该引脚应该是输入4KHZ、50%占空比的方波。
3、在检查这两个大方向后还是无法排查问题
(1)和正常模组对比差异点
最后发现LPO引脚输入24MHZ时钟,而正常模组为高电平;将该引脚修改为高电平,可以正常扫到卡。【解决方法】
LPO引脚正常情况下是输入32.768KHZ的震荡信号作为低功耗时使用,故输入32.768KHZ时钟;测试可以正常扫到卡。
执行wifi初始化指令后,设置LPO输入32KHZ、50%占空比的方波 -
【FAQ】全志D1芯片 如何移植 rtl8821cu wifi 驱动到 Linux-5.4内核?
问题背景
移植 Linux-4.9 或之前的内核版本下的 wifi 驱动到 Linux-5.4 内核版本时会出现编译和运行错误,
该 FAQ 主要用于帮助开发人员解决驱动移植出现的问题。问题分析
移植到 Linux-5.4 内核版本时出现如下编译错误:WARNING: module 8821cu uses symbol kernel_read from namespace VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver, but does not import it.
出现该 WANRING 的原因是内核版本升级后文件系统存在差异,驱动调用了 kernel_read()和 kernel_write() 函数,需要进行 import VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver 处理。
移植到 Linux-5.4 内核版本时运行的错误:
出现该问题的原因是 Linux-5.3 及以后版本 cfg80211.h 里结构体 wiphy_vendor_command 新增了变量 policy 和 maxattr。解决方案
出现编译错误时解决方案如下:
diff --git a/drivers/net/wireless/rtl8821cu/os_dep/linux/os_intfs.c b/drivers/net/wireless/rtl8821cu/os_dep/linux/os_intfs.c index c4b515763ac8..466a3b27f521 100644 --- a/drivers/net/wireless/rtl8821cu/os_dep/linux/os_intfs.c +++ b/drivers/net/wireless/rtl8821cu/os_dep/linux/os_intfs.c @@ -28,6 +28,8 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek Wireless Lan Driver"); MODULE_AUTHOR("Realtek Semiconductor Corp."); MODULE_VERSION(DRIVERVERSION); +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
出现运行错误时解决方案为遍历驱动里所有使用 wiphy_vendor_command 结构体并修改如下:
diff --git a/drivers/net/wireless/rtl8821cu/os_dep/linux/rtw_cfgvendor.c b/drivers/net/wireless/rtl8821cu/os_dep/linux/rtw_cfgvendor.c index 6bac27d37766..09e24dcc9480 100644 --- a/drivers/net/wireless/rtl8821cu/os_dep/linux/rtw_cfgvendor.c +++ b/drivers/net/wireless/rtl8821cu/os_dep/linux/rtw_cfgvendor.c @@ -1756,6 +1756,11 @@ static const struct wiphy_vendor_command rtw_vendor_cmds[] = { }, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rtw_cfgvendor_gscan_get_capabilities +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) + , + .policy = VENDOR_CMD_RAW_DATA, + .maxattr = 1 +#endif },
-
【FAQ】全志D1芯片 Tina 如何查看通过 procd init 脚本启动的应用输出到 stdout/stderr 的打印信息?
问题描述
当我们使用 procd init 脚本让某个应用程序实现开机自启时,会发现应用程序中原本通过 printf/fprintf 等输出到 stdout/stderr 的打印信息都无法从串口或 adb shell 中看到了。
这些打印默认是输出到什么地方?我们可以如何看到这些打印?
原因
一般情况下,当用户在终端中执行命令来运行某个应用程序时,stdin/stdout/stderr 就确定下来是在当前终端,因此应用程序的打印信息自然能从当前终端中显示出来。
而如果该应用程序是通过 procd init 脚本进行开机自启,它会被认为是一个守护进程(daemon)。守护进程是随系统自启的,它们有可能在用户登录终端之前就已经开始运行了,也无法得知用户是从哪个终端登录,因此也就无法将打印信息输出到用户所在的终端。
解决方法
一般来说,要获取守护进程的打印,需要通过 syslog 之类记录系统整体日志的方法。procd init 脚本也提供了方法将应用程序的打印重定向到 syslog 中。
下面是一个简单的 procd init 脚本例子,它会启动应用程序 /usr/bin/foobar,但我们默认没法看到 foobar 输出到 stdout/stderr 的打印:
#!/bin/sh /etc/rc.common START=50 USE_PROCD=1 start_service() { procd_open_instance procd_set_param command /usr/bin/foobar procd_close_instance }
通过增加“procd_set_param stdout 1”和“procd_set_param stderr 1”两个参数,可将其输出到 stdout/stderr 的内容重定向到 syslog:
#!/bin/sh /etc/rc.common START=50 USE_PROCD=1 start_service() { procd_open_instance procd_set_param command /usr/bin/foobar procd_set_param stdout 1 # 将其 stdout 的内容重定向到 syslog procd_set_param stderr 1 # 将其 stderr 的内容重定向到 syslog procd_close_instance }
如此设置后,就可以从 syslog 中看到 foobar 应用程序输出的打印。
-
【FAQ】全志D1芯片 如何解决Gstreamer:fb UI旋转(直接修改内核参数)后,sunxifbsink显示异常问题?
1、问题背景
客户在使用D1做项目开发时,通过直接修改内核参数的方法来旋转fb以达到旋转UI的目的(如下图所示),但此修改会导致sunxifbsink中获取到的视频层信息也随之改变,影响显示效果。2、解决方法
需要重置视频层的分辨率为屏幕物理分辨率,改动如下图所示: -
【FAQ】全志D1芯片 如何对D1主频进行调节?
【问题背景】
在D1项目开发的过程中,有时候需要调节CPU主频,以对一些场景、功耗或性能进行测试,故对主频的调节方法进行介绍,方便大家后面调主频【适用场景】
硬件:D1芯片平台 软件:Tina系统【基本操作】
1.在烧了Tina固件的D1开发板上可以看到调频相关节点:
2.在系统启动log中,可以看到默认频率,一般是1G
3.查看内核文件发现SDK提供了RV的调频代码,看来可以将调频模块加载起来
4.打开内核相关配置:make ARCH=riscv menuconfig,选择:CPU Power Management ---> CPU Frequency scaling ---> ARCH RISCV Allwinner nvmem based SUN50I CPUFreq driver
如图:
5.查找调频相关的dts中频率表的配置,只有1008M,可以进行超频修改,比如改到1.3G
6.重新编译打包烧写,可以跑到1.3G
-
【FAQ】全志D1芯片 如何解决Gstreamer播放1080视频显示异常问题(重影)?
1、问题描述
Gstreamer升级后测试发现,在播放1080P视频时,画面会出现重影现象。2、问题分析
通过抓取图像数据以及对log分析,怀疑与VE的数据对齐有关,经验证确实如此。在解码的时候,VE会做16位对齐,所以1080P解码出来的数据除了需要将frame buffer的宽高设置给显示外,还需要做crop处理。而造成重影现象的原因就是在做crop处理前未将frame buffer的真实宽高设置给显示。3、解决方法
在omx层第二次设置输出状态时,图像宽高为对齐后的宽高,sunxifbsink做crop处理;见附件。将附件中的0008 patch放在tina/package/multimedia/gst1-omx/patches目录下,然后用附件中的gst-plugins-aw-1.8.2.tar.x替换dl目录下的同名文件。
gst-plugins-aw-1.8.2.tar.xz
0008-Solve-the-ghosting-problem-of-sunxifbsink-display-du.patch -
【FAQ】全志D1芯片 如何在 Linux Device Tree 中配置预留内存?
前言
有时我们需要在 Linux 内核中预留一部分内存空间用作特殊用途(给安全模块使用,给其它处理器使用,或是给特定的驱动程序使用等),在 Device Tree 中有提供两种方法对预留内存进行配置:memreserve 和 reserved-memory。
memreserve
memreserve 的使用方法比较简单,如下所示,会将从地址 0x40000000 开始共 1MB 的内存空间预留出来:
/memreserve/ 0x40000000 0x00100000;
使用 memreserve 预留出来的内存一般无法再被 Linux 系统使用(当然,也可以通过特殊方法让代码固定访问该地址,但这种并非标准用法,在此不展开描述)。
reserved-memory
reserved-memory 框架提供了更多样的使用方法,并且与内核的 DMA API 和 CMA 框架紧密联系。
推荐先阅读一下内核自带的文档 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt,里面有其详细的语法说明和注意事项(例如 reserved-memory 节点中的 #address-cells 和 #size-cells 的值需要与根节点的保持一致)。
下面对几种常见的使用方法进行举例说明:
通过 memremap/ioremap 来使用
在 Device Tree 配置如下,然后通过“memory-region”参数可将该预留内存分配给特定的设备驱动使用:reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; foobar_reserved: foobar@70000000 { no-map; reg = <0x0 0x70000000 0x0 0x10000000>; }; }; foobar_driver: foobar_driver@0 { memory-region = <&foobar_reserved>; };
在设备驱动程序中,可解析 Device Tree 节点获得预留内存的物理地址和大小,然后通过 memremap/ioremap 映射这片内存空间来使用:
/* Get reserved memory region from Device-tree */ np = of_parse_phandle(dev->of_node, "memory-region", 0); if (!np) { dev_err(dev, "No %s specified\n", "memory-region"); goto error1; } rc = of_address_to_resource(np, 0, &r); if (rc) { dev_err(dev, "No memory address assigned to the region\n"); goto error1; } lp->paddr = r.start; lp->vaddr = memremap(r.start, resource_size(&r), MEMREMAP_WB); dev_info(dev, "Allocated reserved memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);
通过 DMA API 来使用
设置“shared-dma-pool”属性后,可让设备驱动通过 DMA API 来使用预留内存:
reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; foobar_reserved: foobar@70000000 { compatible = "shared-dma-pool"; no-map; reg = <0x0 0x70000000 0x0 0x10000000>; }; }; foobar_driver: foobar_driver@0 { memory-region = <&foobar_reserved>; };
设备驱动程序中可类似常规地使用 DMA API,它申请的内存不是来源于默认的 CMA 内存池,而是来源于该预留内存:
/* Initialize reserved memory resources */ rc = of_reserved_mem_device_init(dev); if(rc) { dev_err(dev, "Could not get reserved memory\n"); goto error1; } /* Allocate memory */ dma_set_coherent_mask(dev, 0xFFFFFFFF); lp->vaddr = dma_alloc_coherent(dev, ALLOC_SIZE, &lp->paddr, GFP_KERNEL); dev_info(dev, "Allocated coherent memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);
给 CMA 预留内存
有时我们不需要将预留内存分配给特定的设备驱动,而只是想给默认 CMA 内存池分配一片固定的内存区域,这时我们可配置上“reusable”和“linux,cma-default”:
reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; linux,cma { compatible = "shared-dma-pool"; reusable; reg = <0x0 0x70000000 0x0 0x10000000>; linux,cma-default; }; };
由此可见,不同于 memreserve,通过 reserved-memory 预留的内存有可能进入系统 CMA,这需要满足以下几个条件:
- compatible 需要为“shared-dma-pool”
- 没有定义“no-map”属性
- 定义了“reusable”属性
-
【FAQ】全志D1芯片 如何解决Audiocodec使用S24_LE格式进行录音,软件分析波形异常的问题?
问题背景
硬件:R329
软件:Tina
内核:Linux-4.9问题描述
使用Audiocodec进行录音,格式S24_LE,录制的.wav波形在某些软件中异常
arecord -D hw:audiocodec -f S24_LE -r 16000 -c 2 -d10 /tmp/test3_S24_LE.wav
需要放大很多倍才能看到声音波形问题分析
1.R329的Audiocodec用于录音的ADC只支持16bit和20bit的采样精度。采样后的数字信号会存放到RX_FIFO中,RX_FIFO的大小为256*20-bit,其他平台可以在User Manual确认支持的采样精度,从而判断是否会有这个问题产生
2.RX_DATA是一个32位的寄存器,保存的是从RX_FIFO获取的一个channel的样本数据,当使用arecord进行录音时,RX_DATA中的值会经DMA搬至内存,最后保存到.wav中
其中RX_DATA有四种模式去获取RX_FIFO的数据,S24_LE和S32_LE均采用20-bit mode0
当设置了20bit采样精度时,对应的两种模式如下图所示:
3.先说明一下S24_LE和S32_LE这两种格式的区别
S24_LE指有符号整型,范围是-2^23 ~ ((2^23) - 1),有效数据在低24位
S32_LE指有符号整型,范围是-2^31 ~ ((2^31) - 1),有效数据在高24位LSB MSB 1st byte 2nd byte 3rd byte 4th byte alignment S32_LE: 00000000 xxxxxxxx xxxxxxxx xxxxxxxx 32 bits S24_LE: xxxxxxxx xxxxxxxx xxxxxxxx 00000000 32 bits S24_3LE: xxxxxxxx xxxxxxxx xxxxxxxx 24 bits
4.在驱动程序中,S24_LE和S32_LE虽然都支持,但他们两者都是使用20-bit的mode0,这导致这两种格式保存到文件中的数据排布是一致的,但生成的wav头信息中的采样位数则不一样,从下图可以看出两者的差异
S32_LE的wav文件信息:
若软件以S32_LE进行解析,以上红框的数据变为0x0f80f0,依然可以保留全部有效数据
S24_LE的wav文件信息:
若软件以S24_LE进行解析,以上红框的数据变为0x55f000,便会丢失一部分数据
解决方案
总结原因就是audiocodec的采样精度只支持16和20bit,因此PCM格式中S24_LE虽然也支持,但硬件的特性使驱动并不能做到很好的适配,若软件以标准S24_LE格式进行分析,则会丢失高位的有效数据,这取决于软件如何对数据进行分析,解决方法有以下三种
- 使用audiocodec时,使用-f S32_LE,修改wav头信息中的采样位数位32,这对大部分软件都有效
arecord -D hw:-f S32_LE -r 16000 -c 2 -d10 /tmp/test32.wav
- 如果必须使用S24_LE格式进行录音,可以选择其他支持24bit采样的音频接口,如I2S等
- 假如必须使用audiocodec声卡,S24_LE格式进行录音,可以自行调整RX_DATA寄存器的模式,结合RX_DATA寄存器中实际的有效数据分布,自己开发软件进行数据分析
如果有分析和处理音频数据的需求,可以参考以上思路,结合RX_DATA寄存器去调整
-
【FAQ】全志D1芯片 mp4(Xvid)视频文件播放花屏问题
1、问题背景
客户使用F133进行一体广告机项目开发过程中,测试到附件中的片源《少女时代OhMVFullHD1080(播放花屏).mp4》播放时会出现花屏现象。但在之前的C800相同项目中该片源测试正常。2、问题描述
出错第一帧标号为17(标号从0开始),如下图所示:抓取该帧花屏图像,现象如下:
花掉的图像帧数据如下:
3、问题分析
(1)关掉cache,花屏现象仍旧,排除漏刷cache影响;
(2)64位系统(tina/melis)播放均花屏,32位系统正常;
(3)寄存器对比,未发现异常;
(4)在FbmRequestBuffer中将请求到的buffer清零,图像仍会花掉,但是花屏现象如下:
推测在视频播放的过程中,该部分未有数据写出,仍然保留该buffer中上一帧的数据。(未修改代码前出错第一帧下半部分之所以是黑色的,是因为该视频前面的12帧均为黑色图像帧,所以该buffer中残留有上一帧的数据)
最后,通过添加打印发现,正常情况(R528平台),在未解码完一帧时,会通过检查同步标记函数而进入下一个packet的解码 ,但是现在异常(F133/D1)情况下跑到了else里面,导致一帧图像没有解码完,就解下一帧了。 正好前面抓图的现象也是第一帧出错的图像,下半部分是前面图像的数据残余。
经分析,该问题的根本原因是在64位编译器中,i>>32 都等于i;而在32位编译器中,i>>32 都等于0。所以此发现也正好解释了之前的测试结果 “64位系统(tina/melis)播放均花屏,32位系统正常”。
4、解决办法
对出现右移32位的情况做判断,即return (rbit-n)<0?0:((rbit-n)>=32?0:(ld->bit_a & (0xFFFFFFFF >> (ld->bitcnt))) >> (rbit-n));对应的库文件见附件。
library.7z -
【FAQ】全志XR806芯片FAQ汇总(你不知道的和你想知道的的这里都有)
01、【FAQ】全志XR806芯片 系统异常重启如何解决?
02、【FAQ】全志XR806芯片 如何更换打印log口?
03、【FAQ】全志XR806芯片 为什么开机时候串口以及部分gpio会有电平跳变?
04、【FAQ】全志XR806芯片 mac地址要如何存放以及如何获取?
05、【FAQ】全志XR806芯片 固件烧录完成后,程序不是预期烧录程序或者甚至无法启动如何解决?
06、【FAQ】全志XR806芯片 如何使用timer命令行命令?
07、【FAQ】全志XR806芯片 如何修改中断优先级?
08、【FAQ】全志XR806芯片 如何设置AP模式默认IP地址、子网掩码、网关?
09、【FAQ】全志XR806芯片 低功耗蓝牙BLE断开连接错误码和分析?
10、【FAQ】全志XR806芯片 select引发崩溃如何解决?
11、【FAQ】全志XR806芯片 如何打开 LwIP 调试信息?
12、【FAQ】全志XR806芯片 执行扫描动作时,偶尔会扫描不到目标的AP如何解决?
13、【FAQ】全志XR806芯片 如何解决编译错误undefined reference to?
14、【FAQ】全志XR806芯片 standby模式下gpio的电平状态是如何变化?
15、【FAQ】全志XR806芯片 如何清除扫描列表缓存?
16、【FAQ】全志XR806芯片 RTC时钟不能跑、有时候时间不准是什么原因?
17、【FAQ】全志XR806芯片 如何使用watchpoint功能?
18、【FAQ】全志XR806芯片getsockopt、setsockopt失败如何解决?
19、【FAQ】全志XR806芯片 串口修改波特率后与目标波特率不匹配如何解决?
20、【FAQ】全志XR806芯片 Xradio Skylark 中的无线网络回调事件含义
21、【FAQ】全志XR806芯片 如何使用phoenixMC软件把应用固件与etf固件合并在一起?
22、【FAQ】全志XR806芯片 RF参数是如何保存的?
23、【FAQ】全志XR806芯片 汇编代码调试技巧
24、【FAQ】全志XR806芯片 如何解决第三方静态库函数符号重复?
25、【FAQ】全志XR806芯片 如何创建自定义状态回调函数?
26、【FAQ】全志XR806芯片 如何获取ap的rssi值?
27、【FAQ】全志XR806芯片 XR806如何添加本地音频到flash?
28、【FAQ】全志XR806芯片 XR MCU的外设驱动为什么修改无效?
29、【FAQ】全志XR系列 XRMCU如何播放xip中的音频?
30、【FAQ】全志XR系列 XRMCU如何修改录音编码器的输入数据?
31、【FAQ】全志XR系列 如何调试wifi频偏问题?
32、【FAQ】全志XR系列 如何统计XRMCU的内存使用情况
33、【FAQ】全志XR系列 设置音频结构体HttpStreamBufferConfig成员有什么意义?
34、【FAQ】全志全系列芯片 APST平台无法下载或者更新工具
35、【FAQ】全志XR806 Freertos+XRMCU+ADC采样频率偏低
36、【FAQ】全志XR806 Freertos + XRMCU +ADC采样数值不准
37、【FAQ】全志系列芯片如何把flash擦成空片?
38、持续更新.... -
【FAQ】全志XR806芯片 XR MCU的外设驱动为什么修改无效?
1.问题背景
为了节省更多的flash空间,在xr872/xr808/xr806芯片上把大部分的驱动都已经做了rom化处理,即出厂的时候已经把外设驱动都集成到rom当中,因此默认sdk中驱动的代码是不能修改的。而客户有时候调试需要在驱动层添加一些打印信息或者修改外设驱动的配置,此时则需要舍弃rom化的驱动,自己在源代码上添加相应的驱动了。2.问题描述
XR MCU的外设驱动为什么修改无效。3.解决办法
- 先舍弃rom化驱动的代码:在lib\xradio_v2目录下的rom_symbol.ld文件,删除所要修改的模块的驱动相关的函数。注,xr806的rom_symbol.ld文件在lib\xradio_v3文件夹。
- 在src\driver\chip这个目录相应的外设模组上的源代码文件填上所需的函数,一般情况下把src\rom\rom_bin\src\driver\chip相关模组的函数移植过去即可,因为该目录下的驱动只提供客户查看,并没有编译到sdk镜像里面,真正编译到镜像的是src\driver\chip里面的函数。
-
【FAQ】全志XR806芯片 XR806如何添加本地音频到flash?
问题背景
XR806的文件管理系统是littlefs或spifs,不像fatfs可以直接进行文件传输,有客户放映不清楚如何通过文件管理系统调用音频文件。问题描述
XR806SDK中的audio_demo找不到本地mp3等音频文件。问题分析
- 因为XR806的文件管理系统是littlefs或者spifs,所以需要在PC本地把文件打包成littlefs文件系统格式,否则无法识别。打包工具是SDK下的tools/fs_img_tools/mklittlefs。
- 上一步打包好的文件,如果通过“section”的方式打包进img镜像会有64byte的偏移,所以需要用“raw_bin”的方式进行打包。
解决方法
打包音频文件
新建一个文件夹,如data(名称随意),并把目标音频文件存放仅该目录下,值得注意的是因为audio_demo中默认播放的的是music文件夹下的音频文件,所以音频文件也必须放在music文件夹下。. ├── data │ └── music │ └── 1.mp3 └── mklittlefs
打包该文件夹使用如下命令
./mklittlefs -c data/ -d 0 -b 4096 -p 256 -s 524288 lfs.bin
-c后接目标路径。
-d后接debug等级,默认为0,不用修改。
-b后接block的大小,littlefs默认为4096,一般情况下不用修改。
-p后接page大小,默认为256,不用修改。
-s后接littlefs镜像大小,和在make menuconfig中的配置必须一致。
lfs.bin是生成的镜像文件名。名称随意,但一般是.bin后缀。make menuconfig配置
进入图形化界面配置,并选中filesystem support后选项配置如下。推荐勾选上flash filesystem image pack support,编译代码后会自动把lfs.bin打包到镜像,否则只能在phoenixMC的调试界面中擦除flash地址1572864(0x18000)后的内容,并手动把lfs.bin写进flash。其中步骤1所说的镜像大小524288就是由2048*1024-1572864而来。--- filesystem support [*] flash filesystem image pack support FileSystem Type Select (LittleFS) ---> (1572864) little filesystem start address (4096) little filesystem block size (128) little filesystem block count
修改工程cfg文件配置
把前面打包好的lfs.bin复制到project/demo/audio_demo/image/xr806目录下,并修改目录下的image.cfg。{ "magic" : "AWIH", "version" : "0.5", "image" : {"max_size": "1532K"}, "section" :[ {"id": "0xa5ff5a00", "bin" :"boot_40M.bin", "cert": "null", "flash_offs": "0K", "sram_offs": "0x00230000", "ep": "0x00230101", "attr":"0x1"}, {"id": "0xa5fe5a01", "bin" :"app.bin", "cert": "null", "flash_offs": "71K", "sram_offs": "0x00201000", "ep": "0x00201101", "attr":"0x1"}, {"id": "0xa5fd5a02", "bin" :"app_xip.bin", "cert": "null", "flash_offs": "104K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x2"}, {"id": "0xa5fa5a05", "bin" :"wlan_bl.bin", "cert": "null", "flash_offs": "1075K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, {"id": "0xa5f95a06", "bin" :"wlan_fw.bin", "cert": "null", "flash_offs": "1078K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, {"id": "0xa5f85a07", "bin" :"sys_sdd_40M.bin", "cert": "null", "flash_offs": "1103K", "sram_offs": "0xffffffff", "ep": "0xffffffff", "attr":"0x1"}, {} ], "raw_bin" :[ {"bin" :"lfs.bin", "flash_offs": "1536K"}, {} ] }
编译完成后编译烧录即可。
-
【FAQ】全志XR806芯片 如何获取ap的rssi值?
1.问题背景
在网络设备上,很多开发者要把当前的网络质量反馈到用户上,以便用户了解当前网络环境,当网速慢或联网失败时做出及时的网络环境调整。2.问题描述
如何获取目标ap的信号强度。3.解决办法
(1)当连接到ap时获取当前ap的信号强度值,通过以下方式来获取rssi和dbm的值:ret = wlan_ext_request(g_wlan_netif, WLAN_EXT_CMD_GET_SIGNAL, (int)(&signal)); printf("signal = %d! noise = %d!\r\n", (signal.noise + (signal.rssi/2)), signal.noise);
(2)获取附近ap的rssi值和dbm值,可以通过扫描结果来获取,其中获取ap信息结构体wlan_sta_ap里面的rssi成员代表rssi值,level代表dbm值。
wlan_ssid_t ssid; uint8_t bssid[6]; uint8_t channel; uint16_t beacon_int; int freq; int rssi; /* unit is 0.5db */ int level; /* signal level, unit is dbm */ int wpa_flags; int wpa_cipher; int wpa_key_mgmt; int wpa2_cipher; int wpa2_key_mgmt; } wlan_sta_ap_t;
注意:获取的信号强度信息时需要进行一次扫描动作才能刷新缓存结果,所以获取信息之前需要调用wlan_ap_scan_once()函数扫描,并且等待扫描成功后,信号强度信息才会刷新。
-
【FAQ】全志XR806芯片 如何创建自定义状态回调函数?
问题背景
XR_MCU的SDK中,audio,wlan,Fs等模块被引用时,已经根据状态,设置好了回调函数,但是有客户不清楚如何利用SDK自身资源,创建自己应用模块的状态回调函数。问题描述
如何创建自定义状态回调函数。问题分析
状态回调函数依赖于SDK的framework框架,且已经为用户预留了自定义回调函数的框架,audio,bt,fs,net的状态回调框架都是完全公开的,参照编写即可。解决方法
1 添加自定义状态回调类型
在project\common\framework\sys_ctrl\sys_ctrl.h文件中,修改ctrl_msg_type结构体,在CTRL_MSG_TYPE_USER后添加自定义类型,如CTRL_MSG_TYPE_TEST。typedef enum ctrl_msg_type{ CTRL_MSG_TYPE_SYSTEM = 0, CTRL_MSG_TYPE_NETWORK, CTRL_MSG_TYPE_VKEY, CTRL_MSG_TYPE_VOLUME, CTRL_MSG_TYPE_SDCARD, CTRL_MSG_TYPE_FS, CTRL_MSG_TYPE_AUDIO, CTRL_MSG_TYPE_HANDLER, /* message defined by user starts from CTRL_MSG_TYPE_USER */ CTRL_MSG_TYPE_USER = 0x100, CTRL_MSG_TYPE_TEST, //自主添加的回调函数 } ctrl_msg_type;
2 创建回调函数
在.h文件中添加状态类型enum test_status { TEST_MSG_STATE_FIRSR, //状态1 TEST_MSG_STATE_SECOND, //状态2 TEST_MSG_STATE_UNDEFINE, };
在.c文件中添加实体函数
/* 处理状态变化 */ static void test_ctrl_msg_process(uint32_t event, uint32_t data, void *arg) { switch (EVENT_SUBTYPE(event)) { case TEST_MSG_STATE_FIRSR: test_act_first(data); break; case TEST_MSG_STATE_SECOND: test_act_second(data); break; default: break; } }
/* 创建回调函数 */ int test_init(void) { observer_base *base = sys_callback_observer_create(CTRL_MSG_TYPE_TEST, //监控event类型 0xFFFF, //监控类型可分类,可参考fs_ctrl.h test_ctrl_msg_process, //回调函数 NULL); if (base == NULL) { printf("create fail\n"); return -1; } /* 挂载到观察链表 */ if (sys_ctrl_attach(base) != 0) { printf("attach fail\n"); return -1; } }
3 发送状态
当线程中状态发生变化时,通过project\common\framework\sys_ctrl\sys_ctrl.h中定义的API,会调用步骤2中设置好的回调函数。
常规用法/* 实体 */ int sys_event_send(uint16_t type, uint16_t subtype, uint32_t data, uint32_t wait_ms); /* 例,其中wait_ms为队列等待超时时间,队列深度默认为6 */ sys_event_send(CTRL_MSG_TYPE_TEST,TEST_MSG_STATE_FIRSR,0,0);
特殊用法,发送完成后自动释放数据
/* 实体 */ int sys_event_send_with_free(uint16_t type, uint16_t subtype, void *data, uint32_t wait_ms); /* 例,其中data是希望传输到回调函数的数据,使用该API能在传输完成后释放内存 */ struct STtest *testdata = malloc(sizeof(*testdata)); memset(testdata,0,sizeof(*testdata); testdata = dataget(testdata) //数据赋值 sys_event_send_with_free(CTRL_MSG_TYPE_TEST,TEST_MSG_STATE_FIRSR,testdata ,0);//发送完成后会自动释放数据
特殊用法,自定义销毁方式
/* 实体 */ int sys_event_send_with_destruct(uint16_t type, uint16_t subtype, void *data, void (*destruct)(event_msg *), uint32_t wait_ms)
该API和sys_event_send_with_free差异点为,sys_event_send_with_free执行完回调后,会固定执行free(data),但是sys_event_send_with_destruct在执行完回调后,继续执行destruct函数,destruct函数由用户自主编写,可以选择销毁data,也可以选择特殊处理。
-
【FAQ】全志XR806芯片 如何解决第三方静态库函数符号重复?
1.问题背景
联合开发中,由于软件保密,合作方仅提供.a静态库。但可能出现合作方提供的.a静态库和自身的.a静态库冲突,导致镜像合成失败,且可能因为合作方的各种缘故不方便修改静态库。2.问题描述
程序编译过程中没有出错,但是在ld链接过程中提示错误:multiple definition of。3.问题分析
1 对于在程序中可以修改的重复定义,直接修改程序即可。
2 multiple definition of的函数都在保密的.a库文件中,重复函数众多,且无法轻易更改程序。由于.a静态文件是由.o可执行文件打包而来,可以先对.a静态库解包,删除掉重复定义的.o可执行文件后重新打包即可。4.解决方法
1 拆包.a静态库。ar -x libtarget.a
2 删除多余的.o文件。
3 重新打包静态库。ar crv libtarget.a *.o
4 重新编译。
-
【FAQ】全志XR806芯片 汇编代码调试技巧
1.问题背景
问题平台:XR806 + RTOS2.问题描述
XR806(M33内核)适配新的RTOS时,沿用M4F的启动代码后出现了系统奔溃,但是expection显示的PC地址和LR地址都被修改,无法准确判断哪条语句导致的错误。3.问题分析
- 通过log定位到是启动代码出现了问题。
- arm汇编中,可利用b .进入死循环。
- 在汇编中可以利用以下代码打印log:
//.c文件中插入 void AsmPrint(void) { printf("var = %#x\n",PrintMagic); } #汇编代码中插入以下代码查看R0的值 LDR R8,=PrintMagic str R0,[R8] LDR R8,=AsmPrint bx R8
- 最后发现汇编代码正常,但在运行第一个任务时系统奔溃。原因为M33内核新增了PSPlimit功能,任务栈超过了设定值时会直接触发usage Fault。同时RTOS在系统启动前修改了PC地址和LR地址,导致exception中无法正确显示PC地址和LR地址。
4.解决方法
使用__set_PSPLIM可以设置PSPlimit地址,在不确定PSP限制时,可以__set_PSPLIM(0)取消这个功能,MSP也是相同道理。 -
【FAQ】全志XR806芯片 RF参数是如何保存的?
1.问题背景
客户在量产中,由于各个产品一致性不太一样,很多时候需要在出厂时对每个产品进行RF参数的校准才能确保RF性能达到最佳。那么校准后,相关的参数保存在什么地方?2.问题描述
XR系列MCU的RF参数是如何保存的?3.解决办法
目前出厂校准的RF参数主要包括有频偏和发射功率。XR系列MCU获取这两个参数的主要途径有两个:镜像打包时的sdd文件和efuse。而获取来源是遵循以下原则:
1、MCU启动后,会读取efsue上RF参数区域的数据,如果是合法数据,则采用efuse上的参数,而sdd文件的参数会无效。
2、MCU启动后,如果efsue上RF参数区域的数据不是合法数据,则采用sdd文件上的参数。如果在软件代码上写入频偏,代码流程上参考project\example\efpg这个示例写入efuse。 -
【FAQ】全志XR806芯片 如何使用phoenixMC软件把应用固件与etf固件合并在一起?
1.问题背景
在客户量产中,出厂的板子rf参数不太可能做到完全一致,所以需要etf固件来校准rf参数。但是如果烧录完etf固件又再一遍烧录应用固件生产起来就比较麻烦。把两个固件合并在一起的话可以减少生产工序,提高生产效率。2.问题描述
如何使用phoenixMC软件把应用固件与etf固件合并在一起?3.解决办法
3.1使用方法:
1、打开phoenixMC.exe,点击设置按钮。2、点击合并固件,并依次选择应用固件和etf固件。
3、选择完成后,固件会在etf固件的目录下生产新的combineImage.cimg文件,此文件即是两者合并的文件。
3.2注意事项:
- 如果应用固件支持ota升级,合并文件一定要先选择应用固件后再选择etf固件,因为etf固件不支持ota功能。
- 合并后固件flash layout为:app1->应用固件,app2->etf固件。
- 固件进行一次ota升级后,etf固件将会被升级后的应用固件覆盖,即app2的区域装载固件不是etf固件,而是新的etf固件。
-
【FAQ】全志XR806芯片 Xradio Skylark 中的无线网络回调事件含义
1. 问题背景
部分客户在使用 WLAN 时,不清楚各回调事件的含义,不利于上层应用逻辑开发。2. 问题描述
无线网络各个事件的代表的含义是什么?3. 问题分析
首先看一下无线网卡处于 Station 模式下连接 AP 的过程:- Station 发起扫描,扫描附近的 AP
- Station 选中指定的 AP,发起认证(Authentication)、关联(Association)
- (可选) 如果是 WPA/WPA2 加密方式,则 AP 发起四次握手协商密钥
- Station 发起 DHCP 获取 IP 地址
到这里网络通路已经可以正常运行,上层应用数据包可以发出去了。
Station 模式断开连接过程:
- Station 向 AP 发送断开连接请求 (Disassociation)
以上是正常断开连接过程。除此之外的异常断开连接还有 Station 掉电、重启、发送 DHCP 释放报文等。
4. 解决办法
使用 sys_callback_observer_create 创建类型为 CTRL_MSG_TYPE_NETWORK 的事件观察器后,当网络状态发生变化或底层完成相应的任务后,会触发对应的事件回调。创建方法如下:```
observer_base *ob = sys_callback_observer_create(
CTRL_MSG_TYPE_NETWORK,
NET_CTRL_MSG_ALL,
net_ctrl_msg_process,
NULL
);CTRL_MSG_TYPE_NETWORK 类型的事件目前有以下几种:
NET_CTRL_MSG_WLAN_CONNECTED /* 连接成功事件。代表 Station 关联或四次握手完成后(如果 WPA/WPA2 加密,则是四次握手完成后) /
NET_CTRL_MSG_WLAN_DISCONNECTED / 断开连接事件。代表 Station 请求断开连接成功后 /
NET_CTRL_MSG_WLAN_SCAN_SUCCESS / 扫描完成事件。代表 Station 完成一次扫描 /
NET_CTRL_MSG_WLAN_SCAN_FAILED / 扫描失败事件。代表 Station 扫描操作失败 /
NET_CTRL_MSG_WLAN_4WAY_HANDSHAKE_FAILED / 四次握手失败事件。代表 WPA/WPA2 四次握手过程失败 /
NET_CTRL_MSG_WLAN_CONNECT_FAILED / 连接失败时间。代表 Station 在认证/关联阶段失败 /
NET_CTRL_MSG_CONNECTION_LOSS / 连接丢失事件。代表 Station 当前连接丢失,可能是 AP 修改了配置重启、掉电之类的,导致 Station 无法扫描到 /
NET_CTRL_MSG_NETWORK_UP / 网络启动完成事件。代表已经获取到 IP 地址(DHCP 完成),网卡已经可以正常工作,通过协议栈收发网络中的数据包 /
NET_CTRL_MSG_NETWORK_DOWN / 网络关闭事件。代表已经发送 DHCP 释放报文,而且网卡已经关闭 /
NET_CTRL_MSG_NETWORK_IPV6_STATE / IPv6 地址状态事件。代表 IPv6 地址状态发生了变化 */
NET_CTRL_MSG_ALL = ALL_SUBTYPE
}; -
【FAQ】全志XR806芯片 串口修改波特率后与目标波特率不匹配如何解决?
1、问题背景
有客户反应,XR系列MCU在修改完串口波特率后,打印输出的是乱码,通过仪器抓波形发现输出的波特率与设置不一致。2. 问题描述
串口修改波特率后与目标波特率不匹配。3. 问题分析
XR系列MCU的波特率理论计算是baund=apb_freq/(16*div),其中apb_freq是APB总线时钟频率,div是分频系数,div计算结果采用去尾法会产生误差。板级文件board_config.h 中BOARD_APBS_CLK_SRC以及BOARD_APBS_CLK_FACTOR可配置APB时钟总线频率。实际应用中,串口误码率建议小于5%,从计算公式得知波特率会出现不是百分百的匹配的情况,但是波特率在允许的误差范围内是可以正常使用的。4.解决方法
通过调整BOARD_APBS_CLK_SRC以及BOARD_APBS_CLK_FACTOR、或者cpu主频来匹配相应的波特率,但是要注意同一个分频值可能出现不能同时兼容所有波特率的情况。下图是主频和分频计算出波特率的示例:
-
【FAQ】全志XR806芯片 getsockopt、setsockopt失败如何解决?
1. 问题背景
调用 setsockopt 设置 socket 属性失败,或者 getsockopt 获取 socket 属性失败。2. 问题描述
调用 setsockopt、getsockopt 时返回 -1,且 errno 为 ENOPROTOOPT(92)。3. 问题分析
LwIP 当前版本有部分 socket 属性的设置/获取是暂时没有支持的,操作这些属性会返回 errno = ENOPROTOOPT 的错误码。4. 解决办法
以 SDK 中 lwip-2.0.3 为例。可以通过打开文件 include/net/lwip-2.0.3/lwip/sockets.h 查看目前版本 LwIP 支持哪些 socket 属性的设置/获取。#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ #define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ #define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ #define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ #define SO_LINGER 0x0080 /* linger on close if data present */ #define SO_DONTLINGER ((int)(~SO_LINGER)) #define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ #define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ #define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ #define SO_RCVBUF 0x1002 /* receive buffer size */ #define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ #define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ #define SO_SNDTIMEO 0x1005 /* send timeout */ #define SO_RCVTIMEO 0x1006 /* receive timeout */ #define SO_ERROR 0x1007 /* get error status and clear */ #define SO_TYPE 0x1008 /* get socket type */ #define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ #define SO_NO_CHECK 0x100a /* don't create UDP checksum */
如上所示,若该属性后的注释有 Unimplemented 标识,则未支持。
-
【FAQ】全志XR806芯片 如何使用watchpoint功能?
1.问题背景
watchpoint,一般用来观察某个变量/内存地址的状态(也可以是表达式),如可以监控该变量/内存值是否被程序读/写情况。2.问题描述
在程序运行异常的时候,可以借助watchpoint来进行辅助调试。3.问题分析
在XR MCU SDK中,支持了watchpoint功能,通过使能指定宏以及调用指定函数来使用该功能。4.解决办法
(1)使能watchpoint的宏开关:export __CONFIG_WATCHPOINT:=y
(2)调用watchpoint_add函数添加观察点
以下是代码使用示例:#include <debug/watchpoint.h> static int watchpoint_test_value; static struct watchpoint wp; static enum cmd_status cmd_watchpoint_value_init(char *cmd) { int ret; watchpoint_test_value = 1; wp.address = (unsigned int)&watchpoint_test_value; wp.length = sizeof(watchpoint_test_value); wp.rw = DWT_WRITE; ret = watchpoint_add(&wp); if (ret) { printf("watchpoint_add fail.\n"); } return CMD_STATUS_OK; } static enum cmd_status cmd_watchpoint_value_change(char *cmd) { watchpoint_test_value = 0; return CMD_STATUS_OK; }
-
【FAQ】全志XR806芯片 RTC时钟不能跑、有时候时间不准是什么原因?
1、问题背景
有客户反应,XR系列MCU在跑rtc模块时,读取时间一直是同一个值,或者出现时间与现实时间相比不准确。2. 问题描述
RTC时钟不能跑、有时候时间不准是什么原因。3.解决方法
(1)、先确定RTC使用的时钟源是内部震荡还是外部晶振。并且通过修改板级文件board_config.h中BOARD_LOSC_EXTERNAL这个宏定义来匹配相关配置(0是使用内部震荡作为震荡源,1是使用外部晶振),如果配置是外部晶振,但是却没有外接外部32.768khz晶振的话,RTC是跑不起来的。
#define BOARD_LOSC_EXTERNAL 0 /* 0: inter 32k, 1: external 32k */(2)、使用内部震荡的话,由于是rc震荡产生时钟,所以受到温度漂移影响很大,所以客户如果需要精度高的计时,建议使用外部晶振来作为震荡源。
-
【FAQ】全志XR806芯片 如何清除扫描列表缓存?
问题背景
XR MCU平台发起扫描后,会对扫描结果进行缓存一段时间。问题描述
Wi-Fi设备发起扫描,获取扫描结果。当关闭掉目标路由器后,再次进行扫描获取的扫描结果会依旧包含已关闭的路由器SSID。问题分析
由于实现机制,会对扫描结果进行一定时间缓存,导致已关闭的路由,仍然能扫描到。只要下次发起扫描时,对缓存进行清除,就可以解决该问题。解决办法
调用一下函数即可清除缓存:int wlan_sta_bss_flush(int age);
如移除30s内未更新的AP节点为例:
wlan_sta_bss_flush(30);
-
【FAQ】全志XR806芯片 standby模式下gpio的电平状态是如何变化?
1.问题背景
客户在做低功耗设计时经常会使用GPIO外接一些硬件外设,这些外设在芯片的管脚在休眠时候如果跟GPIO有压差就会产生漏电流。我们如何保证休眠时的GPIO的电平状态呢。2.问题描述
客户提问:XR808/XR809/XR871/XR872和XR806在standby模式下芯片的gpio电平状态如何保持的。3.解决办法
XR808/XR809/XR871/XR872在芯片进入standby模式后,如果属于wakeup IO的管脚电平状态处于高阻抗,要保持电平的话,需要调用HAL_Wakeup_SetIOHold才可以把wakeup io固定在某种电平状态。但是不属于wakeup IO的管脚进入休眠是不受控制的,仍然为高阻态。而XR806所有的GPIO都可以通过hold功能来保持原来的电平。
在硬件设计的时候,如果要休眠保持电平就选wakupio做普通io用。具体哪些管脚属于wakeup IO请在相关芯片的PIN_Multiplexing文档查询。 -
【FAQ】全志XR806芯片 如何解决编译错误undefined reference to?
1.问题背景
把SDK默认的Freertos切换为其他RTOS后,部分用户反馈,工程中已经定义某个函数,但是在编译最后依旧会报错undefined reference to(找不到某个函数)。2.问题描述
工程编译过程中没有报错,但是在最后连接过程时会提示错误undefined reference to"",找不到特定函数。3.问题分析
编译过程中没有报错,在最后连接时才报错,说明编译时引用了某个头文件,指明该函数需要用其他库实现。4.解决方法
1)首先使用nm命令检查该函数所在的.a静态库,确认该静态库中是否已经包含了该函数,如果没有该函数,则检查该函数的编译是不是需要使能某些宏。2)如果已经确认了静态库中已经包含了该函数,说明在gcc链接过程中被忽略了,则可以尝试使用以下三种方法。
-
把该静态库的顺序提前,如first.a中调用了second.a中的某个函数,但是编译时却报错找不到second.a的某个函数,尝试调换两个静态库的顺序。
-
使用-Wl,–start-group和-Wl,–end-group修饰该静态库,代表该静态库不会只检索一次,而是多次来回检索,防止遗漏。缺点如果修饰的静态库太多,会导致编译速度变慢。
-
使用-Wl,–whole-archive和-Wl,–no-whole-archive修饰静态库,代表该静态库的所有函数都不会被忽略,强制链接。缺点是如果该静态库中存在多余的函数,会浪费flash。
-
-
【FAQ】全志XR806芯片 执行扫描动作时,偶尔会扫描不到目标的AP如何解决?
1、问题背景
有客户反应,XR系列MCU在连接进行扫描附近AP时,扫描不出所需要的AP,但第二次或者第三次就能扫描出来了。2. 问题描述
当mcu执行扫描动作时,扫描不出所需要的ap时,可以通过哪些方法来改善这种情况。3. 问题分析
-
增加扫描结果的缓存
如果附近网络环境复杂,AP数量太多时,扫描达到缓存上限值就会忽略掉一些ap数。此时可以通过增加扫描结果的缓存来让目标AP显示出来。具体方法可通过net sta scan result_num指令来查询当前AP的上限值(默认是20个),设置上限则通过net sta bss max count 和net sta scan result 指令来实现(num代表实际所需的AP数量)。函数实现在cmd_wlan.c里面查询相关指令接口。 -
增加信道停留时间
同一个信道的信道停留时间也有限,如果附近的AP都挤在同一个的信道,扫描这个信道的时间超时了也会忽略掉一些AP的信息。所以有时候也需要通过增加信道停留时间的方式来改善扫不出目标AP的情况。具体方法可通过以下代码来实现:
param.num_probes = num_probes; param.probe_delay = probe_delay; param.min_dwell = min_dwell; param.max_dwell = max_dwell; ret = wlan_ext_request(g_wlan_netif, WLAN_EXT_CMD_SET_SCAN_PARAM, (int)(¶m)); 其中几个参数的典型值是:n=2 d=100 min=100 max=125 n
-
-
【FAQ】全志XR806芯片 如何打开 LwIP 调试信息?
1. 问题背景
出现网络问题时,常常需要打开 LwIP 内部打印信息调试、查看协议栈运行状态,以获取更多的有效信息。2. 问题描述
如何打开 LwIP 调试信息?3. 问题分析
LwIP 内部调试信息有两类:
(1) LWIP_DEBUG,可以显示协议栈内部各层运行流程,收发通路信息等。
(2) LWIP_STATS,可以显示协议栈内部各种资源使用情况,包括内存、队列、信号量等。4. 解决办法
(1) 打开 LWIP_DEBUG 的方式
文件 include/net/lwip-x.x.x/arch/cc.h 中使能 DEBUG:/* Debug on/off */ #define LWIP_DEBUG
文件 include/net/lwip-2.0.3/lwipopts.h 中
设置调试等级,使能全局调试信息:
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL #define LWIP_DBG_TYPES_ON LWIP_DBG_ON
根据需要打开相应类别的调试信息,如 socket 相关调试信息:
#define SOCKETS_DEBUG LWIP_DBG_ON
(2) 打开 LWIP_STATS 的方式
文件 include/net/lwip-2.0.3/lwipopts.h 中使能 LWIP_STATS:
#define LWIP_STATS 1 #define LWIP_STATS_DISPLAY 1
根据需要打开相应类别的统计信息(默认已打开),如协议栈堆内存使用信息:
#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0))
最后自行调用相应统计函数,就可以显示当前协议栈统计信息:
LINK_STATS_DISPLAY(); // 链路层统计信息 ETHARP_STATS_DISPLAY(); // ARP层统计信息 IPFRAG_STATS_DISPLAY(); // IP层分片统计信息 IP_STATS_DISPLAY(); // IP层统计信息 IGMP_STATS_DISPLAY(); // IGMP协议统计信息 ICMP_STATS_DISPLAY(); // ICMP协议统计信息 UDP_STATS_DISPLAY(); // UDP层统计信息 TCP_STATS_DISPLAY(); // TCP层统计信息 MEM_STATS_DISPLAY(); // 堆内存使用统计信息 MEMP_STATS_DISPLAY(i); // 内存池i的使用统计信息 SYS_STATS_DISPLAY(); // 系统统计信息,包括队列(mbox)、信号量、锁 stats_display(); // 打印上面所有信息
-
【FAQ】全志XR806芯片 select引发崩溃如何解决?
1. 问题背景
lwip-1.4.1 版本使用 select 函数后引发崩溃。
内部报错:sock != NULL at line 1296 in src/api/sockets.c2. 问题描述
在多线程同时使用 lwip-1.4.1 版本的 select 函数,可能产生崩溃问题。3. 问题分析
lwip-1.4.1 中 lwip_select 函数获取 sock 结构时,未对空指针进行处理,从而引发崩溃。多线程操作同个 socket 的场景下易复现该问题。
该 bug 在 lwip 后续版本中进行了修复。4. 解决办法
方法 (1): 使用 lwip-2.0.3
因为 lwip-2.0.3 中已修复该 bug,切换使用即可。
xradio_skylark_sdk 中切换使用 lwip-2.0.3 的方法:在本地工程的 gcc/localconfig.mk 内部导出 __CONFIG_LWIP_V1 为 n。如下所示:export __CONFIG_LWIP_V1 := n
方法 (2): 合入 lwip 修复成果至 lwip-1.4.1
lwip 开源代码获取方式:git clone https://git.savannah.gnu.org/git/lwip.git
该 bug 在提交 5ceaed291f2c1320d36f9501fadd51923fa1c556 中修复,查看修改的代码:
git show 5ceaed291f2c1320d36f9501fadd51923fa1c556
修改内容如下所示:
commit 5ceaed291f2c1320d36f9501fadd51923fa1c556 Author: sg <goldsimon@gmx.de> Date: Sat Jan 17 21:02:58 2015 +0100 fixed bug #43361 select() crashes with stale FDs diff --git a/CHANGELOG b/CHANGELOG index 2c9aebbb..3bf40441 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -152,6 +152,9 @@ HISTORY ++ Bugfixes: + 2015-01-17: Simon Goldschmidt + * sockets.c: fixed bug #43361 select() crashes with stale FDs + 2015-01-17: Simon Goldschmidt * sockets.c/.h, memp_std.h: fixed bug #40788 "lwip_setsockopt_internal() crashes" by rewriting set/getsockopt functions to combine checks with the actual code diff --git a/src/api/sockets.c b/src/api/sockets.c index 3369c6d1..4109cee4 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -1209,6 +1209,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, u32_t msectimeout; struct lwip_select_cb select_cb; int i; + int maxfdp2; SYS_ARCH_DECL_PROTECT(lev); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n", @@ -1266,47 +1267,69 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, SYS_ARCH_UNPROTECT(lev); /* Increase select_waiting for each socket we are interested in */ - for(i = 0; i < maxfdp1; i++) { + maxfdp2 = maxfdp1; + for (i = 0; i < maxfdp1; i++) { if ((readset && FD_ISSET(i, readset)) || (writeset && FD_ISSET(i, writeset)) || (exceptset && FD_ISSET(i, exceptset))) { - struct lwip_sock *sock = tryget_socket(i); - LWIP_ASSERT("sock != NULL", sock != NULL); + struct lwip_sock *sock; SYS_ARCH_PROTECT(lev); - sock->select_waiting++; - LWIP_ASSERT("sock->select_waiting overflow", sock->select_waiting > 0); + sock = tryget_socket(i); + if (sock != NULL) { + sock->select_waiting++; + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + } else { + /* Not a valid socket */ + nready = -1; + maxfdp2 = i; + SYS_ARCH_UNPROTECT(lev); + break; + } SYS_ARCH_UNPROTECT(lev); } } - /* Call lwip_selscan again: there could have been events between - the last scan (without us on the list) and putting us on the list! */ - nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); - if (!nready) { - /* Still none ready, just wait to be woken */ - if (timeout == 0) { - /* Wait forever */ - msectimeout = 0; - } else { - msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); - if (msectimeout == 0) { - /* Wait 1ms at least (0 means wait forever) */ - msectimeout = 1; + if (nready >= 0) { + /* Call lwip_selscan again: there could have been events between + the last scan (without us on the list) and putting us on the list! */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + if (!nready) { + /* Still none ready, just wait to be woken */ + if (timeout == 0) { + /* Wait forever */ + msectimeout = 0; + } else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if (msectimeout == 0) { + /* Wait 1ms at least (0 means wait forever) */ + msectimeout = 1; + } } - } - waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout); + waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout); + } } - /* Increase select_waiting for each socket we are interested in */ - for(i = 0; i < maxfdp1; i++) { + + /* Decrease select_waiting for each socket we are interested in */ + for (i = 0; i < maxfdp2; i++) { if ((readset && FD_ISSET(i, readset)) || (writeset && FD_ISSET(i, writeset)) || (exceptset && FD_ISSET(i, exceptset))) { - struct lwip_sock *sock = tryget_socket(i); - LWIP_ASSERT("sock != NULL", sock != NULL); + struct lwip_sock *sock; SYS_ARCH_PROTECT(lev); - LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); - sock->select_waiting--; + sock = tryget_socket(i); + if (sock != NULL) { + /* @todo: what if this is a new socket (reallocated?) in this case, + select_waiting-- would be wrong (a global 'sockalloc' counter, + stored per socket could help) */ + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + if (sock->select_waiting > 0) { + sock->select_waiting--; + } + } else { + /* Not a valid socket */ + nready = -1; + } SYS_ARCH_UNPROTECT(lev); } } @@ -1330,6 +1353,12 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, sys_sem_free(&select_cb.sem); #endif /* LWIP_NETCONN_SEM_PER_THREAD */ + if (nready < 0) { + /* This happens when a socket got closed while waiting */ + set_errno(EBADF); + return -1; + } + if (waitres == SYS_ARCH_TIMEOUT) { /* Timeout */ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
-
【FAQ】全志XR806芯片 低功耗蓝牙BLE断开连接错误码和分析?
1、问题背景
硬件:R系列芯片,XR806
软件:Tina2、问题简述
说明:该FAQ旨在列出和分析低功耗蓝牙BLE常见的断开连接原因,方便排查连接过程、连接之后和绑定过程中的断开连接问题。3、问题分析和解决办法
蓝牙低功耗BLE连接时和连接后和绑定过程中可能会有不同原因造成蓝牙断开,下面是比较常见的几种断开错误码和原因分析。
-
【FAQ】全志XR806芯片 如何设置AP模式默认IP地址、子网掩码、网关?
1. 问题背景
特殊需求想要修改 AP 模式使用的 IP 地址、子网掩码、网关。2. 问题描述
xradio_skylark_sdk 中如何设置 AP 模式默认 IP 地址、子网掩码、网关?3. 问题分析
目前 SDK 中使用都是使用静态的方法去配置 AP 模式的 IP 地址、掩码、网关以及 DHCP 的地址池范围。若要修改,则需要到指定文件中修改。注意:若修改了 AP 的 IP 地址,需要确保 DHCP 分配的地址池与 AP 的 IP 地址是同一子网段下。4. 解决办法
(1) 修改 AP 模式的 IP 地址的方法
在 project/common/framework/sysinfo.c 的 int sysinfo_default(void) 函数内部进行修改:/* netif AP */ IP4_ADDR(&g_sysinfo.netif_ap_param.ip_addr, 192, 168, 51, 1); IP4_ADDR(&g_sysinfo.netif_ap_param.net_mask, 255, 255, 255, 0); IP4_ADDR(&g_sysinfo.netif_ap_param.gateway, 192, 168, 51, 1);
(2) 修改 DHCP 地址池范围的方法
在 src/net/udhcp-0.9.8/dhcpd_cfg.h 进行修改:#define DHCPD_ADDR_START "192.168.51.100" #define DHCPD_ADDR_END "192.168.51.104" #define DHCPD_INTERFACE "en1" #define DHCPD_OPTION "" #define DHCPD_OPT "" #define DHCPD_REMAIN "yes" #define DHCPD_MAX_LEASES "5"
-
【FAQ】全志XR806芯片 如何修改中断优先级?
1.问题背景
XR809/XR871/XR808/XR872上如何修改中断的优先级。2.问题描述
很多模块都会使用到中断,但中断的优先级在模块的初始化或者在使能中断时就设定了,那么如何修改中断的优先级呢?3.解决办法
3.1 中断是如何设定优先级的
中断的优先级是在下面这个函数设定的,每个带中断的模块都会调用到这个函数
HAL_NVIC_ConfigExtIRQ(GPADC_IRQn, GPADC_IRQHandler, NVIC_PERIPH_PRIO_DEFAULT);(第二个参数代表的是优先级)
这个HAL_NVIC_ConfigExtIRQ会调用到HAL_NVIC_SetPriority函数
HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)函数的2个参数分别是中断类型以及中断优先级。
最终是HAL_NVIC_SetPriority进行了中断优先级的设定的。3.2如何设定中断优先级
(1).本来修改模块的优先级通过修改这个模块的HAL_NVIC_ConfigExtIRQ函数即可,但大部分模块的初始化函数都已经rom化了,直接修改rom化的初始化函数是无法生效的。(2).所以首先我们需要找到模块中哪里调用了HAL_NVIC_ConfigExtIRQ函数,然后在应用层代码中添加HAL_NVIC_SetPriority函数重新设定优先级
例如:
GPIO模块在HAL_GPIO_EnableIRQ模块中调用到了HAL_NVIC_ConfigExtIRQ函数,那么需在上层应用代码中再次调用HAL_NVIC_SetPriority
函数来进行重新设定中断的优先级HAL_GPIO_EnableIRQ ---> 这个接口里面的代码已经rom化,不能进行优先级设定 HAL_NVIC_SetPriority ---> 重新设定优先级,覆盖HAL_GPIO_EnableIRQ函数里设定的默认值
ADC模块在HAL_ADC_Init模块中调用到了HAL_NVIC_ConfigExtIRQ函数,那么需在上层应用代码中再次调用HAL_NVIC_SetPriority
函数来进行重新设定中断的优先级HAL_ADC_Init ---> 这个接口里面的代码已经rom化,不能进行优先级设定 HAL_NVIC_SetPriority ---> 重新设定优先级,覆盖HAL_ADC_Init函数里设定的默认值
(3).需要注意HAL_NVIC_SetPriority函数的2个参数
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) IRQn_Type IRQn ---> 中断类型,在应用层需要根据具体的模块进行填写,例如GPIO需要填GPIOA_IRQn/GPIOB_IRQn,ADC需要填GPADC_IRQn uint32_t priority ---> 优先级,根据具体所需进行填写,数字越小优先级越低
-
【FAQ】全志XR806芯片 如何使用timer命令行命令?
1.问题背景
XR809/XR871/XR808/XR872上timer如何使用2.问题描述
timer模块在芯片中只是一个小模块,在发布文档中没有专门的文档进行说明,只有代码用例,部分客户看代码后会对timer里的一些参数云里雾里的3.解决办法
下面会从几个方面帮助客户去了解以及测试timer模块。3.1如何在固件中添加测试timer测试用的命令行命令:
(1).project/XXX/command.c文件中添加timer命令如下:
--- a/platforms/xr806/xr806-os/project/tuya/command.c +++ b/platforms/xr806/xr806-os/project/tuya/command.c @@ -181,6 +181,7 @@ static const struct cmd_data g_main_cmds[] = { + { "timer", cmd_timer_exec }, { "sysinfo",cmd_sysinfo_exec, CMD_DESC("system information command") }, { "help", cmd_main_help_exec, CMD_DESC(CMD_HELP_DESC) },
(2).添加完成后编译烧录固件在命令行中输入help命令可以看到timer命令被编译进固件中了:
[*] net : network command [*] rf : radio frequency command [*] drv : driver command [*] echo : echo command [*] mem : memory command [*] heap : heap use information command [*] thread : thread information command [*] upgrade : upgrade command [*] reboot : reboot command [*] ota : over the airtechnology upgrade commands [*] etf : etf command [*] pm : power management command [*] efpg : efpg command [*] flash : flash control command [*] lmac : low mac command [*] timer [*] sysinfo : system information command [*] ble [*] help : print this message and quit
(3)timer命令的源码在project/common/cmd/cmd_timer.c,用户可以对该源码进行修改测试,也可以参考该文件的timer接口进行应用开发。
3.2timer命令如何使用
(1)timer命令行命令支持下面几个子命令:
timer config i=X m=X s=X d=X p=X --> 配置timer的参数,参数后面说明 timer deconfig i=X --> 取消当前timer的配置参数 timer start i=X --> 启动对应的timer(支持2个timer 0/1) timer stop i=X --> 停止对应的timer timer pause i=X --> 暂停对应的timer timer continue i=X --> 继续对应的timer Timer value i=X --> 获取对应timer的值(该值是剩余多少定时时间,需计算)
(2)timer命令行参数说明:
参数i:0/1 --> 控制的是哪个timer,806支持2个timer,0/1 参数m:repeat/once --> 定时模式,repeat:循环定时模式,once:一次定时模式 参数s:LF/HF --> 时钟源,LF:低频32k晶振,HF:高频晶振,该晶振为外部所接晶振,例如如果接的是40M,HF就是40M,接的是26M,HF就是26M 参数d:1/2/4/8/16/32/64/128 --> 分频系数 参数p: --> 定时period值,该值需要用户根据时钟源,分频系数,定时时间进行计算: 需要用到的计算公式如下: interval = period / ( clk-src / clk-div ) period = interval * ( clk-src / clk-div )
(3)公式参数说明:
参数interval:需要定时的值(例如5s,0.5s) 参数clk-src:时钟源(例如40M,26M) 参数clk-div:分频系数(分频系数的选择影响精度和定时时间长短范围) 参数period:根据定时值,时钟源,分频系数计算出来的值,该值会写到timer寄存器中,timer模块 就是根据这个值进行定时的。
例子:例如需要定时5s,板子外接时钟源是26M,分频系数选4,那么根据公式可以计算出period的值应该需要填32,500,000
4.timer命令实操说明说明
timer config i=1 m=once s=HF d=4 p=32500000 -- 1 <ACK> 200 OK timer start i=2 -- 2 <ACK> 200 OK timer value i=1 -- 3 <ACK> 200 value=19462685 timer value i=1 -- 4 <ACK> 200 value=4617179 timer value i=1 -- 5 <ACK> 200 value=0
(1).配置timer 1,模式设定once模式,时钟源选HF(26M),分频选4,因为想定时5s,因此算出p值需要填写32500000 (2).启动定时器1 (3).第一次获取period值19462685,根据公式interval = period / ( clk-src / clk-div ) 可以算出剩余时间interval = 19462685 / (26000000 / 4) = 2.994s (4).同上可以算出第二次剩余时间 interval = 4617179 / (26000000/4) = 0.71s (5).第3次获取值时因为已经超过5s,所以获取到的值为0
想设定其他分频和时间可根据公式以及需要设定的分频时间算出period值填到对应的函数接口即可。
其他命令相对简单,因此不再进行实操显示
注意:
如果遇到timer测试定时时间与现实实际实际不符,可以从下面一些方向进行排查:
(1).计算晶振与实际晶振部分(例如:计算时使用26M,但实际板子接的时40M)
(2).重复计算一下period值,period值填写错误也会导致定时时间不准确附件timer_pparam.xlsx:timer_param.xlsx
附件是26M参数的时间,分频,period值的参考表,其他频率的晶振也可以参考该表算出period值。 -
【FAQ】全志XR806芯片 固件烧录完成后,程序不是预期烧录程序或者甚至无法启动如何解决?
1.问题背景
872平台上烧录成功后,程序运行没有改变,还是原来的程序,有时候甚至烧录成功后系统无法启动。2.问题描述
烧录固件完成后,提示烧录成功,断上电后,程序跑的还是原来的程序,没有烧录成功,同时还出现概率性提示烧录成功后无法启动现象。3.问题分析
程序跑的还是原来的,同时还出现烧录成功后无法启动。如果img没有问题,那么大概率就是程序根本没有烧录到flash中。所以在烧录flash前,勾选上"写入后进行校验"。勾选上写入校验后,发现,烧录异常,无法进行写入。
4.解决办法
根本原因还是在于固件没有写入 -
【FAQ】全志XR806芯片 mac地址要如何存放以及如何获取?
1.问题背景
XR809/XR871/XR808/XR872上的mac地址改如何存放,还有获取的方式是怎样。2.问题描述
客户使用中XR809/XR871/XR808/XR872中,可能不了解mac地址的存放以及获取方式。3.解决办法
3.1 MAC地址存放在什么地方的
根据prj_config.h里面的#define PRJCONF_MAC_ADDR_SOURCE 来决定
1、SYSINFO_MAC_ADDR_FLASH是sysinfo里面的一个缓冲区。
2、SYSINFO_MAC_ADDR_CHIPID是根据chipid来随机生成,有小概率重复。
3、SYSINFO_MAC_ADDR_EFUSE写进efuse里面的mac地址。
注:MAC地址的第一个字节必须为偶数,可参考mac地址规则3.2 MAC地址怎么获取
如mac地址存放在flash,使用控制台指令:sysinfo get mac
如mac地址存放在efuse,使用控制台指令:efpg get mac -
【FAQ】全志XR806芯片 为什么开机时候串口以及部分gpio会有电平跳变?
1.问题背景
客户在测试时,发现开机的一瞬间串口以及部分gpio会有电平跳变,这个电平跳变的来源是哪里的,是否能消除掉的。2.问题描述
客户设备在开机的时候PB2上的LED会闪烁一下,使用示波器抓有一段5毫秒左右的脉冲。3.解决办法
由于XR系列MCU最开始运行的是固化在ROM里面的brom代码,会执行下列操作:
1、PB2、PB3管脚会反初始化后再初始化成输入功能GPIO,用于检测是否进入烧录模式,导致PB2、PB3管脚会有跳变。
2、UART0会初始化,用于与烧录工具通信,导致UART0的TX、RX管脚有跳变。
3、如果该型号的MCU的flash是外挂的话,那么操作flash相关的管脚也会有跳变,包括hold管脚与wp管脚。
从跳变原因来看,这几个管脚是来自brom的操作,已经固化到芯片上,所以是不能消除的,所以硬件设计的时候需要注意。 -
【FAQ】全志XR806芯片 如何更换打印log口?
1.问题背景
XR809/XR871/XR808/XR872/XR806 上如何把log口uart0更换为uart1或uart2。2.问题描述
我司SDK发布时镜像log默认由uart0输出,有些客户的产品需要把log从非uart0口输出(例如从uart1或uart2输出)。3.解决办法
3.1 修改board_config.c文件,配置对应uart口的ping脚
例如:(下面的代码只做事例讲解,实际代码不同平台可能会不一样,请根据原理图和数据手册进行修改):
(1).添加对应uart的脚static const GPIO_PinMuxParam g_pinmux_uart2[] = { { GPIO_PORT_B, GPIO_PIN_14, { GPIOB_P14_F3_UART2_TX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, /* TX */ { GPIO_PORT_B, GPIO_PIN_15, { GPIOB_P15_F3_UART2_RX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, /* RX */
(2).确保board_config.c中board_get_pinmux_info函数已有对应的uart口的初始化代码(有些版本可能没有uart2的初始化代码,需要自行添加)
case HAL_DEV_MAJOR_UART: if (minor == UART0_ID) { info[0].pinmux = g_pinmux_uart0; info[0].count = HAL_ARRAY_SIZE(g_pinmux_uart0); } else if (minor == UART1_ID) { info[0].pinmux = g_pinmux_uart1; info[0].count = HAL_ARRAY_SIZE(g_pinmux_uart1); } else if (minor == UART2_ID) { info[0].pinmux = g_pinmux_uart2; info[0].count = HAL_ARRAY_SIZE(g_pinmux_uart2); } else { ret = HAL_INVALID; } break;
3.2 修改board_config.h文件配置log从那个uart输出
例如:把log从uart0修改为从uart2输出#define BOARD_MAIN_UART_ID UART0_ID --改为--> #define BOARD_MAIN_UART_ID UART2_ID
注意点:
不同芯片不同方案会有不同的board_config.c和board_config.h文件,确保修改的文件是你所需要的方案 -
【FAQ】全志XR806芯片 系统异常重启如何解决?
1. 问题背景
产品在做稳定性测试,发现设备异常重启2. 问题描述
用户应用在做长运压测,发现设备长运2~3天后会异常重启,log中未发现有代码奔溃现象。3. 问题分析
系统重启,需要判断重启原因,系统中的重启原因有如下:typedef enum { SYS_POWERON = 0, //硬件上电启动 SYS_WATCHDOG_CHIP_RST = 1, //看门狗复位重启,包含整个系统 SYS_WATCHDOG_CPU_RST = 2, //看门狗复位重启,仅CPU SYS_REBOOT = 6, //执行reboot命令重启 SYS_CPU_RST = 7, //cpu异常复位启动 SYS_NVIC_RST = 8, //中断异常重启 }SystemStartupState;
在系统阶段就可以插桩代码,判断重启原因,下面是示例代码:
const char* startup_state_str[] = {"powerOn", "wdgSocRst", "wdgCpuRst", "sleep", "standby", "hibernation", "reboot", "cpuRst","nvicRst"}; printf("startup state: %s\n", startup_state_str[SysGetStartupState()]);
通过插桩代码,就可以明确重启原因,导致看门狗喂狗不及时而重启。
4. 解决办法
(1)插桩代码,判断重启原因。
(2)如果是看门狗重启,大部分情况下看门狗重启都是由于代码执行异常卡死,解决该问题,就先把看门狗关掉,接上JTAG等调试工具,程序执行异常,就会block住(不会重启),就可以利用jtag判断代码执行位置,进而找出异常点。 -
【FAQ】全志R329芯片FAQ汇总(你不知道的和你想知道的的这里都有)
01、【FAQ】全志R329 Tina中如何使用adb/串口密码登录?
02、【FAQ】全志R329 Tina中使用如何使用perf分析CPU使用率?
03、【FAQ】全志R329以太网模块初始化失败如何解决?
04、【FAQ】全志R329以太网网络不通或丢包严重怎么解决?
05、【FAQ】全志R329以太网常用调试命令和排查手段
06、【FAQ】全志R329以太网使能报No phy found或Initialize hardware error怎么解决
07、【FAQ】全志R系列Tina系统Wi-Fi联网失败排查思路
08、【FAQ】全志R系列Tina-Linux4.9部分平台唤醒源配置
09、【FAQ】全志R系列休眠唤醒使用和框架介绍
10、【FAQ】全志R329 Tina下如何使用硬件Watchdog?
11、【FAQ】全志R系列启动频率的修改方法
12、【FAQ】全志R329如何查看和修改sdio的频率?
13、【FAQ】全志R329如何进行coredump的配置与调试?
14、【FAQ】全志R系列如何进行Tina根文件系统定制?
15、【FAQ】全志R系列Tina内核配置不生效如何解决?
16、【FAQ】全志R329如何在DragonSN烧写mac地址?
18、【FAQ】全志R系列如何解决wpa_supplicant服务启动问题?
19、【FAQ】全志R329如何进行WiFi驱动收发帧打印控制?
20、【FAQ】全志R329如何解决Tina删除内核根目录.config后一直编译失败的问题?
21、【FAQ】全志R329如何于Tina下支持多用户?
22、【FAQ】全志R系列Wi-Fi唤醒问题排查思路
23、【FAQ】全志R329如何解决Tina双系统安全固件启动失败问题?
24、【FAQ】全志R329如何通过wpa_cli手动调试wifi station?
25、【FAQ】全志R329如何实现Tina secureboot 打包分离模式支持?
26、【FAQ】全志R329如何解决在线mp3流媒体seek异常?
27、【FAQ】全志R系列wpa_supplicant和wpa_cli简介
28、【FAQ】全志R系列Tina 如何使用 NFS?
29、【FAQ】全志R329如何在平台修改uboot阶段CPU频率?
30、【FAQ】全志R329Tina下如何判断是安全固件?
31、【FAQ】全志R329如何将I2C时钟频率变成800kHz?
32、【FAQ】全志R系列如何在Tina下使用bootchartd来分析rootfs启动时间?
33、【FAQ】全志R329如何通过uboot修改设备树属性?
34、【FAQ】全志R329如何解决RTL驱动hostap启动失败的问题?
35、【FAQ】全志R329如何解决无法将wlan0加到br-lan这个网桥上面的问题?
36、【FAQ】全志R329如何解决蓝牙设备断开慢的问题?
37、【FAQ】全志R329如何利用wpa_cli设置国家代码?
38、【FAQ】全志R329Tina网络adb的使用
39、【FAQ】全志R329如何解决在Audiocodec使用S24_LE格式进行录音时产生的软件分析波形异常问题?
40、【FAQ】全志R系列如何解决gpio-keys驱动在系统启动上报两次input事件?
41、【FAQ】全志R329以太网常用调试说明
42、【FAQ】全志R329如何在Linux Device Tree中配置预留内存?
43、【FAQ】全志R329如何正确查看XRADIO相关版本信息?
44、【FAQ】全志R329如何解决调用Bluez mainloop_add_fd函数失败的问题?
45、【FAQ】全志R329蓝牙设备类型分类与过滤方法
46、【FAQ】全志R329一些正常的I2C报错
47、【FAQ】全志R329如何关闭HT40?
48、【FAQ】全志R329 XRADIO如何查看发射功率和频偏?
49、【FAQ】全志R329如何在WiFi Softap模式添加自定义Vendor IE?
50、【FAQ】全志R329如何解决蓝牙播放无声(snd_pcm_open error: Out of memory)?
51、【FAQ】全志R329 多台设备的adb device id相同,无法指定设备,如何指定设备的adb device id?
52、【FAQ】全志R329如何解决Tina中package下新增软件包目录很深导致检测不到问题
53、【FAQ】全志R329如何使用DMIC的高通滤波寄存器滤掉低频噪声?
54、【FAQ】全志R329如何解决Tina双系统uboot校验rootfs失败的问题?
55、【FAQ】全志R329如何在Tina安全应用程序调试?
56、【FAQ】全志R329在dtbo中如何配置gpio?
57、【FAQ】全志R329 LRADC电压设置、识别精度、识别误差、应用等相关知识
58、【FAQ】全志R329如何设置蓝牙自动重连时间或关闭自动重连?
59、【FAQ】全志R系列 Tina 如何查看通过 procd init 脚本启动的应用输出到 stdout/stderr 的打印信息?
60、【FAQ】全志R329如何在Tina在不烧写secure bit下开发OPTEE安全应用?
61、【FAQ】全志R329 MiniGUI如何获取和设置BITMAP像素点?
62、【FAQ】全志R329如何判断Tina安全启动boot分区证书与boot分区数据是匹配与否?
63、【FAQ】全志R329Tina安全启动校验linux/rootfs失败直接重启如何解决?
64、【FAQ】全志R329Tina下几种安全存储实现有何区别
65、【FAQ】全志R329 Tina下为何有些optee的demo只有NA源码没有TA源码?
66、【FAQ】全志R329Tina中TA下有哪些加解密接口?
67、【FAQ】全志R329Tina下无法选中libcedarx如何解决?
68、【FAQ】全志R系列在Tina下如何确认方案的optee版本信息
69、【FAQ】全志R系列在Tina下如何设置optee-secure-storage默认保存路径
70、【FAQ】全志R329如何使用audiocodec如何减少snd_pcm_open的耗时
71、【FAQ】全志R329如何进入monitor模式
72、【FAQ】全志R329如何进行Tina Linux蓝牙低功耗默认连接参数修改
73、【FAQ】全志R329在Tina如何在蓝牙已连接情况下拒绝其他耳机回连
74、【FAQ】全志全系列芯片 APST平台无法下载或者更新工具
75、【FAQ】全志 F系列/R系列/V系列 RTOS平台cache操作接口介绍
76、【FAQ】全志系列芯片如何把flash擦成空片?
77、【FAQ】持续更新中...... -
R329语音识别视频教程,从编译到部署,完全可用
R329语音识别 视频教程, 从编译到部署,完全可用
R329语音识别之前发布了一个简单版本, 今天终于出了一个详细的版本, 可以自行在 R329上实现编译
获取到语音识别的结果用于其他的DIY项目
手把手教会如何编译, 以及在哪修改源码更改功能视频地址: https://www.bilibili.com/video/BV1Rq4y1B7WH?spm_id_from=333.999.0.0
项目地址: https://github.com/7758258abc/r329_speed
下载Maix-Speech-master的地址: https://github.com/sipeed/Maix-Speech -
RISC-V SoC + AI | 在全志 D1「哪吒」开发板上,跑个 ncnn 神经网络推理框架的 demo
引言
D1 是全志科技首款基于 RISC-V 指令集的 SoC,主核是来自阿里平头哥的 64 位的 玄铁 C906。「哪吒」开发板 是全志在线基于全志科技 D1 芯片定制的 AIoT 开发板,是目前还比较罕见的使用 RISC-V SoC 且可运行 GNU/Linux 操作系统的可量产开发板。
ncnn 是腾讯优图实验室推出的一个为移动端极致优化的高性能神经网络前向计算框架,是目前同样也比较罕见的为 RISC-V 架构做过适配和优化的神经网络框架。
本文 是一份教程,步骤骑着步骤 (step by step) 地展示了如何在一块全新的全志 D1「哪吒」开发板上,跑个 ncnn 神经网络推理框架的 demo。
本文的完成参考了以下材料,非常感谢 nihui、BedRock 等开发者们在互联网上的分享!
[1] D1 哪吒 - 在线文档
[2] nihui: 在全志d1开发板上玩ncnn
[3] 腾讯优图ncnn新版本适配国产CPU,全志D1加持最高速度提升70倍!目录
- 引言
- 必要的材料
- 上电!
- 重刷固件
- 安装并配置交叉编译工具链
- 编译 ncnn,并准备 demo 程序
- 使用 ADB 传输文件
- 运行 demo
1、必要的材料
- 「哪吒」开发板
- Type-C USB 线
- USB 转 TTL 转换器
- 安装有 GNU/Linux 或较新版本的 Windows 10 / 11 的可联网电脑
(本文以 Windows 10 的电脑为例来展示)
2、上电
取出哪吒和 USB 转 TTL 转换器,先把转换器按下图所示接上 DEBUG 端口,再将转换器插入电脑的 USB 口。如果电脑没有识别到串口,可能是因为没有安装转换器的驱动程序,可以在 这里 下载。
在使用开发板自带的固件时,DEBUG 端口会在开发板上电后通过 115200 波特率的 UART 串口来提供一个 Shell,我们可以使用 PuTTY、MobaXterm 或者其它类似的终端模拟器访问这个串口来在 Shell 中操作开发板。(如果使用 PuTTY,可以参考下图来设置)
启动终端模拟器后,暂时只能看到个黑框,因为开发板还没上电。
找出 Type-C USB 线,将开发板的 OTG 接口与电脑相连即可上电(虽然连 POWER 也可以,但是后边一直需要使用这个 OTG 接口,所以推荐连接它)。上电后可发现,开发板上的灯亮了,而且终端模拟器中开始一条条地冒出各种提示了!大概 10 ~ 20 秒后,根据一行按下回车的提示,在终端模拟器中按下回车,即可进入如下图所示的界面,然后就可以执行各种 GNU/Linux 命令了。
3、重刷固件
新开封的开发板里边自带的固件是基于 Open v1.0 版本的 D1 Tina SDK 编译的,在运行 ncnn 程序时会发生非法指令错误 (参考 这里),根据全志在线开发者论坛中网友 BedRock 的评论,我们可将固件更新为基于 Open v1.01 版本的 SDK 编译的固件再进行后续的步骤。
如果想自己编译固件或者修改固件,可以参考 D1 文档中的 “Tina SDK版本” 部分 下载 SDK。当然,如果只想先跑个 ncnn 玩玩看的话,我们可以直接在 “固件下载” 选择一款全志原厂为我们准备好的固件,比如我可以选择 D1哪吒HDMI测试固件20210804(开机HDMI就有小企鹅启动logo) (名字有点长) 为例。这个固件估计是为展示 HDMI 输出功能而设计的,我们给开发板用 HDMI 线接上个屏幕就能看到个小企鹅,但是因为它是基于 v1.01 SDK 编译的,所以我们刚好也能用它来跑 ncnn 的 demo。
下载好 .img 固件文件后,参考 D1 文档中 “编译与烧写” 的 “烧写” 部分 ,下载 全志USB驱动,使用管理员权限运行 install.bat 安装驱动,再安装烧写软件进行操作,即可将固件烧写进开发板。比如,在 Windows 电脑中我们要下载 PhoenixSuit 软件来烧写固件。要注意的是,在烧写软件中要选择 “全盘擦除升级”,如下图所示,否则无法成功烧写。
烧写成功后,重新上电即可。
4、安装并配置交叉编译工具链
注意:第 4 节和第 5 节的操作需要在 GNU/Linux 操作系统下进行。如果你的电脑装有 GNU/Linux 操作系统、或者可使用虚拟机,可直接在 GNU/Linux 操作系统中进行操作。如果你的电脑装的是较新版本的 Windows 10 / 11,也可以上网搜索相关教程安装并配置个 WSL (Windows Subsystem Linux),然后在 WSL 中进行操作。
在平头哥芯片开放社区的 “资源下载” 页面,我们可以在 “工具链-900系列” 中找到 V2.0.1 版本的 riscv64-linux-x86_64-20210512.tar.gz,下载它到电脑里,放到 <自己想放的路径>,解压它
tar -xf riscv64-linux-x86_64-20210512.tar.gz
解压完成后,使用 vim 或者其它文本编辑器,打开 ~/.bashrc,在结尾添加工具链的路径至环境变量,即下列内容
# My PATH export RISCV_ROOT_PATH=/<自己想放的路径>/riscv64-linux-x86_64-20210512
保存并关闭编辑器,然后使用 source 命令刷新 Shell 环境:
source ~/.bashrc
就安装并配置好交叉编译工具链了。
5、编译 ncnn,并准备 demo 程序
在 GitHub 上下载 2021 年 7 月 20 日 Release 的 ncnn 的源代码(Source code)到电脑里,比如 ncnn-20210720.tar.gz,解压,然后进入 ncnn-20210720 目录,准备并开始编译
mkdir build-c906 cd build-c906 cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/c906.toolchain.cmake -DCMAKE_BUILD_TYPE=relwithdebinfo -DNCNN_OPENMP=OFF -DNCNN_THREADS=OFF -DNCNN_RUNTIME_CPU=OFF -DNCNN_RVV=ON -DNCNN_SIMPLEOCV=ON -DNCNN_BUILD_EXAMPLES=ON .. make -j32
编译大概需要几分钟,完成后,可在 build-c906 目录中找到编译好的所有文件。
我们可先尝试两个 demo,分别为:
① 使用 benchncnn 做基准测试
② 使用 NanoDet 模型对一张自己的图片做目标检测(NanoDet 是个可在移动端超快运行的超轻量目标检测模型)为了方便后续将文件传输进开发板,我们可先在电脑上创建 benchncnn_demo 和 nanodet_demo 两个文件夹,将 ① 和 ② 所需的文件分别都放进去准备好。
对于 ①,我们把 ncnn 目录中的 build-c906/benchmark/benchncnn 和 benchmark/*.param 复制进去即完成准备。
对于 ②,我们需要把 ncnn 目录中的 build-c906/examples/nanodet 复制进去,在 ncnn-assets中下载 nanodet_m.bin 和 nanodet_m.param 两个文件放进去,再放入一张你想检测的图片即可。
即,两个目录的结构分别是类似这样的:
benchncnn_demo/ ├ benchncnn ├ alexnet.param ├ blazeface.param └ <一大堆其它模型的 param> nanodet_demo/ ├ nanodet ├ nanodet_m.bin ├ nanodet_m.param └ <你想检测的图片>
6. 使用 ADB 传输文件
ADB 是 Android Debug Bridge,原本为 Android 设计,全志给 Tina Linux 也做了适配,所以我们可以使用 ADB 来调试 Tina Linux 的设备。参考 D1 文档中的 “研发生产工具” 部分 可以下载,下载完成后,将其路径加入环境变量即可使用。
我们在跑这两个 demo 时,可能暂时只需要用到以下几个命令,如果需要使用其它功能也可上网搜索相关教程。
检查设备与电脑的连接情况,可以使用
adb devices
adb push 可以把电脑上的文件或文件夹传给开发板,使用语法如下
adb push <电脑上的文件路径> <开发板上的目标路径>
adb pull 可以把开发板上的文件或文件夹传给电脑,使用语法如下
adb pull <开发板上的文件路径> <电脑上的目标路径>
要注意的是,开发板上的路径都需要填写绝对路径。
7、运行 demo
把第 5 节所示的类似 benchncnn_demo 和 nanodet_demo 的文件夹通过 adb push 命令传进开发板,在开发板上进入 benchncnn_demo 目录,再输入
./benchncnn 4 1 0 -1 0
可开始做基准测试。如果成功运行即可慢慢看到类似下图这样的提示,展示了当前平台运行 ncnn 使用各种模型推理一帧所需的毫秒耗时。
(如果不想等测试慢慢跑完,也可以按 Ctrl + C 停止运行)
在开发板上进入 nanodet_demo 目录,再输入
./nanodet <你想检测的图片>
即可使用 NanoDet 对这张图片目标检测。运行完成后,可在命令行中看到文字版的检测结果,也可以使用 adb pull 把生成的 image.png 图片文件传给电脑再打开查看,上边会画好边界框、类别和置信度。比如这是它检测出的南京市长 江大桥上边的车车车车。
到了这里,我们就成功在一块运行 GNU/Linux 系统的 RISC-V 开发板上跑了个神经网络框架的 demo,如果想进行后续的实验或研发,可以了解有关 D1 哪吒 和 ncnn 的更多内容。
B站视频讲解:https://www.bilibili.com/video/BV1wQ4y1B7VU
本文来源:https://verimake.com/topics/275 -
【366期开放夜】自制一款低成本的RISC-V 64单板机(基于全志D1s)
本周开放夜嘉宾Young将为我们带来一个最低成本的RISC-V单板机分享。有哪些特征?
门槛高么?
可以拿来做些什么?
我也可以设计、制作一个吗?
我们的嘉宾将为您解答。
嘉宾介绍
Young
Young,电子科技大学通信工程专业大四学生。
既是从前端到操作系统均有涉猎的软件开发者,也是一名硬件hacker。自接触到嵌入式Linux以来深深被这个领域吸引,从ARM、MIPS到RISC-V架构的平台均有接触过。
RISC-V是什么?
RISC-V是一个基于精简指令集(RISC)原则的开源指令集架构(ISA),V表示为第五代。第五代的问世受到了大量创客们的广泛关注,但它的硬件却不易获得。
自制的RISC-V是什么样子的?
芯片
全志科技D1s是系统级封装(SIP)采用了阿里平头官方C906,具有单RISC-V 64内核@1.008G和64MB DDR2。
单板机介绍
在一个紧凑的56*56毫米的2层板上,引出全部IO,包括模拟外围设备
配有标准接口,包括USB、micro SD、LCD、Line-in和耳机
为自制优化布局,适合在加热台上焊接
拿来运行Linux妥妥的!
我们可以获得的体验感与现在的发行版别无二致,甚至可以安装任何我们想要的东西。
本次开放夜我们将采用线上分享
也欢迎大家来到线下一起交流讨论!
博云路111号 B1
期待的小伙伴们准备好加入我们!
活动时间:12月2日 19:30 - 21:00
我们不见不散!
线上观看直播入口
-
【小白自制Linux开发板】Debian文件系统制作,以及WIFI配置、交换分区配置
该片文章将完整记录一个Debian的最小文件系统的生成,以及自定义配置WIFI组件、网络组件和交换分区配置
本文章参考:
https://whycan.com/t_4236.html
http://www.leux.cn/doc/debootstrap.html(该网站在备案中,可能暂时无法查看了)
帖子整理完成。1. 制作Debian系统
构建debian文件系统,作为记录,最小rootfs在180MB左右。1.1 配置构建环境
安装构建文件系统的工具,一个是用来chroot,一个是用来构建文件系统sudo apt install qemu-user-static -y sudo apt install debootstrap -y mkdir rootfs
构建文件系统之前,你要知道你想要构建哪个版本的文件系统,
我从 https://www.debian.org/mirror/list.zh-cn.html 这里,
选择了我访问速度快的源,并且该源有armel。armhf (支持硬件浮点)
armel (软浮点)我看华为源挺快的,就用这个了mirrors.huaweicloud.com
然后就是debian的版本,我尝试一下最新的,bustersudo debootstrap --foreign --verbose --arch=armel buster rootfs http://mirrors.huaweicloud.com/debian/
构建完成之后,需要chroot进去修改密码等配置
cd rootfs sudo mount --bind /dev dev/ sudo mount --bind /sys sys/ sudo mount --bind /proc proc/ sudo mount --bind /dev/pts dev/pts/ cd .. sudo cp /usr/bin/qemu-arm-static rootfs/usr/bin/ sudo chmod +x rootfs/usr/bin/qemu-arm-static sudo LC_ALL=C LANGUAGE=C LANG=C chroot rootfs /debootstrap/debootstrap --second-stage --verbose sudo LC_ALL=C LANGUAGE=C LANG=C chroot rootfs
最后一条命令chroot完成,
此时可以先apt-get等给你的文件系统安装你需要的软件包为了提高下载速度,我们首先修改源
vi /etc/apt/sources.list
改为(注意要换其他源用http方式可以访问的,要不然还得处理https的内容):
deb http://mirrors.huaweicloud.com/debian buster main
改完以后执行:
apt-get update
使源生效
安装网络相关的库
apt-get install wpasupplicant #安装WIFI配置相关的组件 apt-get install net-tools #安装网络基础组件、如使用ifconfig等 apt-get install udhcpc #当wifi连接成功后,需要用这个组件去获取IP地址
其他组件
apt-get install wireless-tools apt install sudo vim openssh-server htop apt install pciutils usbutils acpi
1.2 配置账号
修改root登录密码的方式如下passwd root
添加用户
groupadd <用户组> useradd -m -g <用户组> -s /bin/bash <用户名> passwd <用户名>
1.3 新增账号sudo配置
对于出现<用户名> is not in the sudoers file. This incident will be reported.
切换到超级用户:
$ su
打开/etc/sudoers文件:
# vi /etc/sudoers
修改文件内容
保存退出
修改主机名,否则将会以当前编译的系统的主机名进行设置(如:笔者为Ubuntu,相当的尴尬)
HOSTNAME=<你的主机名> echo $HOSTNAME > /etc/hostname echo $HOSTNAME > /proc/sys/kernel/hostname sed -i '/localhost/s/$/\t'"$HOSTNAME"'/g' /etc/hosts
1.4 配置时区
修改系统默认时区cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
1.5 配置SSH
如果要使用SSH服务,并且允许SSH客户端以root方式登录需要进行一下设置。vi rootfs/etc/ssh/sshd_config
添加
PermitRootLogin yes
1.6 rootfs打包
当所有的内容制作完成,就可以清理缓存,打包之后就可以替换你的文件系统了apt-cache clean #删除安装包 exit #退出chroot rm rootfs/usr/bin/qemu-arm-static
卸载刚在挂载的文件夹。
cd rootfs sudo umount dev/pts/ sudo umount dev/ sudo umount sys/ sudo umount proc/ sudo umount dev/pts/
打包文件。
# cd rootfs #进到文件系统目录,如果已经在该文件夹下,可以忽略 tar cvf ../rootfs.tar . #要注意那个. 代表当前目录
生成的rootfs.tar任意解压到文件系统即可
2. 使用wpa_supplicant连接wifi
创建配置文件
vi /etc/wpa_supplicant.conf
输入内容:
network={ ssid="我的热点" psk="我的密码" }
然后执行命令
wpa_supplicant -B -d -i wlan0 -c /etc/wpa_supplicant.conf
示例如下
root@dika-pc:~# wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf Successfully initialized wpa_supplicant rfkill: Cannot open RFKILL control device root@dika-pc:~# [ 444.817608] wlan0: authenticate with e4:67:1e:02:33:c0 [ 444.843208] wlan0: send auth to e4:67:1e:02:33:c0 (try 1/3) [ 444.900867] wlan0: authenticated [ 444.935561] wlan0: associate with e4:67:1e:02:33:c0 (try 1/3) [ 444.986874] wlan0: RX AssocResp from e4:67:1e:02:33:c0 (capab=0x411 status=0 aid=4) [ 445.033483] wlan0: associated [ 446.047125] IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready ^C
执行dhcp命名,获取IP地址
root@dika-pc:~# udhcpc -i wlan0 udhcpc: started, v1.30.1 udhcpc: sending discover udhcpc: sending select for 172.16.1.106 udhcpc: lease of 172.16.1.106 obtained, lease time 43200 root@dika-pc:~# ping www.dika.ren PING www.dika.ren (149.129.80.46) 56(84) bytes of data. 64 bytes from 149.129.80.46: icmp_seq=3 ttl=45 time=217 ms 64 bytes from 149.129.80.46: icmp_seq=4 ttl=45 time=417 ms 64 bytes from 149.129.80.46: icmp_seq=5 ttl=45 time=102 ms 64 bytes from 149.129.80.46: icmp_seq=6 ttl=45 time=108 ms ^C --- www.dika.ren ping statistics --- 6 packets transmitted, 4 received, 33.3333% packet loss, time 346ms rtt min/avg/max/mdev = 102.009/211.020/417.367/127.586 ms
3. 增加swap分区
在使用一些软件的过程中,会遇到系统崩溃,尤其是使用 apt-get install 的时候,所以需要加入swap分区,可以简单理解为虚拟内存。
使用
free
查看当前swap大小,
使用如下命令创建你想要添加swap分区的大小dd if=/dev/zero of=/swap1 bs=1M count=512 #改成你要设置的SWAP大小,512就是512MB
设置swap分区文件
mkswap /swap1
激活swap分区
swapon /swap1
此时使用free命令就能看到创建好的swap分区,但这只是临时性的, 重启会失效,需要配置一下,下次开机时候要开swap
vi /etc/fstab
操作,在最后一行添加
/swap1 swap swap defaults 0 0
展示
开始进入Debian使用我们自定义的pi账户登录
使用命令查看Swap分区
以上内容来自 WhyCan Forum哇酷开发者社区
小白自制Linux开发板(F1C200s)整理系列,持续更新中
作者:twzy -
回复: 【资料释放】D1哪吒计算条资源汇总(持续更新)
@jordonwu 编译和烧写的内容都已经在D1的在线文档里公布了 https://d1.docs.aw-ol.com/study/study_4compile/
这部分帖子的内容是从sipeed那边搬运过来的 -
回复: 【资料释放】D1哪吒计算条资源汇总(持续更新)
昨天晚上本来准备睡觉发现D1的核心板居然上架了,立刻上车了一个,那么问题来了,我是想买来点手里的MIPI带鱼屏,看了下资料,官方的核心板很明显是考虑到内嵌设计的,本体只有用户指示灯和SPI屏的接口,倒是底下放了两个M2的金手指来引出IO,MIPI的差分对也在金手指上,想用的话就得自己画个底板了,两个M2金手指的相对间距在外围尺寸图里看不出来,找了找有3D模型,我是在模型里测的,最后糊了个空板,周围有几个朋友一起买的,他们拿去之后转了下格式后丢给我一起备份了,尺寸我看了下应该是没什么问题了,就在这一起放出来吧。
注意:压缩包里一共有三个版本,kicad的是5.99,阿狸狗是17.2,用某d的那个朋友说无所谓啥版本都能兼容。。
空板如图。一边的座子标了那几个电源脚,画板的时候记得别放反了~
PS:铜柱我用的是个小尺寸的,如果自己有更合适的话可以换掉;我打算用是4.2mm高度的B-KEY座子,算了下核心板下面除去背面的器件还有大概1.5mm空隙,可以塞小芯片和比较薄的wifi模块。
文件放在下面了,有需要的小伙伴可以自取~
-
回复: 【资料释放】D1哪吒计算条资源汇总(持续更新)
哪吒计算条(LicheeRV-Nezha Compute Module)位号图
Lichee_RV_7001(TOP-Designator Drawing).pdf
Lichee_RV_7001(Bottom-Designator Drawing).pdf -
回复: 【资料释放】D1哪吒计算条资源汇总(持续更新)
哪吒计算条(LicheeRV-Nezha Compute Module)规格书
中文版:Sipeed Lichee RV 规格书 V1.1.pdf
英文版:Sipeed Lichee RV Datasheet V1.1.pdf -
回复: 【单板仅需99】D1哪吒计算条上线!为智能家居提供高性价比的RISC-V算力
@jordonwu 官方Tina Linux编译烧写文档见:https://d1.docs.aw-ol.com/study/study_4compile/
板子由合作伙伴Sipeed推出,更多相关参数可到Sipeed社区了解 -
回复: 「免费申请」基于安谋科技STAR-C1的全志XR806 IoT开发板试用活动
PS.如果已有XR806开发板,也可以参与发文,极术社区同样有好礼相赠
开发过程中,遇到任何问题,欢迎访问全志在线开发者社区发帖咨询及加入XR806活动交流群,有任何问题也可以直接在微信群里咨询。
-
【小白自制Linux开发板】 二. u-boot移植
上一篇: 【小白自制Linux开发板】 一. 瞎抄原理图与乱画PCB中我们做了一个小型而没用的开发板,用的是Licheepi Nano的镜像,那从本篇开始我们开始自己构建它的灵魂吧。
我们都知道,PC在启动的时候,首先是进入BIOS,再根据BIOS中配置信息引导后续的启动操作系统,比如配置Windows启动。
而对于嵌入式linux中,并没有BIOS,这时候就需要一种类似引导程序来处理。于是就有了BootLoader。
BootLoader是一段小程序,可以把它想象成PC机linux上的GRUB/LILO引导程序,可以直接从flash或TF卡中运行,来装载内核。它可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。
1. 嵌入式开发板的启动过程
一个嵌入式系统从软件角度来看分为三个层次:
-
引导加载程序
包括固化在芯片中的boot程序(可选)和BootLoader两大部分,对于固化的boot程序。主要是芯片通过外围电路连接的实际情况选择读入程序的位置,比如:通过TF卡或是SPI以及其他方式启动,至于优先顺序这就要具体看芯片的数据手册,个人没做过具体测试。 -
linux内核
特定于嵌入式平台的定制内核 -
文件系统
包括了系统命令和应用程序
BootLoader启动过程可分为单阶段和多阶段(stage1、stage2),其中stage1完成初始化硬件,如CPU寄存器、内存控制器,为stage2准备内存空间。一般stage1是可以直接在nor flash中运行的,并将stage2复制到内存RAM中,设置堆栈,然后跳转到stage2(从这也可以看出stage2是在RAM中运行的,与stage1不同)
Boot Parameters 顾名思义,就是配置了要启动内核的参数,包含要加载系统内核相关文件的位置,要加载到内存中的位置,定位到文件系统的位置,相关输入输出的呈现等一系列参数。
kernel 在存放在bootloader之后,对于SoC来说,代码都需要在RAM中运行,这里与MCU不一样的地方就是引入了MMU(内存管理单元)。对于MCU而言,由于其执行速度低,因此运行代码都在ROM中直接运行,而对于Flash而言,其读取速度远不及RAM的速度,因此对于运行速度非常快的SoC而言,所有的代码都需要在RAM中运行。但是这里有一个问题,RAM掉电数据将会丢失,故代码保存不可能放在RAM中,当前所有的嵌入式设备而言,代码保存都是放在ROM中,因此在SoC中运行代码需要将代码搬运到RAM中然后再执行。
Root Filesystem 由于其执行过程需要对ROM进行读写操作,因此可以不用搬运到RAM中,但是实际过程中内核启动后会产生一个虚拟的文件系统,该文件系统是挂在根文件系统的关键所在,这里不详细讲解。整体来说,大致的过程为,嵌入式设备上电后将执行bootloader,对硬件进行硬件和堆栈初始化,然后搬运内核到RAM中并启动内核,紧接着挂载根文件系统。
2. 环境配置与参考项目
系统:Ubuntu 16
编辑器:VSCode
参考项目:Lichee-Pi Nano
地址:https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html
Lichee-Pi Nano
需要注意的是一定要选择Nano版本,因为我们开发板使用的主控芯片和Nano的主控是一致的,所以后续我们要编译U-boot,内核都可以参考(bai piao)这里面的配置。
主控芯片:F1c100s/F1c200s,100s内置32MB DDR1内存,200s内置64MB DDR1内存,200s贵一点,他们都是QFN88封装。
ARM926ejs内核,主频默认408MHz,据了解做产品出货的一般在600M左右。
带有100M的SPI接口2个,SDIO接口1个,USB OTG接口,还有CSI摄像头接口,LCD RGB显示屏接口,音频接口,I2C I2S UART PWM等等。
还有就是他们不支持硬件浮点,所以浮点运算使用软浮点方式。
F1c100s/F1c200s芯片功能3.交叉编译器
我们通过PC版的Linux自带的gcc编译的程序只能在当前系统架构下的cpu架构(x86)下运行,如果我们想要编写的程序在嵌入式Linux下运行,那么就需要用到对应的编译器。
我们做的开发板主控芯片F1C200S,内核为ARM9,其架构使用的是ARMv5架构,所以我们也要选用对应的编译器,同样,这样的编译器很多,这里我们使用最常用的arm-linux-gnueabi- ,因为交叉编译器F1C200S必须高于6.0版本,这里我们使用7.2版本
下载完成后解压文件:
tar -vxjf gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi.tar.xz
然后在/usr/local目录下新建arm-linux-gcc目录
sudo mkdir /usr/local/arm-linux-gcc
进入解压目录下:
cd gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi/
将该目录下的所有文件复制到新建的目录下
sudo cp -rd * /usr/local/arm-linux-gcc/
最后需要添加该工具链的环境变量使其可以在任何目录下执行,打开/etc/profile文件
sudo nano /etc/profile
在文件末尾添加以下内容
PATH=$PATH:/usr/local/arm-linux-gcc/bin
添加完毕,使路径生效
source /etc/profile
接下来在终端输入:
arm-linux-
然后连按两次Tab键,如图在表示成功:
如果没有出现,则进行下面操作,安装必要的动态链接库
sudo apt-get install lib32ncurses5 lib32z1
至此,我们完成了编译工具的配置。
4. 编译U-boot
当Arm开发板上电以后第一个要加载到内存并运行的程序就是BootLoader,BootLoader的同类型程序很多,如U-boot、X-boot、Rt-Thread,这里我们依然选中最常用的U-boot作为目标(因为其他的我也不会呀),
最新版本的uboot几乎包含当前主流的SoC芯片,前面提到本开发板使用的芯片和licheePI nano相同,大部分硬件也是兼容的,为了快速移植该部分,这里采用licheePI nano的u-boot来进行移植。在终端输入如下命令克隆u-boot:
git clone https://github.com/Lichee-Pi/u-boot.git -b nano-v2018.01
克隆完毕文件会保存在当前目录下,进入该目录,
cd u-boot
在该文件夹下有很多分支,我们可以查看所有分支,使用如下命令:
git branch -a
现在我们使用的是nano开发板,所以将当前分支切换到nano分支,命令如下:
git checkout nano-v2018.01u-boot
切换到Nano分支
默认的没有指定交叉工具链和架构,因此在编译之前需要指定交叉工具链和芯片架构,u-boot的交叉编译器在u-boot 的根目录下中的Makefile文件中定义了。打开文件找到CROSS_COMPILE变量,修改为如下:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
配制交叉编译环境
这样我们就能使用我们指定的编译器来编译u-boot了。
在u-boot项目的config目录下存在对多种板子的配置描述文件,由于每个板子的外设不同,因此编译之前必须要对u-boot进行配置。然而配置是一件比较繁琐的事情,特别是像u-boot这种比较复杂的项目而言,初学者几乎无法完成。幸运的是对于大部分开发板而言,config目录下有其配置好的默认配置文件。进入config目录中,然后执行ls查看当前所有的配置文件
cd config ls
查看配制文件
找到licheepi_nano_defconfig 和 licheepi_nano_spiflash_defconfig,前者表示为TF卡启动,后者表示从SPI 设备启动,因为我们做的小板只有从TF卡启动,所以我们需要使用 licheepi_nano_defconfig 。
现在回到上级目录,然后执行
make licheepi_nano_defconfig cd .. make make licheepi_nano_defconfig
这样我们把licheepi_nano_defconfig 作为默认配置项。
接下来我们就可以用图形界面进行配置了,执行
make menuconfig
此时出现图形配置选项,如下图所示
u-boot Menuconfig配制,注意红框中的配置,我们后续要用到。
至此我们的u-boot环境配置就完成了,但是我们还有个问题要解决:如何让u-boot引导系统
我们在PC端安装Windows系统的时候往往需要选择启动顺序,比如需要优先通过光驱或u盘启动等。
同样在u-boot中也需要这样的配置,当然u-boot比PC配置稍微复杂一丢丢。我们前面提到Linux嵌入式系统结构分布中有个Boot Parameters 部分,这部分就是做引导配置的,那怎么配置呢,总体来说可以分为两部分:
-
bootcmd,主要用于描述控制Linux内核文件以及其他描述文件加载到内存中位置以及启动Linux内核系统等
-
bootargs,用于配制文件系统、串口信息等。
bootcmd
在最开始提到过,内核一般不在flash中运行,这样就需要将内核搬运到内存中,这个过程需要u-boot来完成。对于mmc (TF卡)而言,在u-boot有专门的命令load mmc,该命令可以将mmc中的代码从flash搬运到指定的地址处。
当u-boot中环境变量bootdelay计数到0时,此时uboot就会开始执行bootcmd中的命令。
bootdelay这个环境变量是一个计数器,当u-boot主体运行完毕后,此时bootdelay该变量的值将会开始递减,递减时间为1s,当递减到0时,此时u-boot将会跳转到bootcmd处开始执行bootcmd命令,(你可以简单理解为PC启动后有一两秒时间等待,你可以可以通过F8或Enter键打断进入Bios设置的过程,这个等待时间就由u-boot中的bootdelay来控制)。
下面我们需要记住这句指令,这就是我们当前制作的开发板需要用到的bootcmd全部内容
load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;
如果你需要详细了解这句话那就接着往下看,如果不需要则可以跳到 下面的u-boot参数配置环节
对于上面命令,我们根据分号拆分为3部分:
1> load mmc 0:1 0x80008000 zImage; 2> load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb; 3> bootz 0x80008000 - 0x80c08000;
其中两个 load mmc 命令、一个bootz 命令。
先看第一条:
load mmc 0:1 0x80008000 zImage
load mmc有三个参数:第一个参数是mmc(TF卡)分区,第二个参数是内存中目标地址,第三个参数是源文件。
即上面的命令意思是将mmc的0:1 分区中的zImage复制到内存中的0x80008000地址处。这里的zimage就是Linux内核,后续会提到该文件编译,0:1这个可以这样理解0表示TF卡(TF卡属于mmc存储器的一种),1这表示TF卡的第一个分区(boot分区)后面会提到。
而对于内存位置 0x80008000 地址位置,将其理解为默认值就行了。这样完成了zImage的加载。
下面分析第二条命令:
load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb
有了上面的加载zImage的说明,可以很轻松的理解上面的命令意思是将mmc的0:1分区中的suniv-f1c100s-licheepi-nano.dtb文件加载到内存中的0x80c08000地址处。对于suniv-f1c100s-licheepi-nano.dtb 这个文件,叫做设备树文件,简单来说就是当前开发板上面所有外设备描述文件,这部分将会在后续内核编译部分进行详细说明。
对于第三条命令:
bootz 0x80008000 - 0x80c08000
的意思是告诉内核镜像的起始地址为0x80008000,加载的设备树地址为0x80c08000。这里是告诉cpu从这里开始启动Linux, bootz命令的格式是:bootz空格0x80008000空格-空格0x80c08000,注意-左右有空格。
除了bootz 命令外,有些系统里面还可能存在一个叫做bootm命令,这是是对没有使用设备树内核的镜像启动命令,早期版本的内核没有引入设备树,因此对于早期的内核一般使用的是bootm,其命令格式为bootm内核地址,比如bootm x0x30008000,意思是从0x30008000开始启动内核,启动内核的过程其实是将pc指针指向该地址,这样处理器就会从该地址处运行代码。
这里我们就完成了bootcmd的说明,接下来我们看另外一个参数。
bootargs
bootargs也是u-boot环境变量中一个非常重要的变量,上面已经讲解了内核的启动可以通过bootcmd来完成,那接下来内核启动完毕后必须挂在根文件系统(rootfs)。但是内核并不知道根文件系统的具体位置,我们必须要告诉根文件的位置后内核才能将其挂载,这时就需要有bootargs变量。该变量的作用是告诉内核根文件系统的位置和属性以及必要的配置,
console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw
同上面分析的方法一样,我们依然将这部分命令拆成几部分来说明。这里需要说明的是,这部分配置信息是由u-boot 直接按照参数字符串方式提供给Linux内核,然后由Linux内核进行执行的,这也说明里为什么格式与bootcmd配置方式不一致。**console=ttyS0,115200 **表示终端为ttyS0即串口0,波特率为115200;
panic=5 字面意思是恐慌,即linux内核恐慌,其实就是linux不知道怎么执行了,此时内核就需要做一些相关的处理,这里的5表示超时时间,当Linux卡住5秒后仍未成功就会执行Linux恐慌异常的一些操作。
rootwait 该参数是告诉内核挂在文件系统之前需要先加载相关驱动,这样做的目的是防止因mmc驱动还未加载就开始挂载驱动而导致文件系统挂载失败,所以一般bootargs中都要加上这个参数。
root=/dev/mmcblk0p2 表示根文件系统的位置在mmc的0:2分区处,/dev是设备文件夹,内核在加载mmc中的时候就会在根文件系统中生成mmcblk0p2设备文件,这个设备文件其实就是mmc的0:2分区(这里对应TF卡的第二个分区:rootfs),这样内核对文件系统的读写操作方式本质上就是读写/dev/mmcblk0p2该设备文件。
earlyprintk 参数是指在内核加载的过程中打印输出信息,这样内核在加载的时候终端就会输出相应的启动信息。rw表示文件系统的操作属性,此处rw表示可读可写。
5.u-boot参数配置
make menuconfig
选中 Enable boot arguments 按空格选中,下面会显示:() Boot arguments
然后选中Boot arguments ,按回车,进入配置窗口,接下来上面解释过的bootargs 参数信息:
console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw
配置bootargs信息
然后按Tab键选中<OK>,保存并进入主菜单。
同理配置:Enable a default value for bootcmd 按空格选中,下面会显示:() bootcmd value 配置项,
选中bootcmd value 进入配置界面,输入bootcmd命令:
load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;
配置bootcmd参数
按Tab键选中<OK>,保存并进入主菜单。
6.u-boot编译与烧录
先保存图形配置界面后推出界面,在终端执行make -j4即可对整个u-boot进行编译。
编译u-boot
make -j4后面的-j4表示4个核心进行编译,若电脑的处理器是2核心,请使用make -j2进行编译。
编译完成后会在当前目录生成u-boot-sunxi-with-spl.bin烧录文件。
根目录下找到 u-boot-sunxi-with-spl.bin 文件
该文件就是我们最终要烧录的二进制文件。
在当前目录下会有一个隐藏的文件.config,该文件是u-boot编译后根据各个选项产生的配置文件,这个配置文件记录了所有配置选项的宏开关,编译的时候是根据最终的.config文件来进行编译的,当然编译前是需要有脚本解析.config文件然后进行相应的编译。
烧录到TF卡
只要将u-boot-sunxi-with-spl.bin烧录到tf卡的8k偏移处地址就可以了,烧录步骤如下:使用dd命令进行块搬移:
sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8
该命令中:
- if 文件名:输入文件名,缺省为标准输入。即指定源文件。< if=input file >
- of 文件名:输出文件名,缺省为标准输出。即指定目的文件。< of=output file >
- bs bytes:同时设置读入/输出的块大小为bytes个字节。
- seek blocks:从输出文件开头跳过blocks个块后再开始复制。
这里的输出文件(of)为主机电脑的/dev/sdb文件,也就是TF卡,这里也体现了Linux一切皆文件的思想。
/dev/sdb 这个可以用gparted 软件查看,该软件可以直接用命令安装即可:
sudo apt-get install gparted
此时在Ubuntu下面可以看到如下软件:
安装好GParted软件
打开软件
GParted
在右上角可以看到两个硬盘,/dev/sda 为本地硬盘,/dev/sdb 是我们将要写数据的TF(当然这只是墨云自己的配置使然,具体情况请根据实际情况而定),因此这里的of=/dev/sdb 烧录到8k偏移地址处是指绝对地址,这个绝对地址指的是TF卡的物理地址。这8K的值是由F1C200S 中固化的启动代码决定的,所以照抄即可。
烧写u-boot
然后我们正常退出TF卡,然后插入我们自制的开发板,通过USB线连接电脑,
连接开发板
打开电脑中的命令行工具,我这里使用Xshell,
打开Xshell,新建连接:
配置名称 ,协议选择Serial
配置串口
通过下拉选中com端口,波特率为115200,其他默认即可,点击确定,然后双击主界面左侧会话管理中的刚建立的会话,此时进入连接状态。
因为在你插入USB通电的时候开发板就已经启动了,所以当你打开串口连接的时候可能未必会看到信息,所以按一下重启键,就可以看到如下的输出信息了,这就是我们的u-boot,执行到u-bbot计数完成后会产生错误,那是因为我们还没有进行系统内核的移植,所以默认就会进入u-boot命令模式。
启动信息
输入pri命令打印环境变量的所有值,可以找到已经配置的bootcmd 和bootargs
pri命令结果
至此完成了u-boot移植的全部内容,对于u-boot的移植方法,在后续移植Linux内核和文件系统时都会用到,都是大同小异的,所以有了本篇的说明,之后操作将会非常简单。
而关于u-boot的内容事实上非常的复杂繁琐,有兴趣的可以自行去了解到,毕竟作为一个小白的我初衷只是先让小板先跑起来
参考资料
Lite200 (lishanwen) -- https://lishanwen.cn/index.php/2021/07/03/lite200/全志F1C200S F1C100S 介绍 ( 迪卡魏曼依奇君 ) https://blog.csdn.net/tunqimai9331/article/details/95938903
荔枝派Nano 全流程指南 (矽速科技) https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html
以上内容来自 WhyCan Forum哇酷开发者社区
小白自制Linux开发板(F1C200s)整理系列,持续更新中
作者:twzy -
-
【FAQ】全志R329Tina下无法选中libcedarx如何解决?
问题背景
Tina SDK用户反馈,对于特定方案(v833),在make menuconfig时,可以搜索到PACKAGE_libcedarx,但无法选择。
如上图所示,可以搜索到PACKAGE_libcedarx,但是没有显示其目录路径,同时也没有显示是否选中。
问题分析
在Tina中, 使用 make 编译 tina 下的任何目标,都会通过 build/scan.mk 生成必要的临时文件,scan.mk 会扫描tina package、target等目录下的文件信息,并将对应的扫描结果保存在 tmp 目录,这是tina下所有目标生成的前提。
对于本例来说,PACKAGE_libcedarx对应目录package/allwinner/tina_multimedia,经过扫描之后,会生成临时文件tmp/info/.packageinfo-allwinner_tina_multimedia,此时,该文件的内容如下:
$ cat tmp/info/.packageinfo-allwinner_tina_multimedia Source-Makefile: package/allwinner/tina_multimedia/Makefile
现在,我们打开一个menuconfig可以选中的pacakge,比如tplayerdemo,其对应的临时文件tmp/info/.packageinfo-allwinner_tina_multimedia_demo_tplayerdemo的内容如下:
$ cat tmp/info/.packageinfo-allwinner_tina_multimedia_demo_tplayerdemo Source-Makefile: package/allwinner/tina_multimedia_demo/tplayerdemo/Makefile Package: tplayerdemo Submenu: tina_multimedia_demo Version: 1-1 Depends: +libc +SSP_SUPPORT:libssp +USE_GLIBC:librt +USE_GLIBC:libpthread @TPLAYER libcedarx libstdcpp Conflicts: Menu-Depends: Provides: Build-Depends: libcedarx Section: utils Category: Allwinner Title: use tplayer interface in tina_multimedia Maintainer: Source: Type: ipkg Description: CedarX2.8 tplayerdemo @@
对比两者,可发现libcedarx的临时文件只有一行,缺少很多信息,如Package、Submenu、Depends等,而Package、Submenu、Depends信息都是对应package下的Makefile定义。
对package/allwinner/tina_multimedia/Makefile进行检查,发现上述定义都被下列条件语句所限制。
feq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113))
显然,这里表示的意思是,只有这些方案,才会定义libcedarx所需的目标。
问题解决
对于本例(v833)来说,补丁如下。
diff --git a/allwinner/tina_multimedia/Makefile b/allwinner/tina_multimedia/Makefile index 8f3f263af..28cc01eb2 100755 --- a/allwinner/tina_multimedia/Makefile +++ b/allwinner/tina_multimedia/Makefile @@ -435,7 +435,7 @@ ifeq ($(CONFIG_ONLY_DISABLE_AUDIO),y) CONF_ONLY_DISABLE_AUDIO = -DONLY_DISABLE_AUDIO endif -ifeq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113)) +ifeq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113 v833)) define Package/$(PKG_NAME)/config source "$(SOURCE)/Config.in" @@ -765,6 +765,6 @@ endef endif -ifeq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113)) +ifeq ($(TARGET_BOARD_PLATFORM),$(filter $(TARGET_BOARD_PLATFORM),r16 r58 r40 r18 r6 c200s g102 r11 r7 r30 r311 r333 r331 r7s t7 r332 v306 dolphin h3 h6 mr133 r328s2 r328s3 mr813 r329 r818 a33i r528 r528rv d1 f133 t113 v833)) $(eval $(call BuildPackage,$(PKG_NAME))) endif
-
【FAQ】全志R329Tina中TA下有哪些加解密接口?
问题背景
客户希望在安全OS端实现如下功能:
- RSA私钥的生成与存储;
- 公钥的提取(指定私钥,提取对应的公钥)
- 指定信息的签名;对非安全OS传入的信息进行签名后,返回签名结果;
- 信息的加密、解密;对传入的信息进行加密、解密等操作,并返回结果;
想咨询一下当前Tina optee是否支持openssl库,如果不支持openssl库,如何实现上述功能。
问题分析
首先optee不支持openssl库,但是有其他替代方案。
GlobalPlatform API
OPTEE实现了《GPD_TEE_Internal_Core_API_Specification》这一套API,提供了对称加密、非对称加密、签名、提取密钥等操作。
mbedtls库
mbedtls是为嵌入式设备而开发的一个TLS协议的轻量级实现,虽然是为嵌入式设备而开发,但它也能被用于其他各种平台,因此也常常被用作OpenSSL的一个轻量级替代。Tina SDK中optee-3.7.0中已经支持。
解决办法
GlobalPlatform API
密钥生成API:
TEE_GenerateKey密钥提取API:
TEE_GetObjectBufferAttribute签名验证API:
TEE_AsymmetricSignDigest
TEE_AsymmetricVerifyDigest加解密API:
TEE_CipherInit
TEE_CipherUpdate
TEE_CipherDoFinal
TEE_AsymmetricEncrypt
TEE_AsymmetricDecrypt可参考Linaro Security Working Group提供的例子,https://github.com/linaro-swg/optee_examples
mbedtls库
mbedtls算是比较常用的库,可参考官方API文档:https://tls.mbed.org/api/本文提供了一个rsa例子,如附件0003-optee-add-optee-mbedtls-rsa-demo.patch
0003-optee-add-optee-mbedtls-rsa-demo.patch -
【FAQ】全志R329 Tina下为何有些optee的demo只有NA源码没有TA源码?
问题描述
客户发现Tina SDK下有些optee demo 没有TA,提出问题:
为什么有的sample只有 NA的源码,比如 optee-keybox 和 optee-getdmkey,对应 TA的程序在哪里?
问题分析
高版本optee(对Tina,optee-3.7.0支持)新增了PTA(Pseudo Trusted Application)的支持。
PTA作用与TA类似,但是PTA不是一个TA,是一个接口,可以被TA与NA调用。
PTA直接在optee os中源码实现,直接编译到optee.bin中,运行也与optee_os是同一个特权等级。
简单来说,就是把原本在安全世界应用层实现的功能放到了操作系统层。
解决办法
一般来说TA是由客户自行开发,但是全志基于optee实现一些特有功能,这部分以PTA的形式集成到optee.bin中,暂未开源。
目前仅有optee-keybox与optee-getdmkey两个包是这样。
客户如需开发与keybox与dm-crypt相关的NA,可以参考上述两个包,直接调用相关命令,与正常的NA开发没有什么不同,TA则不用开发。
-
【FAQ】全志R329Tina下几种安全存储实现有何区别
问题描述
客户在《TinaLinux_安全开发指南》上看到有多种安全储存实现,想详细了解他们的区别,进行选择。
问题解答
Tina上目前支持三种安全存储,分别是keybox、optee secure storage与dm-crypt。
keybox
keybox是全志实现的一种安全存储方法,keybox使用flash区域是特意预留的,该区域未映射到逻辑扇区,通常的数据操作无法访问,具有量产不擦除特性。
keybox上保存的数据都是通过optee中进行加解密的。
注:
- 只有使用nand与emmc介质的方案才支持keybox,nor介质的方案不支持。
- keybox最多支持31个key烧写,每个key最大支持3KB数据。设计之初主要目的是存放密钥、证书等小数据对象。
- 可以通过DragonSN与控制台命令方式,烧写数据到keybox上。
optee secure storage
OPTEE Secure Storage是optee提供的根据GP TEE Internal API规范实现的一种安全存储技术。它将数据发送到Secure OS进行加密,然后保存到Linux端的文件系统(/data/tee)或者RPMB中。
注:
- 保存的数据大小没有限制,最大可以为optee os的堆大小。optee官方提到设计的目的主要是用于存放密钥、hash值以及计数器等小数据对象。
- 客户需要开发相关的NA与TA来使用optee secure storage功能,全志有提供一个参考demo,位于package/security/optee-secure-storage目录。
dm-crypt
dm-crypt 是Linux内核提供的一种基于加密API框架和设备映射器(device mapper)子系统的安全存储技术。dm-crypt是对整个文件系统进行加密,在初始化好后,可以直接读写该文件系统,内核自动对数据进行加解密。
注:
- dm-crypt的密钥需要保存在安全域,在初始化时,从安全域读取,配置给Linux内核。
- 从flash上看,该分区数据是经过加密的,但是Linux端可以直接明文访问。
- 由于是一个文件系统,所以这种方式可以保存大数据对象,用户数据分区常采用这种安全机制。