最近刷bilibili发现微雪电子关于luckyfox pico的介绍视频,感叹linux开发板居然可以把价格缩到100RMB以内,也正巧结束了复旦微比赛,受够了FM33LC046N
的低性能,来玩点便宜又高性能的板子。
开发板型号:luckfox pico max
开发环境:Ubuntu 22.04
参考:luckyfox pico官方WIKI、微雪bilibili视频
我的luckfox-pico的github仓库在这里,这边简单记录一下git的使用教程。
首先在github官网新建一个repository,
然后git clone注意用ssh的,因为http的话虚拟机代理会有问题,经常连不上,但是使用ssh需要添加一个ssh key,否则会clone失败,教程在这里[git初始化与ssh秘钥生成]
添加完毕秘钥之后我们就可以git clone,git add, git commit, git push愉快4连了。
开发环境的搭建主要是根据官方WIKI的上手教程来进行,
因为max是新出的,这里暂时只有标准版和plus的教程,我们按照plus的来就可以。虽然SPI NAND FLASH中已经有出厂镜像了,但是实测发现出厂烧录的镜像可能有问题,因此需要手动进行镜像烧录。
这里踩了一些坑,出厂自带的sys/class/pwm下是空的,于是我打算要要么再烧录一遍官方的文件,要么自己编译。由于烧录官方文件更加省时,所以优先考虑。
当然,我其实也尝试编译过镜像,并浅浅记一下踩坑.
【问题1】编译镜像的过程中出现了python找不到的情况,为其添加了软连接.
【问题2】编译完毕需要通过共享文件夹传输文件,发现共享文件系统又坏了
这篇文章修好了vmware的共享文件夹问题
【问题3】编译完的镜像传输到windows平台下会导致软链接失效,需要手动重命名一下。
首先,下载官方的镜像和烧录工具,如下图所示
按照官方教程来就可以了
【问题】下载过程中可能因为连接问题导致固件下载失败,反复尝试多次下载即可。
我们直接接上type-c USB进行ADB连接。WIKI中已经介绍过windows系统下ADB的使用了,因为开发都是在linux中的,所以这里直接介绍linux下的adb连接。
初学者可以按照教程用他给的镜像,但是这里我们头铁,我们要学会嵌入式的通用开发方式。打开我的虚拟机VMware
,系统是ubuntu22.04
,这里不一样都没关系,下面我们开始!
安装相关依赖
sudo apt-get install repo git ssh make gcc gcc-multilib g++-multilib module-assistant expect g++ gawk texinfo libssl-dev bison flex fakeroot cmake unzip gperf autoconf device-tree-compiler libncurses5-dev pkg-config
安装adb工具:
sudo apt install adb
克隆官方git
git clone https://github.com/LuckfoxTECH/luckfox-pico.git
激活交叉工具链环境变量,第一次执行如果报错,再重新执行一遍source即可
cd tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/
source env_install_toolchain.sh
激活之后我们就可以写一个简单的C程序,
#include
int main(){
printf("hello world\n");
return 0;
}
对其进行编译
arm-rockchip830-linux-uclibcgnueabihf-gcc aaa.c aaa.o
编译好后的文件我们使用adb发送到开发板上,首先输入
adb connect 172.32.0.93
完成adb连接,这里需要注意,在windows下adb工具可以不指定端口号,这里需要指定端口号,这和具体的adb工具有关。
除此之外,需要注意启动后过一阵子再连接,这和树莓派是一个道理,机子需要启动时间,否则会连不上。
然后登陆到开发板。
接着另外启动一个窗口,通过adb把交叉编译好的文件发送过去,语法如下
然后我们再查看开发板上的文件,发现多了一个aaa.o,并运行它
注意:从知乎老哥那里学来的,传来的可执行文件要chmod 777 ./yourfile,这样添加权限避免一些隐形故障
为了方便开发,我添加了这些alias,因为没有系统学过shell,所以这个搞了我一晚上,比较笨哈哈,将这个存成bootenv在根目录下,source bootenv就可以直接使用里面的alias了,比如打开luckfox就可以直接用luckfox_openshell
命令了,多方便!
alias g='gvim'
export LUCKFOX_IP=172.32.0.93:5555
alias luckfox_connect='adb connect $LUCKFOX_IP'
alias luckfox_openshell='adb -s $LUCKFOX_IP shell'
alias luckfox_push='push_func'
push_func(){
adb -s $LUCKFOX_IP push $1 /
}
使用gpio之前需要提一下gpio的编号,因为在开发板的linux系统中,gpio通过编号进行索引
GPIO有5个bank,每个bank32个pin;
每个bank又可以分为ABCD四组,每组8个pin
因此,对于GPIO1_C7_d,编号=1*32+8*2+7=55. 后面那个小d是后缀
根据引脚示意图,我们可以查到gpio55的位置,我们接上LED灯和电阻
我们首先尝试使用在shell内直接更改GPIO设备文件的方法
导出gpio55到用户空间
echo 55 > /sys/class/gpio/export
读取 GPIO1_C7_d 引脚电平
echo 55 > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio55/direction
cat /sys/class/gpio/gpio55/value
输出 GPIO1_C7_d 引脚电平,可以观测到led的变化
echo out > /sys/class/gpio/gpio55/direction
echo 1 > /sys/class/gpio/gpio55/value
echo 0 > /sys/class/gpio/gpio55/value
当然,更常用的办法是使用C语言通过系统调用的方式修改设备文件,实现对GPIO的控制。
#include
#include
#include
int main(){
int gpio_pin;
printf("Enter GPIO pin number:");
scanf("%d",&gpio_pin);
//export gpio to user
FILE *export_file = fopen("/sys/class/gpio/export","w");
if(export_file == NULL){
perror("Failed to open GPIO export file");
return -1;
}
fprintf(export_file,"%d",gpio_pin);
fclose(export_file);
//get the direction_path
char direction_path[50];
snprintf(direction_path,sizeof(direction_path),"sys/class/gpio/gpio%d/direction",gpio_pin);//get the format char
//specify the pin direction
FILE *direction_file = fopen(direction_path,"w");
if (direction_file == NULL){
perror("Failed to open GPUI direction file");
return -1;
}
fprintf(direction_file,"out");
fclose(direction_file);
char value_path[50];
snprintf(value_path,sizeof(value_path),"sys/class/gpio/gpio%d/value",gpio_pin);
FILE *value_file = fopen(value_path,"w");
if(value_file == NULL){
perror("Failed to open GPIO value file");
return -1;
}
for(int i=0; i<300; i++){
fprintf(value_file,"1");
fflush(value_file);
sleep(1);
fprintf(value_file,"0");
fflush(value_file);
sleep(1);
}
fclose(value_file);
return 0;
}
使用交叉编译工具链进行编译,然后通过adb传输到开发板上运行即可,过程跟上一节一样这里就不赘述了,可以观察到LED的闪烁。
用到的重要的函数需要解释一下:
fflush(File_ptr)//这个函数用来刷新缓冲区
snprintf()//这个函数用来把带格式控制的字符串转变成普通字符串
这里也是按照教程的方法进行操作就可以了,不再过多赘述,用示波器可以抓到波形
本来买这个rockchipRV1106是看中它性能更高更好地驱动FOC的,但是一来Linux系统不是实时系统,二来虽然我观察源码也暂时不能知道如何调节中央对齐PWM输出,我也不好修改设备树和源码,这样太麻烦。如果使用裸机编程的话,没有寄存器和详细的datasheet,只能根据linux kernel源码来判断地址寄存器功能。所以用RV1106直接驱动FOC PWM的想法暂时搁置。但是看源码中确实是有PWM中央对齐的选项的,应当有机会修改源码。
因此,对于FOC控制,我准备把计算部分放在rockchip上,下级MCU(例如复旦微开发板)上仅仅执行简单的定时、uart中断和pwm更新输出,两者用Uart通讯
主要就是配置串口波特率,然后给/dev/ttyS3 echo内容就能通讯了。参考WIKI上的教程即可
继续照抄wiki参考即可,但需要注意,wiki中scanf的书写有误,需要改一下
照抄WIKI即可
血的教训,我把GND和VCC接错了,导致AS5600烧了,幸好我AS5600多嘻嘻,并且RV1106没被烧,万幸。
有关AS5600的I2C协议和时序图介绍见这篇
将luckfox-pico的I2C 3接口接到AS5600或者其他设备上,并输入
i2cdetect -y -a 3
发现可以detect到0x36地址的设备AS5600
有关i2c tool的使用可以见这篇
这里是参考官方WIKI和对AS5600的特性自己写的
#include
#include
#include
#include
#include
#include
#include
#define I2C_DEVICE_PATH "/dev/i2c-3"
#define AS5600_ADDR 0x36
#define LOW_DATA_ADDR 0x0D
#define HIGH_DATA_ADDR 0x0C
#define _2PI 6.28318530718
int main() {
uint8_t data = 0x00;
uint8_t * data_p=&data;
uint8_t result =0x00;
uint16_t result16=0x0000;
int result16_int=0;
uint8_t * result_p = &result;
const char *i2c_device = I2C_DEVICE_PATH;
int i2c_file;
float rad;
if ((i2c_file = open(i2c_device, O_RDWR)) < 0) {
perror("Failed to open I2C device");
return -1;
}
ioctl(i2c_file, I2C_TENBIT, 0);
ioctl(i2c_file, I2C_RETRIES, 5);//retry times
ioctl(i2c_file,I2C_SLAVE,AS5600_ADDR);
while(1){
data = LOW_DATA_ADDR;
write(i2c_file,data_p,1);
read(i2c_file,result_p,1);
result16=result;
printf("%x,",result);
data = HIGH_DATA_ADDR;
write(i2c_file,data_p,1);
read(i2c_file,result_p,1);
result16=result16 | (result << 8);
result16_int=result16;
rad=((float)result16_int/(float)4096)*_2PI;
printf("%x,",result);
printf("%x,",result16);
printf("%d,",result16);
printf("%f\n",rad);
}
close(i2c_file);
return 0;
}
运行结果如下所示
最后输出一个介于0-6.28之间的值