实验五 Flash在线编程实验

一.实验目的

(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发送擦除、写入、读取命令及参数:

思路分析:

  1. 通过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。
  2. 通过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

思路分析:

  1. 查询数据传的位置的本质,其实就是给组帧添加一种查找的格式。
  2. 查找算法:先获取起始扇区,并将其值存到sect变量中,当其值小于结束扇区号时,初始化偏移量为0,偏移量从0开始增加到1024-count(待查询字节数),然后在循环中将字符串与查找到的字符串进行一一对比,如果相同则向串口发送FOUND,否则发送NOTFOUND
  3. R:[4]查询起始扇区号+[5]查询结束扇区号+[6]查询字节数m+[7]~[(6+m)]查询字符串内容
  4. 设计Q的数据格式:
  5. 根据钱一个实验已经明确了原程序的组帧格式,我将帧格式的第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,取出之后,解析字符串没有成功,所以这里只写出分析思路和部分实现代码。

思路分析:

  1. 连接数据库之后,点击按钮,将数据库的数据获取之后,存到DataSet;
  2. 将DataSet中的数据显示在dataGridView1上;
  3. 点击存入按钮,获取数据,将DataSet数据转化为json格式的字符串,将该字符串分为若干份,分别发送至flash并存到flash相邻的扇区,并想C#程序返回字符串长度;
  4. 在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擦除、写入和读取的基本操作。但是在具体实验的过程中还是遇到许多难题,其中有一些解决了,但是还有一些没有得到完全解决。在后面的时间,我会继续寻找这些问题的解决方法,让自己获得更大的提升。

嵌入式技术基础与实践(第4版)

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值