一.实验目的
(1)掌握 Flash 存储器在线编程的基本概念。。
(2)熟悉 Flash 存储器的在线编程擦除和写入的步骤。
(3)进一步深入理解 MCU 和 C#串口通信的编程方法。
二.实验内容
1.验证性实验
1)验证样例程序(ch09-Flash)中(Flash),主要功能是实现通过串口输入命令 “?”可获得帮助; E:擦除第 50 扇区;R:从 50 扇区 0 字节开始的地址读取 30 字节长度字符串;A:从全局变量 watchGlobalVar 地址直接读取该变量;W:向 50 扇区 0 字节开始的地址写入 30 字节长度字符串;P:保护 50 扇区(实际保护 48,49,50,51 四个对齐扇区)。
实验步骤如下:
(1)将样例 Flash 程序下载至目标板;
(2)将“TTL-USB 串口线”的“USB 端口”接 PC 机的 USB 口,串口线的串口接开发板上的串口 2(3 根,RX 接蓝线,TX 接白线,GND 接黑线);
(3)打开串口调试工具或 ch06-UART 文件夹中的“C#2010 串口测试程序”进行串口通信测试,观察实验现象。
(4)分析理解 main.c 程序和中断服务例程 isr.c。
2.设计性实验
(1)复制样例程序(ch09-Flash)中(Flash),利用该程序框架实现:通过串口调试工具或 ch06-UART 文件夹中的“C#2010 串口测试程序”发送擦除、写入、读取命令及其参数,参数能够设置扇区号(0-127)、写入/读取扇区内部偏移地址(0~1020,要求为 0,4,8,12,......);写入/读取字节数目(4~1024,要求为 4,8,12,......)和数据。
请在实验报告中给出 MCU 端程序 main.c 和 isr.c 流程图及程序语句。
(2)复制样例程序(ch09-Flash)中(Flash),利用该程序框架实现:通过串口调试工具或 ch06-UART 文件夹中的“C#2010 串口测试程序”发送查询 Flash 相应扇区的数据串,(如:50 100 Hello,查找扇区 50 至扇区 100 中的字符串 Hello),如果找到发送 Found 给串口,如果未找到发送 Not Found 给串口。
请在实验报告中给出 MCU 端程序 main.c 和 isr.c 流程图及程序语句。
3.进阶实验★
(1)复制样例程序(ch09-Flash)中(Flash),利用该程序框架实现:通过 C#程序发送擦除、写入、读取命令及其参数,参数能够设置扇区号(0-127)、写入/读取扇区内部偏移地址(0~1020,要求为 0,4,8,12,......);写入/读取字节数目(4~1024,要求为 4,8,12,......)和数据。C#界面设计如如图 1 所示。
请在实验报告中给出 MCU 端程序 main.c 和 isr.c 流程图及程序语句和 C#方主要程序段。
图 1 C#界面设计
(2)复制样例程序(ch09-Flash)中(Flash),利用该程序框架实现:通过 C#程序访问数据库中的表,读取表中存放的数据列表并显示(表中字段如:学号,姓名,成绩),通过发送按钮将数据列表保存至 Flash 中;通过 C#程序界面输入读取的数据列表的条数,从 Flash中读取相应的数据后显示。
请在实验报告中给出 MCU 端程序 main.c 和 isr.c 流程图及程序语句和 C#方主要程序段。
(3)复制样例程序(ch09-Flash)中(Flash),利用该程序框架实现:通过 C#程序打开一副图片(比如自己的一寸电子照片),通过串口将图片数据发送至 MCU 并保存 Flash 中;通过 C#程序可以将 MCU 的 Flash 中保存的图片读取并显示。
请在实验报告中给出 MCU 端程序 main.c 和 isr.c 流程图及程序语句和 C#方主要程序段。
三.实验步骤和结果
1.验证性实验
组帧格式分析:
组帧:[0]P+[1]数据字节数n+数据+[n+2]C
数据部分:[2]握手或数据命令(C-握手/D-flash操作)+[3]flash操作类型(R:按逻辑地址读取;A:按物理地址读取;W:写入操作;E:擦除操作;P:保护操作;)+该操作类型的数据
R:[4]扇区号+[5]偏移量高字节+[6]偏移量低字节+[7]读取字节数
A:[4]地址高位+[5]地址第二高位+[6]地址第二低位+[7]地址低位+[8]读取字节数
W:[4]扇区号+[5]偏移量高字节+[6]偏移量低字节+[7]写入字节数m+写入数据
E:[4]扇区号
P:[4]保护区域号
2.设计性实验
(1)通过串口调试助手,向flash发送擦除、写入、读取命令及参数:
思路分析:
- 通过C#串口调试工具向串口发送擦除指令:50 03 44 45 32 43(擦除50号扇区),正确返回结果:4D 21 43 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 55。
- 通过C#串口调试工具向串口发送写入指令:
50,0C,44,57,32,00,00,06,41,42,43,44,45,46,43(向50号扇区00偏移量写ABCDEF),正确返回结果:4D 07 43 41 42 43 44 45 46 55
3.通过C#串口调试工具向串口发送读出指令:50 06 44 52 32 00 00 0A 43(从50号扇区00偏移量读ABCDEF),正确返回结果:4D 07 43 41 42 43 44 45 46 55 。
流程图:
串口流程图
Main函数流程图:
具体代码如下:
串口程序:
void UART2_IRQHandler(void)
{
uint_8 i,flag;
DISABLE_INTERRUPTS; //关总中断
//-----------------------------------------------------------------------
i=uart_re1(UART_2, &flag); //接收一个字节
if (1 == flag)
{
uart_send1(UART_2, i);
}
//调用内部函数CreateFrame进行组帧
if(CreateFrame(i,g_uart_recvBuf)!=0) //组帧成功
{
//g_uart_recvBuf[2]是命令字节
if(g_uart_recvBuf[2] == 'C') //握手命令
{
g_uart_sentBuf[0]=(uint_8)'M'; //
g_uart_sentBuf[1]=5; //
g_uart_sentBuf[2] = (uint_8)'C'; //
g_uart_sentBuf[3] = (uint_8)'K'; //"PCNode"
g_uart_sentBuf[4] = (uint_8)'L';
g_uart_sentBuf[5] = (uint_8)'2';
g_uart_sentBuf[6] = (uint_8)'5';
g_uart_sentBuf[7] = (uint_8)'U'; //
uart_sendN(UART_2,8,g_uart_sentBuf);
framestate=1;//握手命令帧
}
//
else //其他情况,置“串口0接收完整数据帧事件位(EVENT_UART0_RE)”,触发task_uart0_re任务处理
{
framestate=0;//非握手命令帧
}
}
//-----------------------------------------------------------------------
ENABLE_INTERRUPTS; //开总中断
}
(2)在flash查找存入数据,如果找到想串口发送FOUND,否则发送NOTFOUND
思路分析:
- 查询数据传的位置的本质,其实就是给组帧添加一种查找的格式。
- 查找算法:先获取起始扇区,并将其值存到sect变量中,当其值小于结束扇区号时,初始化偏移量为0,偏移量从0开始增加到1024-count(待查询字节数),然后在循环中将字符串与查找到的字符串进行一一对比,如果相同则向串口发送FOUND,否则发送NOTFOUND
- R:[4]查询起始扇区号+[5]查询结束扇区号+[6]查询字节数m+[7]~[(6+m)]查询字符串内容
- 设计Q的数据格式:
- 根据钱一个实验已经明确了原程序的组帧格式,我将帧格式的第4位,即[3]flash操作类型增加一种查询类型Q
流程图:
具体代码如下:
case 'Q':
offset = 0; //偏移地址
uint_8 sect = g_uart_recvBuf[4];
uint_8 i = 0;
count = g_uart_recvBuf[6]; //存取字节数
uint_8 g_query[count];
uint_8 flag = 0;
while (sect < g_uart_recvBuf[5]) {
offset = 0; //偏移地址
for (offset; offset < 1024 - count; offset++) {
flash_read_logic(&g_query[0], sect, offset, count);
for (; i < count; i++) {
if (g_query[i] == g_uart_recvBuf[i + 7]) {
if (i >= count - 1) {
g_uart_sentBuf[0] = (uint_8) 'M'; //帧头
g_uart_sentBuf[1] = 7; //内容数(内容count+C)
g_uart_sentBuf[2] = (uint_8) 'C'; //内容起始帧
g_uart_sentBuf[3] = (uint_8) 'F';
g_uart_sentBuf[4] = (uint_8) 'O';
g_uart_sentBuf[5] = (uint_8) 'U';
g_uart_sentBuf[6] = (uint_8) 'N';
g_uart_sentBuf[7] = (uint_8) 'D';
g_uart_sentBuf[8] = (uint_8) 'U'; //帧尾
uart_sendN(UART_2, 9,&g_uart_sentBuf[0]);
flag = 1;
}
} else {
i = 0;
break;
}
}
if (flag)
break;
}
if (flag)
break;
sect++;
}
if (!flag) {
g_uart_sentBuf[0] = (uint_8) 'M'; //帧头
g_uart_sentBuf[1] = 9; //内容数(内容count+C)
g_uart_sentBuf[2] = (uint_8) 'C'; //内容起始帧
g_uart_sentBuf[3] = (uint_8) 'N';
g_uart_sentBuf[4] = (uint_8) 'O';
g_uart_sentBuf[5] = (uint_8) 'T';
g_uart_sentBuf[6] = (uint_8) 'F';
g_uart_sentBuf[7] = (uint_8) 'O';
g_uart_sentBuf[8] = (uint_8) 'U';
g_uart_sentBuf[9] = (uint_8) 'N';
g_uart_sentBuf[10] = (uint_8) 'D';
g_uart_sentBuf[11] = (uint_8) 'U'; //帧尾
uart_sendN(UART_2, 12, &g_uart_sentBuf[0]);
}
break;
3.进阶实验★
(1)通过C#程序实现对flash的存、取和擦除等功能
由于已有完整的C#程序,我便通过此程序实验了一些flash基本操作,并且仔细阅读同时理解了具体实现代码。
(2)通过C#程序访问数据库中的表,读取表中的存放的数据并显示通过发送按钮发送并保存到flash中,通过C#程序读取数据并显示。
由于此次实验过程并不是很顺利,将数据库中的数据以字符串的形式存入flash,取出之后,解析字符串没有成功,所以这里只写出分析思路和部分实现代码。
思路分析:
- 连接数据库之后,点击按钮,将数据库的数据获取之后,存到DataSet;
- 将DataSet中的数据显示在dataGridView1上;
- 点击存入按钮,获取数据,将DataSet数据转化为json格式的字符串,将该字符串分为若干份,分别发送至flash并存到flash相邻的扇区,并想C#程序返回字符串长度;
- 在C#界面输入查询的起始扇区和字符串长度,点击查询按钮,从先前存放数据的几个扇区中取出json数据,并将json转换为DataSet数据,并显示到dataGridView2上。
主要代码:
C#端:
//读取数据库
private void button1_Click(object sender, EventArgs e)
{
// sql a = new sql();
//a.Select();
MySqlConnection myconn = null;
MySqlCommand mycom = null;
// MySqlDataAdapter myrec = null;
myconn = new MySqlConnection("server=localhost;user id=root;password=admin;database=armcortex");
// Host =localhost;Database=student;Username=lemon;Password=123
myconn.Open();
mycom = myconn.CreateCommand();
mycom.CommandText = "SELECT *FROM stuinfo";
MySqlDataAdapter adap = new MySqlDataAdapter(mycom);
DataSet ds = new DataSet();
adap.Fill(ds);
dataGridView1.DataSource = ds.Tables[0].DefaultView;
myconn.Close();
}
//将数据库存入flash
private void button2_Click(object sender, EventArgs e)
{
// 1. 临时变量声明
byte sector; //扇区号
int offset; //偏移量
byte n; //写入字节数
int i;
byte len;
//获取数据库数据
MySqlConnection myconn = null;
MySqlCommand mycom = null;
//MySqlDataAdapter myrec = null;
myconn = new MySqlConnection("server=localhost;user id=root;password=admin;database=armcortex");
// Host =localhost;Database=student;Username=lemon;Password=123
myconn.Open();
mycom = myconn.CreateCommand();
mycom.CommandText = "SELECT *FROM stuinfo";
MySqlDataAdapter adap = new MySqlDataAdapter(mycom);
DataSet ds = new DataSet();
adap.Fill(ds);
//dataGridView1.DataSource = ds.Tables[0].DefaultView;
byte[] writeDataArray = System.Text.Encoding.Default.GetBytes(DatasetToJson(ds)); //String转化为Byte[]
// 2. 临时变量赋值
sector = Convert.ToByte(tbSector2.Text); // 将TextBox中的Text转换为Byte类型
offset = Convert.ToInt32(tbOffset2.Text); // 将TextBox中的Text转换为Byte类型
n = (byte)writeDataArray.Length;
byte j = 0,k=0,l=0,m=0;
if (n > 56)
{
if (n % 56 != 0)
{
l = (byte)(n % 56);
j = (byte)(n / 56 + 1);
}
else
{
j = (byte)(n / 56);
l = 56;
}
//n = (byte)(n + 6);
for (; k < j; k++)
{
if (k == j - 1)
{
len = (byte)(l + 6);
m = l;
}
else
{
len = (byte)(56 + 6);
m = 56;
}
byte[] SendByteArray = new byte[len + 3];
this.Txt_recv1.Text = "";
this.Txt_recv2.Text = "";
this.Txt_recv1.Refresh();
this.Txt_recv2.Refresh();
this.tb_statue.Text = "运行状态:" + "单击“KL25 Flash 写测试”按钮...";
this.tb_statue.Refresh();
try
{
SendByteArray[0] = (byte)'P'; //帧头
SendByteArray[1] = len; //帧长
SendByteArray[2] = (byte)'D'; //FLASH操作命令
SendByteArray[3] = (byte)'W'; //FLASH写操作
SendByteArray[4] = (byte)(sector+(byte)k); //扇区号
SendByteArray[5] = (byte)(offset / 256); //偏移量高字节
SendByteArray[6] = (byte)(offset % 256); //偏移量低字节
SendByteArray[7] = m; //字节数
for (i = 8; i <= 7 + m; i++)
{
SendByteArray[i] = writeDataArray[k* 56 + i - 8];
}
SendByteArray[len + 2] = (byte)'C'; //帧尾
//发送、接收、显示
sendRecv(SendByteArray, 2, 100);
}
catch
{
this.Txt_recv1.Text = "KL25 Flash第"+(k+1)+"次写入操作失败!";
}
}
}
this.textBox4.Text = n + "字节";
}
//修改读出程序
private string sendRecvDB(byte[] SendByteArray, int cnt, int time)
{
string result="";
// string str="";
byte[] recvData;
int i;
this.tb_statue.Text += "正在发送...";
this.tb_statue.Refresh();
recvData = pcNode1.PCNodeSendReturn(SendByteArray, cnt, time);
if (recvData.Length <= 0)
{
this.tb_statue.Text += "无数据返回";
this.tb_statue.Refresh();
}
else
{
this.tb_statue.Text += "有数据返回";
this.tb_statue.Refresh();
this.Txt_recv1.Text = string.Empty;
for (i = 0; i < recvData.Length; i++)
this.Txt_recv1.Text += recvData[i].ToString("X2").ToUpper() + " ";
this.tbhead.Text = "" + (char)recvData[0];//帧头
this.tblen.Text = "" + ((char)recvData[1] - 1);//长度-1(内容起始帧C)
this.tbfirst.Text = "" + (char)recvData[2];//内容起始帧C
//this.Txt_recv2.Text = "帧头:" + (char)recvData[0] + " 接收长度:" + (recvData[1]-1) + " 接收起始字符帧:" + (char)recvData[2] + " 接收内容:";
for (i = 3; i < recvData.Length - 1; i++)
{
//可显示字符予以显示,不可显示的ASCII码显示为空
if (recvData[i] > 31 && recvData[i] < 127)
{
this.Txt_recv2.Text += (char)recvData[i];
}
else
{
this.Txt_recv2.Text += " ";
}
}
this.tbtail.Text = "" + (char)recvData[i];//帧尾
result = System.Text.Encoding.Default.GetString (recvData);
result = result.Substring(1, result.Length - 1);
result = result.Substring(1, result.Length - 1);
result = result.Substring(1, result.Length - 1);
return result.Substring(0, result.Length - 2);
// str = result;
//return result;
}
return result;
}
//查询获取flash中数据库的数据
private void button3_Click(object sender, EventArgs e)
{
// 1. 临时变量声明
byte sector; // 扇区号
int offset; // 偏移量
byte count; // 读取字节数
StringBuilder sb = new StringBuilder();
byte[] SendByteArray = new byte[9]; // 定义发送缓冲区
byte offset_high, offset_low; // 定义偏移地址的高低字节
// 2. 临时变量赋值
sector = Convert.ToByte(tbSector1.Text); // 将TextBox中的Text转换为Byte类型
offset = Convert.ToInt32(tbOffset1.Text); // 将TextBox中的Text转换为Byte类型
count = Convert.ToByte(tbCount1.Text);
// 计算偏移地址高低位
offset_low = (byte)(0xff & offset);
offset_high = (byte)((0xff00 & offset) >> 8);
byte j = 0, k = 0, l = 0, m = 0;
if (count > 56)
{
if (count % 56 != 0)
{
l = (byte)(count % 56);
j = (byte)(count / 56 + 1);
}
else
{
j = (byte)(count / 56);
l = 56;
}
//n = (byte)(n + 6);
for (; k < j; k++)
{
if (k == j - 1)
{
m = l;
}
else
{
m = 56;
}
this.Txt_recv1.Text = "";
this.Txt_recv2.Text = "";
this.Txt_recv1.Refresh();
this.Txt_recv2.Refresh();
this.tb_statue.Text = "运行状态:" + "单击“KL25 Flash 按逻辑地址读测试”按钮...";
this.tb_statue.Refresh();
try
{
SendByteArray[0] = (byte)'P'; //帧头
SendByteArray[1] = 0x06; //帧长
SendByteArray[2] = (byte)'D'; //FLASH操作命令
SendByteArray[3] = (byte)'R'; //FLASH读操作
SendByteArray[4] = (byte)(sector + (byte)k); //第几页
SendByteArray[5] = offset_high; //偏移量高字节
SendByteArray[6] = offset_low; //偏移量低字节
SendByteArray[7] = m; //读取字节数
SendByteArray[8] = (byte)'C'; //帧尾
//发送、接收、显示
//str += sendRecvDB(SendByteArray, 2, 100);
sb.Append(sendRecvDB(SendByteArray, 2, 100));
}
catch
{
this.Txt_recv1.Text = "KL25 Flash第"+(k+1)+"读取操作失败!";
}
}
if (sb != null&& !sb.Equals(""))
{
DataSet ds = (DataSet)JsonToDataSet(sb.ToString());
dataGridView2.DataSource = ds.Tables[0].DefaultView;
}else
{
this.Txt_recv1.Text = "KL25 Flash读取操作失败!";
}
}
else
{
}
}
四.实验总结(需加入心得体会)
通过本次实验,我掌握了flash在线编程的基本概念,熟悉了flash擦除、写入和读取的基本操作。但是在具体实验的过程中还是遇到许多难题,其中有一些解决了,但是还有一些没有得到完全解决。在后面的时间,我会继续寻找这些问题的解决方法,让自己获得更大的提升。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)