打印
[MM32软件]

【EV Board (MM32L0136C7P)测评】+通过串口shell终端启动ymodem完成文件收发处理

[复制链接]
1199|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
coslight|  楼主 | 2022-11-30 15:57 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
通过串口shell终端启动ymodem完成文件收发处理
本测试为开发板测试项目的第一步,即利用串口实现一个串口终端,通过终端启动用户控制应用,利用基于串口ymodem完成文件收发控制,并完成数据的spi flash读写操作。
1、串口shell的移植
1.1 硬件连接

通过硬件原理图可见,在SWD连接端子的4和6分别连接了UART2的收发引脚PA2,PA3,我们就利用这个串口完成余下的测试。
1.2 letter shell的移植
1) letter shell 介绍

letter shell是一个C语言编写的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式设备,以C语言函数为运行单位,可以通过命令行调用,运行程序中的函数。
功能强大,具有:
  • 命令自动补全
  • 快捷键功能定义
  • 命令权限管理
  • 用户管理
  • 变量支持
  • 代理函数和参数代理解析

下载源码,地址:https://gitee.com/zhang-ge/letter-shell?_from=gitee_search
2) 源码移植
将下载的源码目录中src目录下的文件添加到工程中,

并新建一个文件,命名为shell_port.c和shell_port.h,来完成驱动移植。
shell_port.c可以找基于stm32的例程来修改,主要需要完成如下几个函数的移植。
  • 串口写驱动的移植

/**

* [url=home.php?mod=space&uid=247401]@brief[/url] 用户shell写

*

* @param data 数据

* @param len 数据长度

*

* [url=home.php?mod=space&uid=266161]@return[/url] unsigned short 写入实际长度

*/

short userShellWrite(char *data, unsigned short len)

{

signed short i;

for(i=0;i<len;i++)

{

while ( 0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(SHELL_UART)) )

{}

UART_PutData(SHELL_UART, (uint8_t)(data[i]));

}

return len;

}


  • 串口读函数的移植

      利用了中断配合环形缓冲区的方式来完成串口中断接收.
      简易环形缓冲区定义
#define SHELLRXBUF_MAX50
struct ringbuf{
char buf[SHELLRXBUF_MAX];
unsigned char head,tail;
};

static struct ringbuf shellrx;
void fill_rx_ringbuf(char data)
{
shellrx.buf[shellrx.head] = data;
shellrx.head ++;
if(shellrx.head >= SHELLRXBUF_MAX)
shellrx.head = 0;
}
串口中断接收函数,在中断中完成环形缓冲区的填充。
/* BOARD_DEBUG_UART_IRQHandler ISR entry. */
void BOARD_DEBUG_UART_IRQHandler(void)
{
if ( (0u != (UART_INT_RX_DONE & UART_GetEnabledInterrupts(BOARD_DEBUG_UART_PORT)))
&& (0u != (UART_INT_RX_DONE & UART_GetInterruptStatus(BOARD_DEBUG_UART_PORT))) )
{
fill_rx_ringbuf((char)UART_GetData(BOARD_DEBUG_UART_PORT));
}
}

shell串口读函数的实现
/**
* @brief 用户shell读
*
* @param data 数据
* @param len 数据长度
*
* @return unsigned short 读取实际长度
*/
signed short userShellRead(char *data, unsigned short len)
{
unsigned short i = 0;
for( i=0; i < len; i++)
{
if(shellrx.head != shellrx.tail)
{
data[i] = shellrx.buf[shellrx.tail];
shellrx.tail++;
if(shellrx.tail >= SHELLRXBUF_MAX)
shellrx.tail = 0;
}
else
{
break;
}
}
return i;
}

3)shell的使用
    在应用程序初始化的时候,首先初始化串口,然后必须调用函数userShellInit()完成shell终端的初始化工作。因为我们将来还要支持串口的ymodem,所以不直接使用中端方式驱动shell,而是在主循环中轮循函数shellTask(),完成shell的调度任务。
2、spi flash读写操作
2.1 硬件连接


spi flash占用spi2外设,使用PB12,PB13,PB14,PB15四个引脚。
2.2 利用sfud来管理spi flash
SFUD (Serial Flash Universal Driver)是一款开源的串行 SPI Flash 通用驱动库。SFUD使用的是SFDP标准,它是 JEDEC (固态技术协会)制定的串行 Flash 功能的参数表标准。该标准规定每个 Flash 中存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。
线路板上焊接的是ZD25WQ80,8Mbit串行flash。
sfud驱动利用官方提供的例程,擦除,写和读都非常流畅。
这里需要注意一点,由于spi flash引脚和slcd引脚有公用部分,我们必须将SW3和SW4两个开关拨到右边,使能spi flash引脚的连接关系。
3、ymodem文件读写
Ymodem是Xmodem的改进版协议,具有传输快速稳定的优点。它可以一次传输1024字节的信息块,同时还支持传输多个文件。平常所说的Ymodem协议是指的Ymodem-1K。
YModem-1K用1024字节信息块传输取代标准的128字节传输,数据的发送回使用CRC校验,保证数据传输的正确性。它每传输一个信息块数据时,就会等待接收端回应ACK信号,接收到回应后,才会继续传输下一个信息块,保证数据已经全部接收。
YModem协议在嵌入式系统中使用官方,占用系统资源少,上位机工具免费且非常的多。多用于在bootloader中完成应用程序更新。
ymodem协议栈网络上有开源代码,大家可以自己去网络上下载。
ymodem系统的移植需要完成如下几个函数。
/**
  * @brief  Test to see if a key has been pressed on the HyperTerminal
  * @param  key: The key pressed
  * @retval 1: Correct
  *         0: Error
  */
uint32_t SerialKeyPressed(uint8_t *key)
{
        if(shellrx.head != shellrx.tail)
        {
                *key = shellrx.buf[shellrx.tail];
                shellrx.tail++;
                if(shellrx.tail >= SHELLRXBUF_MAX)
                        shellrx.tail = 0;
                return 1;
        }
        else
        {
                return 0;
        }
}
/**
  * @brief  Print a character on the HyperTerminal
  * @param  c: The character to be printed
  * @retval None
  */
void SerialPutChar(uint8_t c)
{
        while ( 0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(SHELL_UART)) )
    {}
    UART_PutData(SHELL_UART, c);
}


4、实测效果
通过shell终端,我设置了spi flash的擦除、读写测试函数。文件下载、文件上传等几个函数,我们依次测试一下效果。
int flash_chiperase(int argc, char *agrv[])
{
        shellPrint(&shell, "erash chip spiflash\n\r");
        sfud_err err = sfud_chip_erase(sfud_get_device(SFUD_SPI_DEVICE_INDEX));
        if(err == SFUD_SUCCESS)
        {
                shellPrint(&shell, "erash chip success\n\r");
        }
        else
        {
                shellPrint(&shell, "erash chip faild, err=%d\n\r",err);
        }
        return 0;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), flash_chiperase, flash_chiperase, flash_chiperase);
int flash_wr(int argc, char *argv[])
{
        shellPrint(&shell, "write and read spiflash\n\r");
        if(argc < 2 )
        {
                shellPrint(&shell, "argc less than 2, please input flash w/r num\n\r");
        }
        sfud_err err;
        sector_index = (uint32_t)atoi(argv[2]);
        page_index = (uint32_t)atoi(argv[3]);
        if(strcmp(argv[1], "w") == 0)        //write
        {
                int i;
                for(i=0;i<APP_SFUD_PAGE_SIZE;i++)
                        tab_1024[i] = i;
                /* sfud write. */
                err = sfud_write(sfud_get_device(SFUD_SPI_DEVICE_INDEX), sector_index * APP_SFUD_SECTOR_SIZE + page_index * APP_SFUD_PAGE_SIZE, APP_SFUD_PAGE_SIZE, tab_1024);
                if(err == SFUD_SUCCESS)
                {
                        shellPrint(&shell, "flash write success, sector = %d page = %d\n\r",sector_index,page_index);
                }
                else
                {
                        shellPrint(&shell, "flash write faild, err=%d\n\r",err);
                }
        }
        else if(strcmp(argv[1], "r") == 0)        //read
        {
                /* sfud read. */
                err = sfud_read(sfud_get_device(SFUD_SPI_DEVICE_INDEX), sector_index * APP_SFUD_SECTOR_SIZE + page_index * APP_SFUD_PAGE_SIZE, APP_SFUD_PAGE_SIZE, tab_1024);
                if(err == SFUD_SUCCESS)
                {
                        shellPrint(&shell, "flash read success, sector = %d page = %d\n\r",sector_index,page_index);

                        for(uint32_t i = 0u; i < APP_SFUD_PAGE_SIZE; i++)
                        {
                                if (0 == i % 16u)
                                {
                                        shellPrint(&shell,"\r\n");
                                }
                                shellPrint(&shell,"%02X ",tab_1024[i]);
                        }
                        shellPrint(&shell,"\r\n");
                }
                else
                {
                        shellPrint(&shell, "flash read faild, err=%d\n\r",err);
                }
        }
        return 0;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), flash_wr, flash_wr, flash_write and read);
int download(int argc, char *agrv[])
{
        uint8_t Number[10] = "          ";
        int32_t Size = 0;
        shellPrint(&shell, "download file by ymodem... ...\n\r");

        //        erase flash
        sfud_err err = sfud_chip_erase(sfud_get_device(SFUD_SPI_DEVICE_INDEX));
        if(err == SFUD_SUCCESS)
        {
                shellPrint(&shell, "erash chip success\n\r");
        }
        else
        {
                shellPrint(&shell, "erash chip faild, err=%d\n\r",err);
                return -1;
        }
       
        Size = Ymodem_Receive(&tab_1024[0]);
        if (Size > 0)
        {
                shellPrint(&shell, "\n\n\r Programming Completed Successfully!\n\r--------------------------------\r\n Name: ");
                shellPrint(&shell, (const char *)file_name);
                Int2Str(Number, Size);
                shellPrint(&shell, "\n\r Size: ");
                shellPrint(&shell, (const char *)Number);
                shellPrint(&shell, " Bytes\r\n");
                shellPrint(&shell, "-------------------\n");
        }
       
        return 0;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), download, download, recv a file from ymodem);
int upload(int argc, char *argv[])
{
        uint32_t status = 0;
        if(argc < 2 )
        {
                shellPrint(&shell, "argc less than 2, please input upload num\n\r");
                return -1;
        }
        int length = (uint32_t)atoi(argv[1]);
        shellPrint(&shell, "upload file by ymodem... ...\n\r");
        if (GetKey() == CRC16)
        {
                /* Transmit the flash image through ymodem protocol */
                status = Ymodem_Transmit((uint32_t)APP_SFUD_PAGE_SIZE, (const uint8_t*)"UploadedFlashImage.dat", length);

                if (status != 0)
                {
                  shellPrint(&shell, "\n\rError Occured while Transmitting File\n\r");
                }
                else
                {
                  shellPrint(&shell, "\n\rFile Trasmitted Successfully \n\r");
                }
        }
        else
        {
                shellPrint(&shell, "\r\n\nAborted by user.\n\r");  
        }

        return 0;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), upload, upload, read a file from ymodem);

shell终端启动画面

点击tab键后,shell终端的反映,我可以看到,终端中列出了所有支持的函数。其中就包含我们刚才添加的四个函数,flash_chiperase,flash_wr,download,upload。

flash_chiperase的执行效果:

flash读写函数测试,shell终端的自动提示和补全功能。

download下载测试,系统提示下载的文件名和长度。

执行 upload 5000 命令后,通过ymodem接收到5000字节的文件.实际对比后,上传文件内容是正确的.


使用特权

评论回复
沙发
xld0932| | 2022-11-30 17:01 | 只看该作者
XMODEM和YMODEM最大的区别就是,YMODEM可以一次性上传或下载多个文件,楼主这个功能有么?

使用特权

评论回复
板凳
xiaoqi976633690| | 2022-12-1 09:05 | 只看该作者
膜拜大佬.......

使用特权

评论回复
地板
coslight|  楼主 | 2022-12-1 10:10 | 只看该作者
xld0932 发表于 2022-11-30 17:01
XMODEM和YMODEM最大的区别就是,YMODEM可以一次性上传或下载多个文件,楼主这个功能有么? ...

没有,这个应用方式在本次测试中用不到,所以没有研究这么深

使用特权

评论回复
5
coslight|  楼主 | 2022-12-1 10:11 | 只看该作者

多谢支持,其实都是搬运工的角色

使用特权

评论回复
6
NOo02| | 2022-12-2 18:39 | 只看该作者
楼主给个源码感激不尽,593151490@qq.com

使用特权

评论回复
7
coslight|  楼主 | 2022-12-2 22:16 | 只看该作者
NOo02 发表于 2022-12-2 18:39
楼主给个源码感激不尽,

下一贴提供全部源代码

使用特权

评论回复
8
juliestephen| | 2022-12-5 22:33 | 只看该作者
这个文件ymodem软件在哪下载的

使用特权

评论回复
9
phoenixwhite| | 2022-12-5 22:41 | 只看该作者
这个功能可以,简直太棒了。              

使用特权

评论回复
10
coslight|  楼主 | 2022-12-6 12:27 | 只看该作者
juliestephen 发表于 2022-12-5 22:33
这个文件ymodem软件在哪下载的

我用的xshell,其它软件没有测试过

使用特权

评论回复
11
lzbf| | 2022-12-6 15:17 | 只看该作者
怎么把接收数据修改为中断呢?              

使用特权

评论回复
12
macpherson| | 2022-12-6 17:06 | 只看该作者
最大传输最大数据呢?              

使用特权

评论回复
13
ingramward| | 2022-12-6 17:57 | 只看该作者
串口shell真的非常的棒呢。              

使用特权

评论回复
14
averyleigh| | 2022-12-6 18:57 | 只看该作者
这个完整的工程文件在哪呢?              

使用特权

评论回复
15
coslight|  楼主 | 2022-12-7 08:24 | 只看该作者
averyleigh 发表于 2022-12-6 18:57
这个完整的工程文件在哪呢?

在我的另一个帖子里面:
https://bbs.21ic.com/forum.php?mod=viewthread&tid=3269906&page=1#pid13150962

使用特权

评论回复
16
jackcat| | 2022-12-9 22:10 | 只看该作者
ymodem的功能非常不错。              

使用特权

评论回复
17
wwppd| | 2022-12-9 22:51 | 只看该作者
都是使用的一个串口实现的吗?              

使用特权

评论回复
18
jackcat| | 2022-12-10 08:37 | 只看该作者
这个支持所有的串口吗?              

使用特权

评论回复
19
belindagraham| | 2022-12-10 09:22 | 只看该作者
shell怎么发送文件给下位机的?

使用特权

评论回复
20
minzisc| | 2022-12-10 10:03 | 只看该作者
这个是不是可以升级单片机的源代码?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

61

主题

928

帖子

5

粉丝