×

智能手机加速度计内部:带有NuttX RTOS的PinePhone

消耗积分:0 | 格式:zip | 大小:0.00 MB | 2023-06-12

分享资料个

描述

当我们将智能手机从纵向倾斜到横向时……它如何知道我们正在倾斜手机?

那是因为我们智能手机中的加速度计为了解其工作原理,让我们使用Apache NuttX RTOS(实时操作系统)从PINE64 PinePhone中窥探原始加速度计数据。

构建和启动 NuttX RTOS

按照以下步骤使用 microSD 卡在 PinePhone 上启动 NuttX RTOS(它不会触及内部 eMMC 存储)...

(1) 安装构建先决条件

(2) 下载适用于AArch64 裸机目标的 ARM64 工具链:(跳过测试版部分)aarch64-none-elf

(3) 下载并构建NuttX RTOS for PinePhone ...

git clone https://github.com/apache/nuttx nuttx

git clone https://github.com/apache/nuttx-apps apps

cd nuttx

tools/configure.sh pinephone:sensor

make

cp nuttx.bin Image

rm -f Image.gz

gzip Image

(4) 这会生成 NuttX 图像文件Image.gz我们一会儿将它复制到 PinePhone 中。

(5) 下载PinePhone Jumpdrive镜像:pine64-pinephone.img.xz

(6) 使用Balena EtcherGNOME Disks将下载的镜像写入 microSD 卡

(7) 还记得Image.gz之前的事吗?复制并覆盖 microSD 卡上的文件。

(8) 将 microSD 卡插入 PinePhone 并将隐私开关 6(耳机)拨至关闭。

(9) 将PinePhone Serial Console Cable连接到我们的电脑并给PinePhone 上电。

(10) 在我们的电脑上,使用screen或以115.2 kbps 的速度putty连接到 PinePhone 串口控制台(假设“usbserial-1410”是 USB 串口)...

screen /dev/tty.usbserial-1410 115200

(11) 在提示符下,输入:ls /dev

NuttShell (NSH) NuttX-12.0.3
nsh> ls /dev
/dev:
console
imu0
null
ram0
ram2
ttyS0
userleds
zero

NuttX 表示,PinePhone 加速度计InvenSense MPU-6050现在可以在. (那是惯性测量单元/dev/imu0

获取加速度计数据

让我们从 PinePhone 的加速度计中读取原始加速度计数据。

将我们的 PinePhone 竖直放在纵向输入这个命令...

hexdump /dev/imu0 count=14

这会从加速度计转储 14 个字节的原始数据...

/dev/imu0 at 00000000:
0000: 10 21 00 05 01 6a f7 9e ff d8 00 13 ff fd

现在将我们的 PinePhone 逆时针旋转 90 度,进入横向输入相同的命令...

hexdump /dev/imu0 count=14

我们将看到一组不同的 14 字节原始数据......

/dev/imu0 at 00000000:
0000: 00 19 f0 48 01 12 f8 80 ff d6 00 0f ff fe

但是这些数字是什么意思?

解码加速度计数据

NuttX 内核将MPU-6050 加速度计数据格式定义为...

/* MPU-6050 Accelerometer Data Format
 * (14 bytes, big-endian) */
struct sensor_data_s {
  int16_t x_accel;  /* Accelerometer X */
  int16_t y_accel;  /* Accelerometer Y */
  int16_t z_accel;  /* Accelerometer Z */
  int16_t temp;     /* Temperature */
  int16_t x_gyro;   /* Gyroscope X */
  int16_t y_gyro;   /* Gyroscope Y */
  int16_t z_gyro;   /* Gyroscope Z */
};

(来源)

当我们从上面解码数据时,我们得到......

纵向:

  • 加速度计 X = 4129 (0x1021) ⬆️
  • 加速度计 Y = 5 (0x0005)
  • 加速度计 Z = 362 (0x016A)
  • 温度 = -2146 (0xF79E)
  • 陀螺仪 X = -40 (0xFFD8)
  • 陀螺仪 Y = 19 (0x0013)
  • 陀螺仪 Z = -3 (0xFFFD)

横向:

  • 加速度计 X = 25 (0x0019)
  • 加速度计 Y = -4024 (0xF048) ⬇️
  • 加速度计 Z = 274 (0x0112)
  • 温度 = -1920 (0xF880)
  • 陀螺仪 X = -42 (0xFFD6)
  • 陀螺仪 Y = 15 (0x000F)
  • 陀螺仪 Z = -2 (0xFFFE)

啊哈,加速度计的 X 和 Y 值发生了显着变化!

是的,加速度计测量三轴的加速度......

  • X轴:指向PinePhone 的顶部边缘
  • Y轴:指向PinePhone 的左边缘
  • Z轴:指向PinePhone后盖

加速度计检测到加速度发生变化:在我们旋转手机后,从 X 轴到 Y 轴。

但是我们没有水平或垂直“加速”手机?

这是因为重力地球表面对我们的手机施加向上的法向力。这是在我们旋转手机时由加速度计测量的。

当我们将手机从纵向倾斜到横向时,我们的手机就是这样知道的!

读取加速度计

最后,让我们编写一个程序来读取和解释加速度计数据。

我们首先声明MPU-6050 加速度计数据格式hello_main.c

/* MPU-6050 Accelerometer Data Format
 * (14 bytes, big-endian)
 * Based on NuttX Kernel: https://github.com/apache/nuttx/blob/master/drivers/sensors/mpu60x0.c#L218-L233 */
struct sensor_data_s
{
  int16_t x_accel;  /* Accelerometer X */
  int16_t y_accel;  /* Accelerometer Y */
  int16_t z_accel;  /* Accelerometer Z */
  int16_t temp;     /* Temperature */
  int16_t x_gyro;   /* Gyroscope X */
  int16_t y_gyro;   /* Gyroscope Y */
  int16_t z_gyro;   /* Gyroscope Z */
};

接下来我们打开加速度计.../dev/imu0

/* Open the MPU-6050 Accelerometer for reading */
int fd = open("/dev/imu0", O_RDONLY);
assert(fd > 0);  /* Check that it exists */

我们从加速度计中读取了14 个字节的数据……

/* Accelerometer Data will have 14 bytes */
struct sensor_data_s data;
assert(sizeof(data) == 14);  /* We expect to read 14 bytes */

/* Read the Accelerometer Data (14 bytes) */
int bytes_read = read(fd, &data, sizeof(data));
assert(bytes_read == sizeof(data));  /* We expect 14 bytes read */

但是加速度计数据是Big-Endian Format我们为 PinePhone 转换为Little-Endian 格式...

/* Flip the bytes from Big-Endian to Little-Endian for PinePhone.
 * ntohs() is explained here: https://developer.ibm.com/articles/au-endianc/ */
int16_t x_accel = ntohs(data.x_accel);
int16_t y_accel = ntohs(data.y_accel);
int16_t z_accel = ntohs(data.z_accel);
int16_t temp    = ntohs(data.temp);
int16_t x_gyro  = ntohs(data.x_gyro);
int16_t y_gyro  = ntohs(data.y_gyro);
int16_t z_gyro  = ntohs(data.z_gyro);

最后我们打印转换后的数据并关闭加速度计......

/* Print the Accelerometer Data */
printf("Accelerometer X: %d\n", x_accel);
printf("Accelerometer Y: %d\n", y_accel);
printf("Accelerometer Z: %d\n", z_accel);
printf("Temperature:     %d\n", temp);
printf("Gyroscope X:     %d\n", x_gyro);
printf("Gyroscope Y:     %d\n", y_gyro);
printf("Gyroscope Z:     %d\n", z_gyro);

/* Close the Accelerometer */
close(fd);

要在 PinePhone 上运行它,请在我们的计算机上查找此源文件...

apps/examples/hello/hello_main.c

通过以下内容覆盖该文件的内容:hello_main.c

为 PinePhone 重建 NuttX RTOS...

make

cp nuttx.bin Image

rm -f Image.gz

gzip Image

按照前面的说明复制到 PinePhone microSD 卡。在 PinePhone 上启动 microSD 卡并输入:Image.gzhello

当 PinePhone 处于Portrait Orientation时,我们会看到类似...

nsh> hello
Accelerometer X: 4137
Accelerometer Y: -97
Accelerometer Z: 208
Temperature: -1500
Gyroscope X: -29
Gyroscope Y: 66
Gyroscope Z: 12

当我们将 PinePhone 旋转到Landscape Orientation时,Accelerometer X 和 Y 值会发生显着变化......

nsh> hello
Accelerometer X: -143
Accelerometer Y: -4007
Accelerometer Z: -443

是的,我们的程序已经成功读取了 PinePhone 加速度计!

(非常感谢Filipe Cavalcanti启发了本教程!)

进一步阅读

要了解更多关于PinePhone 的 NuttX RTOS ...

MPU-6050 在微控制器上也能正常工作……

MPU-6050实际上是一个组合加速度计和陀螺仪...

我们可以计算倾斜角吗它在 3D 空间中变得复杂,请查看三角函数公式...

附录:MPU-6050驱动

MPU-6050 Driver是如何在NuttX Kernel中启动的?

启动时,NuttX Kernel在PinePhone Bringup Function pinephone_bringup中启动MPU-6050 Driver ,并在/dev/imu0注册驱动pinephone_bringup.c

/* To register the IMU Driver for MPU-6050 Accelerometer at Port TWI1... */

/* Init the Power Management Integrated Circuit */
ret = pinephone_pmic_init();

/* Wait 15 milliseconds for power supply and power-on init */
up_mdelay(15);

/* Allocate the IMU Driver struct */
mpu_config = kmm_zalloc(sizeof(struct mpu_config_s));
mpu_config->i2c  = i2c1;
mpu_config->addr = 0x68;

/* Register the IMU Driver at /dev/imu0 */
mpu60x0_register("/dev/imu0", mpu_config);

mpu60x0_register在 MPU-6050 驱动中定义)

( pinephone_pmic_init来自PinePhone电源管理集成电路的驱动)

什么i2c1

i2c1是 Allwinner A64 SoC 上的第二个 I2C 端口,连接到 MPU-6050 加速度计。PinePhone 原理图,第 12 页)

Allwinner A64 上的 I2C 端口被命名为 TWI0,TWI1,... 所以i2c1实际上是Allwinner A64 上的端口 TWI1 : pinephone_bringup.c

/* Initialize TWI1 as I2C Bus 1 */
i2c1 = a64_i2cbus_initialize(i2c1_bus);

/* Register I2C Driver for I2C Bus 1 at TWI1 */
ret = i2c_register(i2c1, i2c1_bus);

TWI表示“双线接口”)

a64_i2cbus_initialize定义在 Allwinner A64 I2C Driver 中)

这是什么 PMIC?

启动时,NuttX Kernel 会初始化 PinePhone 的电源管理集成电路 (PMIC) ,然后再启动 MPU-6050 Driver: pinephone_bringup.c

/* To register the IMU Driver for MPU-6050 Accelerometer at Port TWI1... */

/* Init the Power Management Integrated Circuit */
ret = pinephone_pmic_init();

/* Omitted: Start the IMU Driver at /dev/imu0 */

pinephone_pmic_init来自 PMIC 驱动程序)

这是因为 MPU-6050 加速度计由 PinePhone 的 PMIC:X-Powers AXP803 供电PinePhone 原理图,第 3 页,DLDO1)

X-Powers AXP803 上的DLDO1 电源PinePhone 上的 I2C 传感器供电,包括 MPU-6050 加速度计。

(更多关于 DLDO1)

附录:MPU-6050 介绍

我们如何确定 X 轴指向 PinePhone 的顶部边缘?Y 轴指向左边缘?

根据PinePhone Component List, MPU-6050 Accelerometer 的标签为U1202

PinePhone 主板底部布局来看, U1202 在图的左上角。这意味着加速度计位于PinePhone 的左上角(从正面看时)

U1202 有一个小圆圈,朝向PinePhone 的左下边缘。

当我们参考InvenSense MPU-6050 数据表(第 21 页)时,我们得出结论...

  • X轴:指向PinePhone 的顶部边缘
  • Y轴:指向PinePhone 的左边缘
  • Z轴:指向PinePhone后盖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'智能手机加速度计内部:带有NuttX RTOS的PinePhone',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);