* 优化编译脚本,加快编译速度。
* 优化代码生成,大幅减小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的思路。
先看手册:


从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
麻烦各位大哥看看。