* 优化编译脚本,加快编译速度。
* 优化代码生成,大幅减小wasm文件体积。
* 增加模拟器外壳。
AWTK支持越来越丰富了。
串口3的时钟应该是开了,工程里面根据id作为偏移量开启时钟的。
void sunxi_usart_init(sunxi_usart_t *usart)
{
uint32_t addr;
uint32_t val;
/* Config usart TXD and RXD pins */
sunxi_gpio_init(usart->gpio_tx.pin, usart->gpio_tx.mux);
sunxi_gpio_init(usart->gpio_rx.pin, usart->gpio_rx.mux);
/* Open the clock gate for usart */
addr = T113_CCU_BASE + CCU_USART_BGR_REG;
val = read32(addr);
val |= 1 << usart->id;
write32(addr, val);
/* Deassert USART reset */
addr = T113_CCU_BASE + CCU_USART_BGR_REG;
val = read32(addr);
val |= 1 << (16 + usart->id);
write32(addr, val);
/* Config USART to 115200-8-1-0 */
addr = usart->base;
write32(addr + 0x04, 0x0);
write32(addr + 0x08, 0xf7);
write32(addr + 0x10, 0x0);
val = read32(addr + 0x0c);
val |= (1 << 7);
write32(addr + 0x0c, val);
write32(addr + 0x00, 0xd & 0xff);
write32(addr + 0x04, (0xd >> 8) & 0xff);
val = read32(addr + 0x0c);
val &= ~(1 << 7);
write32(addr + 0x0c, val);
val = read32(addr + 0x0c);
val &= ~0x1f;
val |= (0x3 << 0) | (0 << 2) | (0x0 << 3);
write32(addr + 0x0c, val);
}
时钟相关代码应该是这几行。
/* Open the clock gate for usart */
addr = T113_CCU_BASE + CCU_USART_BGR_REG;
val = read32(addr);
val |= 1 << usart->id;
write32(addr, val);
/* Deassert USART reset */
addr = T113_CCU_BASE + CCU_USART_BGR_REG;
val = read32(addr);
val |= 1 << (16 + usart->id);
write32(addr, val);
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/allwinner_t113
remotes/origin/aozima_dev
remotes/origin/aozima_test
remotes/origin/aozima_test1
remotes/origin/gitee_fix
remotes/origin/gitee_master
remotes/origin/gitee_master_dev
remotes/origin/github_EDI-Systems_pr1339
remotes/origin/lts-v3.1.x
remotes/origin/master
remotes/origin/stable-v1.2.x
remotes/origin/stable-v2.0.x
remotes/origin/stable-v2.1.x
remotes/origin/stable-v3.0.x
$ git checkout -b allwinner_t113 origin/allwinner_t113
Updating files: 100% (26292/26292), done.
Switched to a new branch 'allwinner_t113'
branch 'allwinner_t113' set up to track 'origin/allwinner_t113'.
这个地址是分时使用的。
配置DLAB前,和配置之后,访问的寄存器不一样。
之前rt-thread有支持t113的,现在不支持了,各位知道原因吗?
在gitee能找到 rt-thread-allwinner_t113 这个项目的
楼主能出一个配置adb的教程吗,配置了usb后不知道咋操作了,传不了文件
参考这一篇,可以用串口直接传文件
https://whycan.com/t_4266.html
请教一下大佬,这个最后一步烧入bin文件到单板上怎么烧入的,有没有详细步骤。
教程在晕哥这个链接
编译、安装Windows版本sunxi-fel步骤 (32M spi flash补丁,支持W25Q256/MX25L256)
https://whycan.com/t_444.html#p1130
牛13!
@LinjieGuo
我这边调试f1c100s+rtthread的时候,使用sunxi-fel将boot刷到flash的时候,boot程序多次没更新到spi flash中,后来晕哥说sunxi-fel不检测flash的存在。请问你这边用sunxi-fel刷新boot或者app又发现这种现象不?
没有出现你的情况,你可以参考这篇帖子,里面留了文件。
看到一份官网开发文档https://doc.zh-jieli.com/AC79/zh-cn/master/index.html,然后在淘宝和b站都有看到官方在普及芯片。芯片性价比真是无敌呀
2023/01/04,淘宝看到,样品售价,11元/片,还不错。
我买了个饭盒,收件地址,广东广州番禺8天没到,然后。。。
快递到了广州,提示:由于派件员忙,派送时间可能增加。
结果,物流居然直接退回去了。。。
I bought a lunch box. The delivery address was Panyu, Guangzhou, Guangdong. I hadn't been there for 8 days, and then...
When the express arrived in Guangzhou, it was suggested that the delivery time might increase due to the busy dispatchers.
As a result, the logistics actually returned directly...
楼主可能是不知道怎么上传文件到whycan,
我刚好是CSDN会员,帮楼主把文件挪过来啦。(狗头保命)
另外,工程本身很小,git文件夹五十多MB,我删掉了。。。
whycan下载:F1C100S_FreeRTOS_no_git.zip
外站免费下载:F1C100S_FreeRTOS_no_git.rar(可能会失效)
虽然我没搞过C#,但很明显 textBox_debug应该要像textBox_recv那样AppendText时要区分HEX和ASCII
嗯嗯,你说的这一点也是对的。
不过问题不是出在这个地方,问题是:数据内容接收到了,但是出现读取到的长度却不一致的情况。
后来在@周正的指导下,解决这个问题。
(1)使用ReadExisting()这个方法从串口接收中取数据,适用于字符串,可能不太适合面向字节流的数据。
(2)所以,接收代码修改为:
//串口接收事件
private void ReceiveData(object sender, SerialDataReceivedEventArgs e)
{
byte[] datas = new byte[serialPort1.BytesToRead];
int len = serialPort1.Read(datas, 0, datas.Length);
//数据写入环形缓冲区
recv_lp.write_data(datas, len);
show_recv_data(datas, len);
/*
string content = serialPort1.ReadExisting(); //从串口中读取输入流,返回string
ShowData(content);
*/
}
private void show_recv_data(byte[] data, int len)
{
//更新接收数据计数
recv_cntr += (UInt32)len;
lab_recv_cntr.Text = recv_cntr.ToString();
if (rbtn_recv_hex.Checked)
{//按HEX模式 展示接收到的内容
for (int i = 0; i < len; i++)
{
textBox_recv.AppendText(string.Format("{0:X2} ", data[i]));
}
}
else
{//按ASCII模式 展示接收到的内容
string str = System.Text.Encoding.Default.GetString(data);
textBox_recv.AppendText(str);
}
}
3、测试
工程跑起来后,插入CH340G,将TX RX短接,
(1)设置为ascii模式,收发一切正常。
(2)设置为HEX模式,发送0x01、0x02 、0x99等不带字母的十六进制数,正常。
但是发送0xA0,这样带字母的十六进制数,就会出现以下情况。
(为了调试方便,我加入了日志打印区。在发生接收事件时,做2件事情,①在接收区显示收到的数据,②在日志区显示收到了多长的数据)
[1]第一次发送0xA0,触发了接收事件,①显示区没有任何显示;②数据长度是0
[2]第二次发送0xA0,触发了接收事件,①显示区显示了2个A0;②数据长度是1
结果是,数据长度对不上。。。请问有没有大神懂这是什么原因呢?
附上工程:uart_tool_base_v001_20221128.zip
外链免费下载:uart_tool_base_v001_20221128.zip
嵌入式产品一般出场前要经过参数设定、参数校准、终端号设置等操作,单片机上面没有太多的设置接口,一般而言,只有一个串口可以使用,我尝试使用VS2019+WinForm,C#开发开发一款串口调试工具,用于配置产品的参数。
1、建立好工厂后,布局,给控件命名
2、编写代码
2.1 实现端口号扫描
private void ReflashPortToComboBox(SerialPort serialPort, ComboBox comboBox)
{
if (!serialPort.IsOpen)
{//串口处于关闭状态
comboBox.Items.Clear();
string[] str = SerialPort.GetPortNames();
if (str == null)
{
MessageBox.Show("本机没有串口!", "Error");
return;
}
//添加串口
foreach (string s in str)
{
comboBox.Items.Add(s);
Console.WriteLine(s);
}
}
else
{
MessageBox.Show("串口处于打开状态下,不能刷新串口列表", "Error");
}
}
2.2 打开串口按钮代码
private void btn_open_com_Click(object sender, EventArgs e)
{
Int32 ibaudrate = Convert.ToInt32(cb_select_baudrate.SelectedItem.ToString());
serialPort1.PortName = cb_select_com.SelectedItem.ToString();
serialPort1.BaudRate = ibaudrate;
serialPort1.Parity = (System.IO.Ports.Parity)Enum.Parse(typeof(System.IO.Ports.Parity), cb_select_check_bit.Text);
serialPort1.StopBits = (System.IO.Ports.StopBits)Enum.Parse(typeof(System.IO.Ports.StopBits), cb_select_stop_bit.Text);
serialPort1.DataBits = Convert.ToInt16(cb_select_data_bits.Text);
//添加串口事件处理
serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(ReceiveData);
try
{
serialPort1.Open();
btn_open_com.Enabled = false;
btn_close_com.Enabled = true;
}
catch (Exception ex)
{
MessageBox.Show("串口打开失败"+ex, "Error");
}
}
2.3 串口接收事件代码
//串口接收事件
private void ReceiveData(object sender, SerialDataReceivedEventArgs e)
{
string content = serialPort1.ReadExisting(); //从串口中读取输入流,返回string
ShowData(content);
}
private void ShowData(string text)
{
string receiveText = text;
//更新接收数据计数
recv_cntr += (UInt32)receiveText.Length;
lab_recv_cntr.Text = recv_cntr.ToString();
textBox_debug.AppendText("接收到了"+ receiveText.Length.ToString()+ "个数据: "+ text+"\r\n");
if (rbtn_recv_hex.Checked)
{//按HEX模式 展示接收到的内容
byte[] recData = System.Text.Encoding.Default.GetBytes(receiveText);// 将接受到的字符串据转化成数组;
foreach (byte str in recData)
{
textBox_recv.AppendText(string.Format("{0:X2} ", str));
}
}
else
{//按ASCII模式 展示接收到的内容
textBox_recv.AppendText(text); //将收到的字符串追加展示出来
}
}
2.4 发送按钮代码
private void btn_send_Click(object sender, EventArgs e)
{
//串口打开的情况下,才能进行下一步操作
if (!serialPort1.IsOpen) return;
//发送内容不能为空
if ("" == textBox_send.Text) return;
string sendData = textBox_send.Text; //复制发送数据
if (true == rbtn_send_ascii.Checked)
{//字符模式
try
{
serialPort1.Write( sendData );
//更新发送数据计数
send_cntr += (UInt32)sendData.Length;
lab_send_cntr.Text = send_cntr.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "串口数据发送错误");
serialPort1.Close();
btn_open_com.Enabled = true;
btn_close_com.Enabled = false;
}
}
else
{//HEX模式
try
{
sendData.Replace("0x", ""); //去掉0x
sendData.Replace("0X", ""); //去掉0X
string[] strArray = sendData.Split(new char[] { ',', ',', '\r', '\n', ' ', '\t' });
byte[] sendBuffer = new byte[strArray.Length]; //发送数据缓冲区
int decNum = 0,i = 0;
foreach (string str in strArray)
{
try
{
decNum = Convert.ToInt16(str, 16);
sendBuffer[i] = Convert.ToByte(decNum);
i++;
}
catch
{
//MessageBox.Show("字节越界,请逐个字节输入!", "Error");
}
}
serialPort1.Write(sendBuffer, 0, sendBuffer.Length);
//更新发送数据计数
send_cntr += (UInt32)sendBuffer.Length;
lab_send_cntr.Text = send_cntr.ToString();
}
catch
{
MessageBox.Show("当前为16进制发送模式,请输入16进制数据");
}
}
}
https://whycan.com/files/members/1228/bf17a44ca6a9711dd2e93423cb844b6.png
这个电路是我比较倾向的。
在上电时,MUX都会打到终端电阻位置,
这样只有离CPU单元最近的模块能被访问到,就可以依次进行地址赋值。
我感觉原理是没有问题了,但往往这种自定义的设计,会因为开发时间久,测试不方便,成本不够低,不好落地。
请问楼主,航芯的DAP驱动在哪里下载呢?
我原本就装好了DAP-Link的驱动,使用的是WCH-Link(实际上也是DAP-Link)。
按理说,win10应该是免驱的。
说实话,我也不知道我怎么装上的,要不你参考一下这个链接?
参考以下连接:
(1)从MounRiver Studio中提取的WchLink驱动
我的电脑环境情况:
(1)MDK529+coretex-M
(2)有WCH-Link驱动(也是一款DAP-Link)
1、获取开发相关资源
(1)型号支持包:
型号支持包
(2)外设基本例程:
Core_ModulesDemo_Rev2.0.1
Nucleo_ModulesDemo_Rev1.0.8
(3)开发板PDF原理图
ACM32F403RET7_CORE_V1.2
如果链接失效,直接访问官网下载即可:
ACM官方网站
本来想使用ACM32F030xxxx/ACM32F070xxxx,然而晕哥手上只有ACM32F403RET7开发板,这款开发板所使用的主控芯片是ACM32F403RET7,资源非常丰富!不过此处就不细说了,详细内容参考帖子:
上海航芯 ACM32F403RET7 开发板, 促销价 18.8元
开发板贴图
从图上可以看到,资源不多,但是作为一个最小系统板,板载了DAP-Link,已经是非常友好。
6、烧写程序到开发板
参考链接:跳转到全志在线帖子
(1)生成烧写镜像
上述make只是生成了bin文件,我们需要进一步生成烧写镜像,才可以顺利下载。
make image
Flash Layout:
sec bin 0 boot_40M.bin : flash_offs: 0x00000000( 0K) data_size: 0x00002F28( 12K)
sec bin 1 app.bin : flash_offs: 0x00004000( 16K) data_size: 0x00004920( 19K)
sec bin 2 app_xip.bin : flash_offs: 0x00029800( 166K) data_size: 0x000058E0( 23K)
generate image: xr_system.img
cp -t ../../../../out/ ../image/"xr806"/*.bin ../image/"xr806"/xr_system.img *.map
book@book-desktop:~/work/xr806/xr806_sdk_v1.2.1/project/demo/hello_demo/gcc$
(2)获取工具
工具存放在bsp包./tools目录里,程序文件为phoenixMC.exe。
(3)烧写镜像
如下图所示
(4)查看打印
如下图所示
看起来,helloworld字样已经出来了,说明编译环境基本上已经搭建好了。
1、构建工作文件夹
(1)cd ~
(2)mkdir work
(3)cd work
(4)mkdir xr806
(5)cd xr806
2、获取资源
(1)wget http://www.armsoc.cn/whycan/xr806/xr806_sdk_v1.2.1.tgz
(2)wget http://www.armsoc.cn/whycan/xr806/gcc-arm-none-eabi-8-2019-q3-update-linux.tar.bz2
如果获取失败,直接whycan下载(可能需要积分)
-->freeRTOS bsp
xr806_sdk_v1.2.1.tgz
-->gcc工具链
gcc-arm-none-eabi-8-2019-q3-update-linux.tar.bz2
3、编译一个例程
(1)解压bsp
tar -xzvf xr806_sdk_v1.2.1.tgz
(2)选择一个例程进行编译
cd ~/work/xr806/xr806_sdk_v1.2.1/project/demo
ls
可以看到现在有一下几个例程
book@book-desktop:~/work/xr806/xr806_sdk_v1.2.1/project/demo$ ll
总用量 32
drwxrwxr-x 8 book book 4096 4月 2 16:50 ./
drwxrwxr-x 10 book book 4096 4月 2 16:50 ../
drwxrwxr-x 3 book book 4096 4月 2 17:26 at_demo/
drwxrwxr-x 4 book book 4096 4月 2 17:23 audio_demo/
drwxrwxr-x 8 book book 4096 4月 2 16:50 bluetooth/
drwxrwxr-x 3 book book 4096 4月 2 17:22 hello_demo/
drwxrwxr-x 4 book book 4096 4月 2 16:50 wlan_ble_demo/
drwxrwxr-x 3 book book 4096 4月 2 16:50 wlan_demo/
(3)选择hello_demo
cd hello_demo
(4)进入gcc目录,进行编译
cd gcc
make
结果,编译出错了,原因是我们没有安装交叉编译工具链。
...
/bin/sh: 1: /home/book/tools/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-gcc: not found
../../../../project/project.mk:437: recipe for target 'hello_demo.elf' failed
make[1]: *** [hello_demo.elf] Error 127
make[1]: 离开目录“/home/book/work/xr806/xr806_sdk_v1.2.1/project/demo/hello_demo/gcc”
../../../../project/project.mk:426: recipe for target '__all' failed
make: *** [__all] Error 2
4、安装交叉编译工具链
(1)解压工具链
cd ~/work/xr806/
tar -xvf gcc-arm-none-eabi-8-2019-q3-update-linux.tar.bz2
(2)复制到项目例程指定的目录
根据上面的错误信息,我们可以知道交叉编译工具链,应该是存放在这个位置:
/home/book/tools/gcc-arm-none-eabi-8-2019-q3-update/
cd ~
mkdir tools
cd ~/work/xr806/
cp -r gcc-arm-none-eabi-8-2019-q3-update ~/tools/
5、回到例程目录,重新编译
cd ~/work/xr806/xr806_sdk_v1.2.1/project/demo/hello_demo/gcc/
make
编译结果,看起来像是成功了
~/tools/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-objcopy -O binary -R .xip hello_demo.elf hello_demo.bin
~/tools/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-objcopy -O binary -j .xip hello_demo.elf hello_demo_xip.bin
~/tools/gcc-arm-none-eabi-8-2019-q3-update/bin/arm-none-eabi-size hello_demo.elf
text data bss dec hex filename
39768 1576 2248 43592 aa48 hello_demo.elf
make[1]: 离开目录“/home/book/work/xr806/xr806_sdk_v1.2.1/project/demo/hello_demo/gcc”
book@book-desktop:~/work/xr806/xr806_sdk_v1.2.1/project/demo/hello_demo/gcc$
我们得到了两个bin文件,一个elf文件
hello_demo.bin
hello_demo_xip.bin
hello_demo.elf
@llinjupt
哈哈,你这个功能我好像做过,但是不太完善,以前调试USB手柄的时候写的,你可以看看哈。
帖子跳转 ITX-190——一个性能不强却支持强大的开发板
@LinjieGuo
这个文件是要烧写到哪个地址呢,是地址0吗,我把下面提供的虚拟机里的bin文件烧录到0地址结果出现和你第一次报错一样的错误,但是suniv.h已经是删过冒号的了。
https://whycan.com/files/members/6074/微信截图_20220310161208.png
兄弟你的Flash是什么型号呢?
@gdzh26
限制次数可能是为了防止别人爬网站资源,
或者防止限制代下资源的情况。
另外,好像CSDN也限制每天下载次数。
并没有说whycan好,CSDN不好,
CSDN我没有文档输出,
所以,会员也开了好多年了,
我认为网站的规则如此,
不喜欢可以右上角,
强扭的瓜不甜!
相关帖子:
限制下载次数或限制下载不知是何用意?
你好朋友,我是新来的。
这么多很棒的板子。
我想自己画。 谁能给我一个购买 Allwinner F1C200s 和 F1C100s 的链接?
Hello friends, I am new here.
So many great boards.
I want to draw my own. Can anyone send me a link to buy Allwinner F1C200s and F1C100s?
直接购买晕哥的芯片就可以了。
F1C100s/F1C200s
大哥,ws2812你看看这个color表行不行。
SPI CLK 20MHz
T1H = T0L = 16个SPI数据 = 0.8us
T0H = T1L = 8个SPI数据位 = 0.4us
我从别的网站帖子上面看到了如下信息,我根据这些信息列出理论依据。
WS2812原理与实现
裁剪帖子的部分信息,可以看到,对于WS2812,其时序要求如下:
(1)T0H = T1L = 0.4us (±150ns,建立color表时,尽量不考虑误差范围,可作为芯片SPI时钟误差)
(2)T1H = T0L = 0.85us (±150ns,建立color表时,尽量不考虑误差范围,可作为芯片SPI时钟误差)
(3)与1楼的理论同理,得:
令SPI的一个时钟周期t = 0.05us,即SPI 的f = 20MHz。
T1H = T0L = 17个SPI数据
T0H = T1L = 8个SPI数据位
但是,芯片支持误差容忍(0.15us/150ns),
所以,可以尝试:
T1H = T0L = 16个SPI数据(少1个时钟节拍,0.05us/50ns)
T0H = T1L = 8个SPI数据位
对于上面我使用的WS2811的情况,2M SPI CLK,
情况如下:
(1)T0H = T1L = 0.5us = 500ns
(2)T1H = T0L =2.0us = 4 * T0H = 4 * T1L
看了你的WS2812手册里面给了个范围,
我们以WS2812B为准,将SPI CLK提升到4M。
那么,周期变成一半。
(1)T0H = T1L = 0.25us = 250ns
(2)T1H = T0L =1.0us = 1000ns =4 * T0H = 4 * T1L
观察了一下,完全符合WS2812B的时序要求。
(WS2812的数据传输速度是800KHz,WS2811(400KHz)的2倍)
所以,你至少使用4M的SPI CLK,才能驱动WS2812。
另外,可以把dma怼上去。
我是这样搞:
#define CHIP_NUM 100
typedef strut{
uint8_t rst[200]; //0.5us *200 = 100us
uint8_t frame[ CHIP_NUM ][3][5];
//3 代表颜色分量,R / G / B
//5 颜色数据,5字节SPI数据描述一个分量的颜色深浅
}ws28xx_t,ws28xx_pt;
然后定时使用DMA将帧内存通过SPI发送出去就可以了。
哈哈,大哥果然神速。
我昨天调试了半天ws2811。
怎么搞都不行,后面才发现。12V没接......
时隔多日,回来续帖。
前面知道了原理,接下来,我们得生成一个color表。
color表的结构应该是这样:
uint8_t ws2811_color[256][5]={......}
其中,256对应一中颜色分量的颜色深浅,因为RGB各占8bit。
以下是我针对1楼的时序,生成的color表:
color_map_ws2811.c
color_map_ws2811.txt
下一步是借助color_map_ws2811.c文件,编写CH579M的驱动代码!
使用tiny-R2,默认串口为UART1,bootloader的串口非常容易改,反而RTT的finsh默认串口没修改成功,请问这个在哪里修改啊,我改了几个地方都没效果。
参考官方文档:FinSH 移植
FinSH 的输出:
FinSH 的输出依赖于系统的输出,在 RT-Thread 中依赖 rt_kprintf() 输出。在启动函数 rt_hw_board_init() 中, rt_console_set_device(const char* name) 函数设置了 FinSH 的打印输出设备。
对应串口引脚配置,应该在drv_uart.c里面,
有这样一个结构体,
static rt_err_t uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg);
static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *arg);
static int uart_putc(struct rt_serial_device *serial, char c);
static int uart_getc(struct rt_serial_device *serial);
static rt_size_t uart_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction);
void uart_irq_handler(int irqno, void *param);
const struct rt_uart_ops _uart_ops =
{
uart_configure,
uart_control,
uart_putc,
uart_getc,
uart_dma_transmit
};
可以看出,就是以下这个函数负责配置串口引脚的。
static rt_err_t uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg);
市面上常见的2款串行可编程控制LED方案,分别是WS2811和WS2812。
其中,
[1]WS2811是芯片,常规供电为12V,按理说,驱动距离会远一些。
[2]WS2812是灯珠,把可编程芯片内嵌在灯珠里面,常规供电5V,按理说,驱动距离相对短。
相信很多朋友都在51单片机上面驱动过这个芯片,使用方法是:IO模拟单总线时序,达到传输RGB数据的效果。
因为WS2811/2812驱动时序,是us级的,IO模拟的方法,过度依赖CPU,这种做法实在不合理。
正确做法应该是借助片上的通讯外设,如UART/SPI等,兼容WS2811/2812的时序,腾出CPU资源,并且提高灯带驱动的稳定性。
本文章阐述,使用SPI驱动WS2811的思路。
先看手册:
![image-20220122160847782](image-20220122160847782.png)
![image-20220122160959434](image-20220122160959434.png)
从WS2811的手册,可以得知( 高速模式下 ):
[1]这款芯片支持2种驱动速度:分别为高速模式和低速模式。
[2]对单总线送'0'码和'1'码,需要使用T0H / T1H / T0L /T1L搭配。
[3]搭配出来的'0'码和'1'码所占用时长皆为 2.5us。
[4]T0H = T1L = 0.5us
[5]T1H = T0L =2.0us = 4 * T0H = 4 * T1L
因此,SPI总线的驱动时钟,一个时钟脉冲的周期不能大于 T0H / T1L ,也就是不能大于0.5us。
我们试着就让SPI总线一个时钟脉冲的周期就等于0.5us,Tspiclk = 0.5us。
那么,频率Fspiclk = 1 / Tspiclk = 2MHz。
这种情况下,如果想要送RGB888数据中的 1 位 数据 到WS2811,SPI总线需要发出5个位的数据:
○ 1 : 00001
○ 0 : 11110
如果要发1字节数据(8个位),如R = 0xF0,对应二进制为:
1111 0000
那么SPI应该发出这样的数据:
00001 00001 00001 00001 11110 11110 11110 11110
对其进行组合,可以得到5个字节:
0000 1000 0100 0010 0001 1111 0111 1011 1101 1110
所以,SPI应该发送的数据为:
0X08 0X42 0X1F 0X7B 0XDE
同理,对于R 、G、 B都可以这样操作。
那么,也就是说,想要给一颗WS2811输送RGB888数据,需要发送5*3 = 15个字节。
@LinjieGuo
下载了,还是出错误。
https://whycan.com/files/members/7570/rtt错误1.jpg
用惯了Keil这种界面,有错误直接跳到出错代码处
这个rtt一时难以上手
rtt的源码重新下载解压了吗?
我把我的整个rtt目录上传上来,你看看。
rt_thread_fal_elm_dfs_20220115.zip
我也来学习,初次接触RTT,文件在那里也不知道,给我来了个错误了
https://whycan.com/files/members/7570/RTT.png
哈哈,我上传一下代码啊。
你这个情况,感觉像是源码没有下载全的样子。
可以试试重新拉取系统源码,然后把bsp工程复制进去。
allwinner_tina_prj_v1_fat_elm_dfs.zip
@luoyuan0130
同意你说的互相开源学习,
只要你开源了自己的作品/资源,
积分就来了,
那么自然就可以下载想要的资源。
(开源不等于免费,以下资料分享给你 )
[文章分享:再看中国法院是怎么对待GPL的]
对上一个帖子的要求,我们再度优化bsp_fal_init()函数:
void bsp_fal_init(void)
{
int fd, size;
struct statfs elm_stat;
struct fal_blk_device *blk_dev;
char str[] = "enable sdcard 1 ", buf[80];
/* fal init */
fal_init();
/* create block device */
blk_dev = (struct fal_blk_device *)fal_blk_device_create(FS_PARTITION_NAME);
if(blk_dev == RT_NULL)
rt_kprintf("Can't create a block device on '%s' partition.\n", FS_PARTITION_NAME);
else
rt_kprintf("Create a block device on the %s partition of flash successful.\n", FS_PARTITION_NAME);
/* mount elmfat file system to FS_PARTITION_NAME */
if(dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0)
{
rt_kprintf("elmfat filesystem mount success.\n");
return;
}
else
{
rt_kprintf("elmfat filesystem mount failed.\n");
goto __do_mount_bolck_dev_fs_failed_fal_action;
}
__do_mount_bolck_dev_fs_failed_fal_action:
/* make a elmfat format filesystem */
if(dfs_mkfs("elm", FS_PARTITION_NAME) == 0)
{
rt_kprintf("make elmfat filesystem success.\n");
if(dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0)
{
rt_kprintf("elmfat filesystem mount success.\n");
}
}
else
{
rt_kprintf("make elmfat filesystem faild.\n");
while(1);
}
/* Get elmfat file system statistics */
if(statfs("/", &elm_stat) == 0)
rt_kprintf("elmfat filesystem block size: %d, total blocks: %d, free blocks: %d.\n",
elm_stat.f_bsize, elm_stat.f_blocks, elm_stat.f_bfree);
if(mkdir("/user", 0x777) == 0)
rt_kprintf("make a directory: '/user'.\n");
if(mkdir("/sdcard", 0x777) == 0)
rt_kprintf("make a directory: '/sdcard'.\n");
if(mkdir("/config", 0x777) == 0)
rt_kprintf("make a directory: '/sdcard'.\n");
rt_kprintf("Write string '%s' to /config/config.txt.\n", str);
/* Open the file in create and read-write mode, create the file if it does not exist*/
fd = open("/config/config.txt", O_WRONLY | O_CREAT);
if (fd >= 0)
{
if(write(fd, str, sizeof(str)) == sizeof(str))
rt_kprintf("Write data done.\n");
close(fd);
}
}
编译,修饰,烧写,启动,没啥改变
msh />
msh />
msh />
msh />
msh />ls
Directory /:
user <DIR>
whycan <DIR>
msh />
(使用命令行)擦掉这个这个分区的数据,再试一下:
msh />fal probe
No flash device or partition was probed.
Usage: fal probe [dev_name|part_name] - probe flash device or partition by given name.
[I/FAL] ==================== FAL partition table ====================
[I/FAL] | name | flash_dev | offset | length |
[I/FAL] -------------------------------------------------------------
[I/FAL] | sys_partition | wind25qx | 0x00000000 | 0x00100000 |
[I/FAL] | root_partition | wind25qx | 0x00100000 | 0x00f00000 |
[I/FAL] =============================================================
msh />fal probe root_partition
Probed a flash partition | root_partition | flash_dev: wind25qx | offset: 1048576 | len: 15728640 |.
msh /
msh />
msh />fal erase 0 4096
Erase data success. Start from 0x00000000, size is 4096.
msh />
重启单板,咦?我们刚刚创建的whycan文件夹没了??
这一看,那一看,发现,每次开机,bsp_fal_init()函数都会做一遍格式化操作。
我们是不是可以这样:
(1)[分区创建块设备]
(2)尝试挂载块设备到文件系统
如果成功,就不格式化了
如果失败,则格式化完在挂载
说干就干!修改一下这个bsp_fal_init()函数:(关键代码)
__mount_fal:
/* mount elmfat file system to FS_PARTITION_NAME */
if(dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0)
{
rt_kprintf("elmfat filesystem mount success.\n");
}
else
{
/* make a elmfat format filesystem */
if(dfs_mkfs("elm", FS_PARTITION_NAME) == 0)
{
rt_kprintf("make elmfat filesystem success.\n");
goto __mount_fal;
}
else
{
rt_kprintf("make elmfat filesystem faild.\n");
while(1);
}
}
继续前进。
参考文章IOT-OS之RT-Thread(十一)--- FAL分区管理与easyflash变量管理
我们抄袭一下,跟他做法相仿,文件编排略有差异,只为了实现功能。
修改main.c文件,
(1)增加函数 bsp_fal_init()
#include "dfs_posix.h"
#define FS_PARTITION_NAME "root_partition"
void bsp_fal_init(void)
{
int fd, size;
struct statfs elm_stat;
struct fal_blk_device *blk_dev;
char str[] = "elmfat mount to W25Q flash.", buf[80];
/* fal init */
fal_init();
/* create block device */
blk_dev = (struct fal_blk_device *)fal_blk_device_create(FS_PARTITION_NAME);
if(blk_dev == RT_NULL)
rt_kprintf("Can't create a block device on '%s' partition.\n", FS_PARTITION_NAME);
else
rt_kprintf("Create a block device on the %s partition of flash successful.\n", FS_PARTITION_NAME);
/* make a elmfat format filesystem */
if(dfs_mkfs("elm", FS_PARTITION_NAME) == 0)
rt_kprintf("make elmfat filesystem success.\n");
/* mount elmfat file system to FS_PARTITION_NAME */
if(dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0)
rt_kprintf("elmfat filesystem mount success.\n");
/* Get elmfat file system statistics */
if(statfs("/", &elm_stat) == 0)
rt_kprintf("elmfat filesystem block size: %d, total blocks: %d, free blocks: %d.\n",
elm_stat.f_bsize, elm_stat.f_blocks, elm_stat.f_bfree);
if(mkdir("/user", 0x777) == 0)
rt_kprintf("make a directory: '/user'.\n");
rt_kprintf("Write string '%s' to /user/test.txt.\n", str);
/* Open the file in create and read-write mode, create the file if it does not exist*/
fd = open("/user/test.txt", O_WRONLY | O_CREAT);
if (fd >= 0)
{
if(write(fd, str, sizeof(str)) == sizeof(str))
rt_kprintf("Write data done.\n");
close(fd);
}
/* Open file in read-only mode */
fd = open("/user/test.txt", O_RDONLY);
if (fd >= 0)
{
size = read(fd, buf, sizeof(buf));
close(fd);
if(size == sizeof(str))
rt_kprintf("Read data from file test.txt(size: %d): %s \n", size, buf);
}
}
(2)修改main函数,显示调用bsp_fal_init()
int main(int argc, char **argv)
{
rt_kprintf("Start...\n");
rt_kprintf("periph_get_pll_clk:%d\n", periph_get_pll_clk());
rt_kprintf("cpu_get_clk:%d\n", cpu_get_clk());
rt_kprintf("ahb_get_clk:%d\n", ahb_get_clk());
rt_kprintf("apb_get_clk:%d\n", apb_get_clk());
bsp_fal_init();
MainThreadCreat();
return 0;
}
(3)修改start_task,关掉TF/SD卡挂载相关。
void start_thread(void *parameter)
{
int ret = 0;
#if 0
if( 0 == dfs_mount("sd0","/","elm", 0,0) )
{
printf("sd0 mount to / \n");
}
else
{
printf("sd0 mount to / faild \n");
}
#endif
while(1)
{
rt_thread_delay(1);
}
}
(4)编译,修饰,烧写。
按照官方知道文档,测试一下--> 软件包-FAL
(1)查看有那些分区
msh />fal probe
No flash device or partition was probed.
Usage: fal probe [dev_name|part_name] - probe flash device or partition by given name.
[I/FAL] ==================== FAL partition table ====================
[I/FAL] | name | flash_dev | offset | length |
[I/FAL] -------------------------------------------------------------
[I/FAL] | sys_partition | wind25qx | 0x00000000 | 0x00100000 |
[I/FAL] | root_partition | wind25qx | 0x00100000 | 0x00f00000 |
[I/FAL] =============================================================
(2)指定待操作的 Flash 设备或 Flash 分区
不能选择sys_partition 这个分区,因为这个分区存储了我们的系统程序,擦掉系统就没了。
所以我们选择 root_partition分区:
msh />fal probe root_partition
Probed a flash partition | root_partition | flash_dev: wind25qx | offset: 1048576 | len: 15728640 |.
(3)擦除数据
msh />fal erase 0 4096
Erase data success. Start from 0x00000000, size is 4096.
(4)写入数据
msh />fal write 8 1 2 3 4 5
Write data success. Start from 0x00000008, size is 5.
Write data: 1 2 3 4 5 .
(5)读取数据
msh />fal read 0 64
Read data success. Start from 0x00000000, size is 64. The data is:
Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
[00000000] FF FF FF FF FF FF FF FF 01 02 03 04 05 FF FF FF ................
[00000010] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
[00000020] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
[00000030] FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................
(6)性能测试
msh />fal bench 4096 yes
Erasing 15728640 bytes data, waiting...
Erase benchmark success, total time: 36.614S.
Writing 15728640 bytes data, waiting...
Write benchmark success, total time: 61.441S.
Reading 15728640 bytes data, waiting...
Read benchmark success, total time: 4.267S.
也就是说,FAL根本就没有初始化。
我们让他像elm组件一样,自动初始化。
在fal.c文件底部,加入一个宏修饰
INIT_PREV_EXPORT(fal_init);
编译,修饰,下载,启动查看串口打印:
一眼看过去,肯定不对劲,关键的红字信息。
initialize fal_init[E/SFUD] ERROR: Flash device spi00 not found!
在初始化fal的时候,系统跟我们说,找不到spi00。
咦?我们前面定义的宏,明明是:
#define NOR_FLASH_DEV_NAME "wind25qx"
他跟我们说,找不到spi00,那肯定有问题。
我折腾了很久,发现了问题在menuconfig环节,我手欠的把这一项修改了。
修改这一项为 wind25qx,保存,退出menuconfig,然后编译,修饰,烧写。
文件fal_flash_sfud_port.c
/*
* File : fal_flash_sfud_port.c
* This file is part of FAL (Flash Abstraction Layer) package
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-01-26 armink the first version
*/
#include <fal.h>
#include <sfud.h>
#ifdef FAL_USING_SFUD_PORT
#ifdef RT_USING_SFUD
#include <spi_flash_sfud.h>
#endif
#ifndef FAL_USING_NOR_FLASH_DEV_NAME
#define FAL_USING_NOR_FLASH_DEV_NAME "wind25qx"
#endif
static int init(void);
static int read(long offset, uint8_t *buf, size_t size);
static int write(long offset, const uint8_t *buf, size_t size);
static int erase(long offset, size_t size);
static sfud_flash_t sfud_dev = NULL;
struct fal_flash_dev nor_flash0 =
{
.name = FAL_USING_NOR_FLASH_DEV_NAME,
.addr = 0,
.len = 16 * 1024 * 1024,
.blk_size = 4096,
.ops = {init, read, write, erase},
.write_gran = 1
};
static int init(void)
{
#ifdef RT_USING_SFUD
/* RT-Thread RTOS platform */
sfud_dev = rt_sfud_flash_find_by_dev_name(FAL_USING_NOR_FLASH_DEV_NAME);
#else
/* bare metal platform */
extern sfud_flash sfud_norflash0;
sfud_dev = &sfud_norflash0;
#endif
if (NULL == sfud_dev)
{
return -1;
}
/* update the flash chip information */
nor_flash0.blk_size = sfud_dev->chip.erase_gran;
nor_flash0.len = sfud_dev->chip.capacity;
return 0;
}
static int read(long offset, uint8_t *buf, size_t size)
{
assert(sfud_dev);
assert(sfud_dev->init_ok);
sfud_read(sfud_dev, nor_flash0.addr + offset, size, buf);
return size;
}
static int write(long offset, const uint8_t *buf, size_t size)
{
assert(sfud_dev);
assert(sfud_dev->init_ok);
if (sfud_write(sfud_dev, nor_flash0.addr + offset, size, buf) != SFUD_SUCCESS)
{
return -1;
}
return size;
}
static int erase(long offset, size_t size)
{
assert(sfud_dev);
assert(sfud_dev->init_ok);
if (sfud_erase(sfud_dev, nor_flash0.addr + offset, size) != SFUD_SUCCESS)
{
return -1;
}
return size;
}
#endif /* FAL_USING_SFUD_PORT */
文件fal_cfg.h
/*
* File : fal_cfg.h
* This file is part of FAL (Flash Abstraction Layer) package
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-05-17 armink the first version
*/
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_
#include <rtconfig.h>
#include <board.h>
#define NOR_FLASH_DEV_NAME "wind25qx"
/* ===================== Flash device Configuration ========================= */
extern struct fal_flash_dev nor_flash0;
/* flash device table */
#define FAL_FLASH_DEV_TABLE \
{ \
&nor_flash0, \
}
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WORD, "sys_partition", NOR_FLASH_DEV_NAME, 0, 1*1024*1024, 0}, \
{FAL_PART_MAGIC_WORD, "root_partition", NOR_FLASH_DEV_NAME, 1*1024*1024, 15*1024*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
#endif /* _FAL_CFG_H_ */
修改完之后,分区表情况是这样的:、
(1)分区0:
分区名字 :sys_partition,系统分区
分区绑定的块设备 :NOR_FLASH_DEV_NAME
绑定的块设备起始地址:0
本分区的尺寸 :1*1024*1024,也就是1MB
(2)分区1:
分区名字 :root_partition,根分区
分区绑定的块设备 :NOR_FLASH_DEV_NAME
绑定的块设备起始地址:1*1024*1024,也就是从1MB位置开始。
本分区的尺寸 :15*1024*1024,也就是15MB
报错是意料之中,好,那我们就按照要求,修改一下fal相关的2个文件。
[bsp/allwinner_tina_prj_v1/packages/fal-v0.5.0/packge/samples/porting/fal_cfg.h]
[bsp/allwinner_tina_prj_v1/packages/fal-v0.5.0/packge/samples/porting/fal_flash_sfud_port.c]
这两个文件修改后,我们将文件挪一下,
(1)h文件复制到[bsp\allwinner_tina_prj_v1\packages\fal-v0.5.0\inc\]目录下
(2)c文件复制到[bsp\allwinner_tina_prj_v1\packages\fal-v0.5.0\src\]目录下
(3)删除[bsp/allwinner_tina_prj_v1/packages/fal-v0.5.0/packge/samples/]这个目录
(不要问我为什么要这样做,因为我不懂怎么改里面的SConscript脚本,干脆这样干了,~~笑~~)
回到家里,使用家里电脑,menuconfig之后,选择FAL,然后pkgs --update,成功把包更新到bsp。
Linjie@DESKTOP-NTLR93Q D:\SVN_Public\rt_thread\bsp\allwinner_tina_prj_v1
$ pkgs --update
Start to download package : fal-0.5.0.zip
Downloded 137 KB
Start to unpack. Please wait...
==============================> FAL v0.5.0 is downloaded successfully.
Operation completed successfully.
Linjie@DESKTOP-NTLR93Q D:\SVN_Public\rt_thread\bsp\allwinner_tina_prj_v1
先看目录里有没有东西
Linjie@DESKTOP-NTLR93Q D:\SVN_Public\rt_thread\bsp\allwinner_tina_prj_v1
$ ls -l packages\
total 11
-rw-r--r-- 1 Linjie Administrators 266 Jan 8 20:00 SConscript
drwxr-xr-x 6 Linjie Administrators 4096 Jan 8 20:00 fal-v0.5.0
-rw-r--r-- 1 Linjie Administrators 4096 Jan 8 20:00 packages.dbsqlite
-rw-r--r-- 1 Linjie Administrators 86 Jan 8 20:00 pkgs.json
-rw-r--r-- 1 Linjie Administrators 2 Jan 8 20:00 pkgs_error.json
可以看到,fal-v0.5.0已经被下载下来了。
我们什么都不做,编译看看,因为我们没有实现某些接口,不出意外的话,会报错。
编译完,果然,报错了。
CC build\libcpu\stack.o
CC build\libcpu\trap.o
CC build\packages\fal-v0.5.0\samples\porting\fal_flash_sfud_port.o
In file included from packages\fal-v0.5.0\samples\porting\fal_flash_sfud_port.c:25:0:
packages\fal-v0.5.0\inc/fal.h:29:21: fatal error: fal_cfg.h: No such file or directory
compilation terminated.
scons: *** [build\packages\fal-v0.5.0\samples\porting\fal_flash_sfud_port.o] Error 1
scons: building terminated because of errors.
文档的后面还让我们参考这里FAL
说干就干
<1>定义片外 spi flash 设备,可以参考 fal_flash_sfud_port.c
一会我把这个文件下载下来,捣鼓一下。
<2>定义 flash 设备表
Flash 设备表定义在 fal_cfg.h 头文件中,定义分区表前需 新建 fal_cfg.h 文件 ,请将该文件统一放在对应 BSP 或工程目录的 port 文件夹下,并将该头文件路径加入到工程。
fal_cfg.h 可以参考 示例文件 fal/samples/porting/fal_cfg.h 完成。
一会这个文件我也下载下来,捣鼓捣鼓。
好,这两个文件弄下来了(复制),那么放在哪里呢?
想了一下,我编不下去了,source insight包含了整个工程(BSP只包含allwinner_tina_prj_v1,其他没有包含。)。
好家伙,一个"fal"关键词的文件都没有。感觉是配置了跟没配置一样。
---- fal Matches (0 in 0 files) ----
上面是FAL的开启指引,咱们按照指引进行一顿操作!
--- fal: Flash Abstraction Layer implement. Manage flash device and partition.
[*] Enable debug log output
[*] FAL partition table config has defined on 'fal_cfg.h'
[*] FAL uses SFUD drivers
(spi00) The name of the device used by FAL
version (v0.5.0) --->
保存,然后使用命令,从menuconfig更新packge到工程中,packages目录里面有一些内容,
没有.c文件,不知道是不是有异常。
x@LAPTOP-BH56UJG6 E:\SVN_Public\rt_thread\bsp\allwinner_tina_prj_v1
$ pkgs --update
x@LAPTOP-BH56UJG6 E:\SVN_Public\rt_thread\bsp\allwinner_tina_prj_v1
$ ls -l packages\
total 7
-rw-r--r-- 1 meika Administrators 266 Jan 8 13:44 SConscript
-rw-r--r-- 1 meika Administrators 1288 Jan 8 14:27 fal_cfg.h
-rw-r--r-- 1 meika Administrators 2048 Jan 8 13:44 packages.dbsqlite
-rw-r--r-- 1 meika Administrators 2 Jan 8 13:44 pkgs.json
-rw-r--r-- 1 meika Administrators 2 Jan 8 14:59 pkgs_error.json
编译工程,按理说我没有实现指定的接口,应该会报错,但是结果是没有报错,跟之前一样。
@LinjieGuo
请教了RTT群里的大神,RTT是支持把SPI FLASH进行分区管理的。也就是说,可以把Flash分出2个区或者多个区,然后bin文件下载进分区0,分区1/2...就可以用来格式化了。
这得益于RTT的扩展包FAL--RTT-FAL支持
我们根据指导文件进行操作。
1.1、打开 FAL
使用 fal package 需要在 RT-Thread 的包管理器中选择它,具体路径如下:
RT-Thread online packages
system packages --->
--- fal: Flash Abstraction Layer implement. Manage flash device and partition.
[*] Enable debug log output
[*] FAL partition table config has defined on 'fal_cfg.h'
(onchip) The flash device which saving partition table
(65536) The patition table end address relative to flash device offset.
[ ] FAL uses SFUD drivers
(norflash0) The name of the device used by FAL (NEW)
version (latest) --->
每个功能的配置说明如下:
(1)开启调试日志输出(默认开启);
(2)分区表是否在 fal_cfg.h 中定义(默认开启)。如果关闭此选项,fal 将会自动去指定 Flash 的指定位置去检索并装载分区表,
具体配置详见下面两个选项;
存放分区表的 Flash 设备;
分区表的 结束地址 位于 Flash 设备上的偏移。
(3)启用 FAL 针对 SFUD 的移植文件(默认关闭);
然后让 RT-Thread 的包管理器自动更新,或者使用 pkgs --update 命令更新包到 BSP 中。
9、疑问,为什么上面挂接sd卡的代码要延时呢?
因为不延时,挂接不成功啊!
为什么会这样呢?通过查看代码,其实是跟SDIO相关的。
int rt_mmcsd_core_init(void)
{
rt_err_t ret;
/* initialize detect SD cart thread */
/* initialize mailbox and create detect SD card thread */
ret = rt_mb_init(&mmcsd_detect_mb, "mmcsdmb",
&mmcsd_detect_mb_pool[0], sizeof(mmcsd_detect_mb_pool) / sizeof(mmcsd_detect_mb_pool[0]),
RT_IPC_FLAG_FIFO);
RT_ASSERT(ret == RT_EOK);
ret = rt_mb_init(&mmcsd_hotpluge_mb, "mmcsdhotplugmb",
&mmcsd_hotpluge_mb_pool[0], sizeof(mmcsd_hotpluge_mb_pool) / sizeof(mmcsd_hotpluge_mb_pool[0]),
RT_IPC_FLAG_FIFO);
RT_ASSERT(ret == RT_EOK);
ret = rt_thread_init(&mmcsd_detect_thread, "mmcsd_detect", mmcsd_detect, RT_NULL,
&mmcsd_stack[0], RT_MMCSD_STACK_SIZE, RT_MMCSD_THREAD_PREORITY, 20);
if (ret == RT_EOK)
{
rt_thread_startup(&mmcsd_detect_thread);
}
rt_sdio_init();
return 0;
}
rt_mmcsd_core_init()这个函数里面,创建了一个线程,这个线程里面负责SDIO接口的一些事情,
具体做什么事情还没搞懂,凭我的直觉,反正就是跟他有关系。~笑~
void mmcsd_detect(void *param)
{
struct rt_mmcsd_host *host;
rt_uint32_t ocr;
rt_int32_t err;
while (1)
{
if (rt_mb_recv(&mmcsd_detect_mb, (rt_ubase_t *)&host, RT_WAITING_FOREVER) == RT_EOK)
{
if (host->card == RT_NULL)
{
mmcsd_host_lock(host);
mmcsd_power_up(host);
mmcsd_go_idle(host);
mmcsd_send_if_cond(host, host->valid_ocr);
err = sdio_io_send_op_cond(host, 0, &ocr);
if (!err)
{
if (init_sdio(host, ocr))
mmcsd_power_off(host);
mmcsd_host_unlock(host);
continue;
}
/*
* detect SD card
*/
err = mmcsd_send_app_op_cond(host, 0, &ocr);
if (!err)
{
if (init_sd(host, ocr))
mmcsd_power_off(host);
mmcsd_host_unlock(host);
rt_mb_send(&mmcsd_hotpluge_mb, (rt_ubase_t)host);
continue;
}
/*
* detect mmc card
*/
err = mmc_send_op_cond(host, 0, &ocr);
if (!err)
{
if (init_mmc(host, ocr))
mmcsd_power_off(host);
mmcsd_host_unlock(host);
rt_mb_send(&mmcsd_hotpluge_mb, (rt_ubase_t)host);
continue;
}
mmcsd_host_unlock(host);
}
else
{
/* card removed */
mmcsd_host_lock(host);
if (host->card->sdio_function_num != 0)
{
LOG_W("unsupport sdio card plug out!");
}
else
{
rt_mmcsd_blk_remove(host->card);
rt_free(host->card);
host->card = RT_NULL;
}
mmcsd_host_unlock(host);
rt_mb_send(&mmcsd_hotpluge_mb, (rt_ubase_t)host);
}
}
}
}
8、访问tf/sd卡
(1)格式化sd卡,有3种方法
<方法1>使用msh命令行格式化:
msh />list_device
device type ref count
-------- -------------------- ----------
sd0 Block Device 0
wind25qx Block Device 0
spi10 SPI Device 0
spi1 SPI Bus 0
spi00 SPI Device 0
spi0 SPI Bus 0
uart2 Character Device 0
uart0 Character Device 2
msh />mkfs -t elm sd0
msh />
<方法2>程序里面格式化(参考官方文档:虚拟文件系统)
dfs_mkfs( "elm", "sd0");//其中elm是文件系统类型,sd0是块设备名称
<方法3>把tf卡拔出来,电脑格式化~~~笑~~
(2)挂载sd卡到根目录
原本,我想把spi flash块设备挂载为/,然后sd卡挂接为/sdcard,
奈何本人水平太差,不知道怎么分割spiflash的空间,一部分作为程序空间,一部分作为存储空间。(希望高手指教)
所以,直接把sd卡挂接为/。
几行代码搞定,把代码写在线程里:
void start_thread(void *parameter)
{
int ret = 0;
rt_thread_delay(100);
/*挂载sdio0上的sd卡,文件系统类型为elm*/
if( 0 == dfs_mount("sd0","/","elm", 0,0) )
{
printf("sd0 mount to / \n");
}
else
{
printf("sd0 mount to / faild \n");
}
while(1)
{
rt_thread_delay(1);
}
}
(3)使用msh给sd卡创建文件夹。
7、查看代码,看看能不能找到问题所在
既然是一个成熟的系统,那么,十有八九,不是别人系统问题,只能是单板匹配问题。
单板差异,那么驱动就有差异,E/MMC相关的是sdio,我们看看SDIO引脚配置是否匹配。
从源码里面看到,
SDIO 0所使用的引脚为:/* SDC0: PF0-PF5 */[[]],
SDIO 1没有使用,所以没有配置。
static void sdio_gpio_init(struct sdio_drv *sdio_des)
{
int pin;
if ((rt_uint32_t)sdio_des->mmc_des == MMC0_BASE_ADDR)
{
/* SDC0: PF0-PF5 */[[]]
for (pin = GPIO_PIN_0; pin <= GPIO_PIN_5; pin++)
{
gpio_set_func(GPIO_PORT_F, pin, IO_FUN_1);
gpio_set_pull_mode(GPIO_PORT_F, pin, PULL_UP);
gpio_set_drive_level(GPIO_PORT_F, pin, DRV_LEVEL_2);
}
}
else if ((rt_uint32_t)sdio_des->mmc_des == MMC1_BASE_ADDR)
{
//todo: config gpio port
RT_ASSERT(0);
}
}
对应我们的单板,SDIO0 使用的引脚情况是一致的。
找不到红色字的原因,只好先放下他。先看看其他东西怎么玩!
6、搜索相关内容
通过搜索关键字:[E/[MMC]] [err]:0x00000100, RTO
找到相关帖子
(1)f1c100s 运行时提示错
看来RTT论坛并不重视答疑,从2020年的帖子,到现在都没有解答。
(2)找不到相关内容~~~~难受!
5、看看系统启动流程
我们看看系统注册了什么组件、注册了什么驱动。启动的时候,做了什么事情。
RTT借助宏INIT_EXPORT实现了自动初始化机制,我们找到这个宏的定义:
(参考帖子:[求助]RTT组件自动统一初始化宏INIT_EXPORT)
自动初始化机制的核心就在文件D:\rt-thread-gitee_master\src\components.c
和D:\rt-thread-gitee_master\include\rtdef.h里面
因为代码篇幅过长,不做详细描述,咱们使能RTT内核调试宏,将初始化流程打印出来看看
(1)使能内核调试宏
#ifndef RT_DEBUG_INIT
#define RT_DEBUG_INIT 1
#endif
(2)编译、修饰、烧写、查看启动日志
可以看到,启动的时候,执行了哪些组件的初始化。(BOARD/PREV/DEVICE/COMPONENT/ENV/APP)
打印日志上面2条红色的错误依然醒目,得看看是什么原因导致的。
4、现象,接上串口0,看打印。
打印了一些乱七八糟的东西,可能是板子不匹配导致的。
(1)修改main.c文件,干掉一些东西,先让系统跑起来。
#include "rtthread.h"
#include "drv_clock.h"
#include "drv_gpio.h"
#include <rtdevice.h>
#include <dfs_fs.h>
#include <drv_sdio.h>
#include <drv_fb.h>
#include <stdio.h>
#include "UartCursor.h"
#include "LVGL_Interface.h"
#include "demo.h"
#include "AppPriorityList.h"
lcd_layer_Struct lcd_layer1={
.width=100,
.height=100,
.posx=0,
.posy=0,
.priority=1,
.pipe=1,
.alpha_enable=1,
.alpha_value=0xA0,
.vram=(void *)0x80500000,
.vram_format=VRAM_FORMAT_RGB565,
};
lcd_layer_Struct lcd_layer2={
.width=100,
.height=100,
.posx=50,
.posy=50,
.priority=2,
.pipe=0,
.alpha_enable=1,
.alpha_value=0xA0,
.vram=(void *)0x80600000,
.vram_format=VRAM_FORMAT_RGB565,
};
void start_thread(void *parameter)
{
while(1)
{
rt_thread_delay(1);
}
}
#define RT_STARTLOOP_THREAD_STACK_SIZE 2048
#define MY_DISP_HOR_RES LV_HOR_RES_MAX
static void MainThreadCreat()
{
rt_thread_t tid;
tid = rt_thread_create("start_thread", start_thread, RT_NULL,
RT_STARTLOOP_THREAD_STACK_SIZE, PRIORITY_START_LOOP, 20);
RT_ASSERT(tid != RT_NULL);
rt_thread_startup(tid);
rt_kprintf("[Thread]start_thread Created.\n");
}
int main(int argc, char **argv)
{
rt_kprintf("Start...\n");
rt_kprintf("periph_get_pll_clk:%d\n", periph_get_pll_clk());
rt_kprintf("cpu_get_clk:%d\n", cpu_get_clk());
rt_kprintf("ahb_get_clk:%d\n", ahb_get_clk());
rt_kprintf("apb_get_clk:%d\n", apb_get_clk());
MainThreadCreat();
return 0;
}
(2)编译,修饰,烧写。
复位单板,启动,看起来较为正常了,但是还是有些问题。
插上TF卡,还是这个样子,怀疑是板子引脚不匹配导致的。
最近画了块f1c200s_ch579m的板子,尝试给200s跑RTT。
F1C100s-LCD-TV-CH579M 板子
涉及内容:
(1)基于RTThread的F1C100s开发(带boot启动+硬件多图层+硬件游标+LVGL+SDIO)
(2)Gitee-RT-Thread
(3)RT-Thread 下载
1、从上面链接下载资源,获取资源
(1)[rt-thread-gitee_master.zip]
(2)[allwinner_tina_prj_v1.zip]
(3)[env_released_1.2.0.zip]
2、安装ENV
(1)安装步骤,请观看视频:跳转到RTT官方视频页
3、解压资源
(1)解压 [rt-thread-gitee_master.zip] 到D盘根目录(当然也可以其他盘啦~笑~)
(2)解压 [allwinner_tina_prj_v1.zip] ,
然后复制allwinner_tina_prj_v1 到 rt-thread-gitee_master\bsp目录下。
(有人问,为什么不用官方的allwinner_tina呢?因为官方的工程,不带bootloader,咱们为了方便,直接借助网友的工程。)
(3)在目录 D:\rt-thread-gitee_master\bsp\allwinner_tina_prj_v1,打开ENV。
(4)在ENV下,输入menuconfig。
(5)编译RTT,输入scons。
(6)得到未经修饰的rtthread.bin
(7)执行脚本文件,[flashtospi.bat],
这个脚本用于修饰rtthread.bin,增加魔术头,并下载bin文件到flash中。
(8)提示缺少120D.dll文件,咱们从网上复制一个这个文件进来,放在D:\rt-thread-gitee_master\bsp\allwinner_tina_prj_v1\tools目录下。
继续执行第(7)步,修饰rtthread.bin成功。但是下载失败。
板子接入USB,按住上面的BOOT键,按下RST键,然后松开RST,此时电脑识别到usb设备。
继续执行第(7)步,程序下载成功。
问题已经解决,在keil下,关键代码如下:
(1)h文件
/*自动初始化模块*/
#define RT_SECTION(x) __attribute__((section(x)))
#define RT_UNUSED __attribute__((unused))
#define RT_USED __attribute__((used))
typedef int (*init_fn_t)(void);
#ifdef _MSC_VER /* we do not support MS VC++ compiler */
#pragma section("rti_fn$f",read)
struct rt_init_desc
{
const char* level;
const init_fn_t fn;
};
#define INIT_EXPORT(fn, level) \
const char __rti_level_##fn[] = level"__rt_init_"#fn; \
__declspec(allocate("rti_fn$f")) \
RT_USED const struct rt_init_desc __rt_init_msc_##fn = \
{__rti_level_##fn, fn };
#else
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn RT_SECTION(".rti_fn." level) = fn
#endif
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
(2)c文件
static int rti_start(void)
{
return 0;
}
INIT_EXPORT(rti_start, "0");
static int rti_end(void)
{
return 0;
}
INIT_EXPORT(rti_end,"7");
void rt_components_init(void)
{
const init_fn_t* fn_ptr;
for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_end; )
{
(*fn_ptr)();
fn_ptr ++;
}
}
以上代码源自RTT bsp,以及1楼参考的文章。
成功的工程:ITX_190_HWA.zip
继续描述场景
(1)优化等级-O0
(2)不勾选One ELF Section per Function
编译后,想要自动调用的宏还是没有被调用。
因为是公司代码,不方便上传,先上次map文件:
CMSIS-RTOS2-free_map.zip
如果大神们不好诊断,明天,我可以用其他单板实现这个框架,发代码出来。
编译环境:Keil 5
(1)map文件关键信息
__rt_init_rti_start 0x08011ce0 Data 4 hw_action.o(.rti_fn.0)
__rt_init_rti_end 0x08011ce4 Data 4 hw_action.o(.rti_fn.7)
hw_action.o(.text) refers to hw_action.o(.rti_fn.0) for __rt_init_rti_start
hw_action.o(.text) refers to hw_action.o(.rti_fn.7) for __rt_init_rti_end
hw_action.o(.rti_fn.0) refers to hw_action.o(.text) for rti_start
hw_action.o(.rti_fn.7) refers to hw_action.o(.text) for rti_end
hw_dev_led.o(.rti_fn.4) refers to hw_dev_led.o(.text) for register_hw_dev_led
Removing hw_dev_led.o(.rti_fn.4), (4 bytes).
0x08011ce0 0x08011ce0 0x00000004 Data RO 2106 .rti_fn.0 hw_action.o
0x08011ce4 0x08011ce4 0x00000004 Data RO 2107 .rti_fn.7 hw_action.o
如题,我写了个简单的设备管理器,
每个设备,写完之后,应该有个注册函数,
我想让这个注册函数自动被管理器调用。
参考到了RTT的宏,指定这些函数在某一个特定的代码段,
按顺序排列起来,然后从头到尾,逐个调用。
#define INIT_EXPORT(fn, level) \
const init_fn_t __rt_init_##fn RT_SECTION(".rti_fn."level) = fn
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
#define INIT_CPU_EXPORT(fn) INIT_EXPORT(fn, "2")
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
#define INIT_FS_EXPORT(fn) INIT_EXPORT(fn, "5")
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
参考这篇文章: RT-Thread 学习笔记(七)
我从中抄袭了这些代码
(1)头文件代码
/*自动初始化模块*/
#define RT_SECTION(x) __attribute__((section(x)))
#define RT_UNUSED __attribute__((unused))
#define RT_USED __attribute__((used))
typedef int (*init_fn_t)(void);
#define INIT_EXPORT(fn, level) \
const init_fn_t __rt_init_##fn RT_SECTION(".rti_fn."level) = fn
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
#define INIT_CPU_EXPORT(fn) INIT_EXPORT(fn, "2")
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
#define INIT_FS_EXPORT(fn) INIT_EXPORT(fn, "5")
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
(2)c文件代码
static int rti_start(void)
{
return 0;
}
INIT_EXPORT(rti_start, "0");
static int rti_end(void)
{
return 0;
}
INIT_EXPORT(rti_end,"7");
void rt_components_init(void)
{
const init_fn_t* fn_ptr;
for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_end; )
{
(*fn_ptr)();
fn_ptr ++;
}
}
(3)调用:
①初始化设备管理器的时候,调用rt_components_init()函数,
②修饰注册函数
INIT_COMPONENT_EXPORT(register_hw_dev_led);
(4)串口打印,没有看到register_hw_dev_led()函数的调用打印。
板子已经发出去制作,但是还没测试。
板子尺寸:10cm*10cm
板载资源:
(1)F1C100s 408MHz 32MB DDR
(2)引出CVBS/TV out 带莲花座
(3)SPI Flash X2
(都挂载在SPI0上,SPI Flash0 是系统存储器,SPI Flash1焊盘兼容Nand Flash)
(4)引出HP接口,带耳机座
(5)板载功放芯片,可接喇叭
(6)板载MIC模块
(7)板载SD卡座
(8)板载JTAG接口,
板载UART0接口(PE0 PE1)
复位按键,Flash Boot按键,方便调试
(9)板载LCD 接口,带背光电路
FPC40P 电阻屏+ DC2-40P牛角座
(10)板载USB Host座,同时带XH2.54座,方便扩展。
(11)板子DC接口,可12V供电,并引出12V XH2.54座子。
(12)CH579M 30MHz, RAM 20KB/Flash 256KB
(通过SPI1与F1C100s连接,目的是扩展串口,以太网透传)
a.扩展以太网接口 10Mbps
b.扩展4串口
c.引出SWD调试口,方便调试。
d.引出USB Host接口
板子还没测试,程序也没有调试,先上图,各位大神也可以过来观摩观摩!
画板子不专业,希望各位大哥可以指点指点!
板子资源:
调试屏底板V2_线位图.pdf
调试屏底板V2_原理图.pdf
调试屏底板V2_whycan打包_20211220-1235.zip
注意,很多地方我没打丝印标识,兄弟们可以下载文件,自行打丝印!
注意,很多地方我没打丝印标识,兄弟们可以下载文件,自行打丝印!
注意,很多地方我没打丝印标识,兄弟们可以下载文件,自行打丝印!
兄弟们,看看我的帖子,
https://whycan.com/t_3138.html
里面的流程,内容,
完全是晕哥帮忙搞出来的。
晕哥帮忙解决了问题,
还给了这么多的积分,真的很厚道。
个人认为,对于咱们搞电子/嵌入式的职业,少说都有6K/8k起步吧,
真正能解决问题的资源,不说几十块,几百块,甚至几千块都值得!
白P可耻!!白P可耻!!白P可耻!!
居然还在吵,说实话,别人卖服务的,就值这个价。
我们换个角度讲,你搞了个linux开发板,卖200块。
我买了你的开发板,而我水平又比较差,我天天捣鼓。
一有不懂我就艾特你!!!
第一天,我私聊你:环境怎么装?
第二天,我私聊你:怎么编译内核,怎么编译设备树,怎么......
......
过阵子我私聊你:你这个系统接wifi网络不稳定,是不是有问题?
又过阵子我私聊你:你这个系统使用USB接4G模块没反应,是不是有问题?
......
......
那么请你告诉我,直到我把产品做出来,技术支持耗掉多少工时?200块够发工资吗?
我耗掉了多少工时,我老板这个时间段给我发的工资要多少?项目是不是已经逾期了?
这个时候,有一家开发板厂商,他已经把这个东西都捣鼓好了,我只需要写业务代码就好了,一两天上手。
但是,这个开发板有点贵,1800元!!!
天啊,不可想象,居然是我半个月工资了!!!
......
抱歉,兄弟们,我编不下去了,我还要上班......
总得来说,你们懂的......
电脑显示正常吗?
请教了多位大神,借鉴了资料:
https://blog.csdn.net/zottffssent123/article/details/114636842
测试完后,确认是编码问题。
修改为GBK文件就可以了。
因为CH552G买不到,使用了CH554,使用WCH工具,通过USB线下载了楼主的各个版本程序,都识别不到设备,(未知设备都没有)。以下是我的电路图纸:
DAP_link_Project.zip
http://103.47.82.49/share/DAP_link_Project.zip
麻烦各位大哥看看。
从dotnet core 2.1起,官方就发布了关于arm的支持,但是不是支持ARMv6以及更早的 CPU 指令集版本,而V3S的cortex-A7是可以运行的,所以在论坛里找了一下,发现有一个帖子进行了尝试,但是遇到了ICU的问题就没有下文了,这里我分享一下我的过程。
1、编译V3S的buildroot,用的晕哥的配置,烧录到V3S后,确保可以正常运行。
2、去官网下载dotnet core 的对应版本https://dotnet.microsoft.com/ ,我这里下的是5.0的最新版本。
3、解压下载的包,拷贝到一张TF卡上,因为上百M的文件无法放入spiflash内。
4、在本地电脑上预先编译发布一个可以在arm上运行的应用程序,可以下载我的helloworld.
5、拷贝publish文件到TF卡内。
6、插入TF卡,给V3S上电。
7、挂载TF卡,mount /dev/mmcblk0 /mnt/mmc
8、运行一下dotnet --info,如果报ICU找不到的错,再运行一下export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
9、cd到对应目录,执行./publish/hello_world。# ./publish/hello_world Hello World! 你好世界!
附件:
publish.zip
buildroot-2018.08.2-v3s.zipICU问题解决链接:
https://github.com/dotnet/core/issues/2186#issuecomment-671105420
哈哈,我被ICU困住了
Hi everyone ,
I design a new PCB with reference of lichee Pi Schematic, but I removed SPI Flash as I don't need to boot from SPI Flash.
After soldering board I can't see signal on SDClock, But can see on SPI Flash Clock.Unfortunately I also not reserve usb connector, as my idea was to flash sdcard pc side and boot the board.
Crystal is oscillating.
So with default boot form F1C100s it will not attempt to boot from SD ?
Any suggestion how to move forward ?If no option, possibly I need to make new pcb with USB & SPI Flash.
嗨,大家好,
我参考lichee Pi Schematic设计了一个新的PCB,但由于不需要从SPI Flash引导,因此删除了SPI Flash。
焊完板后,我在SDClock上看不到信号,但在SPI闪存时钟上却看到了。不幸的是,我也不保留USB连接器,因为我的想法是在sdcard pc侧进行闪存并启动开发板。
晶体振荡。
因此,使用默认引导形式F1C100s,它将不会尝试从SD引导吗?
任何建议如何前进?如果没有选择,可能我需要使用USB和SPI Flash制作新的PCB
Thanks & Regards
PV
如果我没记错的话,是优先从SD卡启动的。是不是SD卡中没有正确的引导程序呢?