【ZYNQ】ZYNQ7000 UART 控制器及驱动应用示例

UART 简介

我们在使用 PS 的时候,通常会添加 UART 控制器,用于打印信息和调试代码。除此之外,PS 在和外
部设备通信时,也会经常使用串口进行通信。

UART 控制器

UART 控制器是一个全双工异步收发控制器,ZYNQ 内部包含两个 UART 控制器,UART0 和 UART1。每一个 UART 控制器支持可编程的波特率发生器、64 字节的接收 FIFO 和发送 FIFO、产生中断、RXD 和 TXD 信号的环回模式设置以及可配置的数据位长度、停止位和校验方式等。
UART 控制器系统框图如图所示:

在这里插入图片描述

由上图可知,UART 控制器和 IO 端口由参考时钟(UART REF_CLK)驱动,同时控制器也需要连接 APB 总线时钟(CPU_1x clock),UART REF_CLK 和 CPU_1x clock 都是来自于 PS 时钟子系统。UART 控制器的配置以及状态的获取由控制(Control)和状态寄存器(Status Registers)完成。

另外,UART 控制器不仅可以连接至 MIO,也可以映射到 EMIO,从而使用 PL 的端口来实现串口通信的功能。当 UART 控制器连接到 MIO 时,只有 Tx(发送)和 Rx(接收)两个引脚;而当连接 EMIO 时,除 Tx 和 Rx 引脚外,可选的还有 CTSN、DSDN、DSRN 等引脚,这些引脚用于串口的流控制,即调制解调器的数据通讯中。

UART 控制器采用独立的接收和发送数据路径,每个路径包含一个 64 字节的 FIFO,控制器对发送和接收 FIFO 中的数据进行串并转换操作。FIFO 的中断标志支持轮询处理或中断驱动处理两种方式。另外,控制器中还有一个模式开关,支持 RXD 和 TXD 信号的各种环回配置。UART 控制器内部框图如下图所示:

在这里插入图片描述

UART 控制器的寄存器通过 APB 从机接口和 PS AXI 总线互联,控制器的寄存器用于对 UART 控制器进行配置和获取状态。波特率发生器(Baud Rate Generator)为 UART 控制器的接收端和发送端提供位周期时钟;中断控制器(GIC)为串口的收发提供了中断服务的功能。

APB 总线接口通过向 TxFIFO 寄存器写值,将数据加载到 TxFIFO 存储器中。当数据加载至 TxFIFO 后,TxFIFO 的空标志变成无效的状态,直到最后一个数据从 TxFIFO 中移出,加载至传输移位寄存器,TxFIFO 恢复空的标志位。同时 TxFIFO 使用 TFULL(满中断状态)用于表示当前 TxFIFO 已经写满,并且会阻止数据继续写入。如果此时继续执行写操作,那么会触发溢出,数据不会加载到 TxFIFO 中。RxFIFO 存储器接收来自接收移位寄存器的数据,当接收完数据后,RxFIFO 空标志信号同样变成无效的状态,直到所有的数据通过 APB 总线发送出去。RxFIFO 的满标志状态用于表示 RxFIFO 已经写满,并且会阻止更多的数据写入。

Mode Switch

模式切换(Mode Switch)控制器控制 RxD 和 TxD 的信号连接方式,总共分为四种模式,分别为:正常模式(Normal Mode)、自动回音模式(Automatic Echo Mode)、本地环回模式(Local Loopback Mode)和远程环回模式(Remote Loopback Mode)。模式切换的功能示意图如所示:

在这里插入图片描述

从上图中可以清晰的看出 UART 不同模式下所实现的功能。在实际应用中,最常用的就是 UART 的正常模式。

  • 正常模式是标准的 UART 操作模式;
  • 自动回音模式下,RxD 连接至 TxD,控制器可以接收数据,但是不能发送数据;
  • 本地环回模式没有连接 RxD 和 TxD 的引脚,用于本地程序的环回测试;
  • 远程环回模式下,RxD 连接至 TxD,但是并没有和控制器连接,因此控制器在此模式下无法发送数据和接收数据。

UART 驱动应用示例

测试平台:黑金 AX7Z035

芯片型号:XC7Z035-2FFG676

驱动示例

  • uart.c
/**
 * Copyright (c) 2022-2023,HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#include "uart.h"

/**
 * @brief 串口初始化
 * 
 * @param UartInstancePtr 串口实例
 * @param UartFormat 串口通信格式
 * @param UartDeviceId 串口 ID 号
 * @return int 
 */
int UartInit(XUartPs* UartInstancePtr, XUartPsFormat* UartFormat, uint16_t UartDeviceId)
{
    int Status;
    XUartPs_Config *UartConfigPtr;

    UartConfigPtr = XUartPs_LookupConfig(UartDeviceId);
    if (NULL == UartConfigPtr) {
        return XST_FAILURE;
    }

    Status = XUartPs_CfgInitialize(UartInstancePtr, UartConfigPtr, 
                        UartConfigPtr->BaseAddress);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    Status = XUartPs_SelfTest(UartInstancePtr);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /* Set UART mode Baud Rate 115200, 8bits, no parity, 1 stop bit */
    XUartPs_SetDataFormat(UartInstancePtr, UartFormat) ;
    /* Set the UART in Normal Mode */
    XUartPs_SetOperMode(UartInstancePtr, XUARTPS_OPER_MODE_NORMAL);

    return Status;
}

/**
 * @brief 串口中断初始化
 * 
 * @param IntcInstancePtr 中断实例
 * @param UartInstancePtr 串口中断实例
 * @param UartIntrId 串口中断 ID 号
 * @param CallBack 中断服务函数
 * @return int 
 */
int UartIntrInit(XScuGic *IntcInstancePtr,	XUartPs *UartInstancePtr, 
        uint32_t UartIntrId, void(* CallBack)(void *))
{
    int Status;

    XScuGic_Config *IntcConfig;

    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (NULL == IntcConfig) {
        return XST_FAILURE;
    }

    Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
            IntcConfig->CpuBaseAddress);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
            (Xil_ExceptionHandler) XScuGic_InterruptHandler,
            IntcInstancePtr);
    Xil_ExceptionEnable();

    Status = XScuGic_Connect(IntcInstancePtr, UartIntrId,
            (Xil_ExceptionHandler) CallBack,
            (void *) UartInstancePtr);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /* Set the receiver timeout.*/
    XUartPs_SetRecvTimeout(UartInstancePtr, 8);

    /*Set receiver FIFO interrupt trigger level, here set to 1*/
    XUartPs_SetFifoThreshold(UartInstancePtr, 1);
    /* 设置中断触发类型 */
    XUartPs_SetInterruptMask(UartInstancePtr, 
                        XUARTPS_IXR_RXOVR | XUARTPS_IXR_RXEMPTY | XUARTPS_IXR_TOUT);
    XScuGic_Enable(IntcInstancePtr, UartIntrId);

    return Status ;
}

/**
 * @brief 串口数据发送函数
 * 
 * @param InstancePtr 串口实例
 * @param BufferPtr 发送缓冲区指针
 * @param NumBytes 要发送的字节数
 * @return int 成功发送的字节数
 */
int UartPsSend(XUartPs *InstancePtr, uint8_t *BufferPtr, uint32_t NumBytes)
{
    uint32_t SentCount = 0U;

    /* Setup the buffer parameters */
    InstancePtr->SendBuffer.RequestedBytes = NumBytes;
    InstancePtr->SendBuffer.RemainingBytes = NumBytes;
    InstancePtr->SendBuffer.NextBytePtr = BufferPtr;


    while (InstancePtr->SendBuffer.RemainingBytes > SentCount)
    {
        /* Fill the FIFO from the buffer */
        if (!XUartPs_IsTransmitFull(InstancePtr->Config.BaseAddress))
        {
            XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
                    XUARTPS_FIFO_OFFSET,
                    ((uint32_t)InstancePtr->SendBuffer.
                            NextBytePtr[SentCount]));

            /* Increment the send count. */
            SentCount++;
        }
    }

    /* Update the buffer to reflect the bytes that were sent from it */
    InstancePtr->SendBuffer.NextBytePtr += SentCount;
    InstancePtr->SendBuffer.RemainingBytes -= SentCount;

    return SentCount;
}

/**
 * @brief 串口数据接收函数
 * 
 * @param InstancePtr 串口实例
 * @param BufferPtr 接收缓冲区指针
 * @param NumBytes 要读取的字节数
 * @return int 成功读取的字节数
 */
int UartPsRev(XUartPs *InstancePtr, uint8_t *BufferPtr, uint32_t NumBytes)
{
    uint32_t ReceivedCount = 0;
    uint32_t CsrRegister;

    /* Setup the buffer parameters */
    InstancePtr->ReceiveBuffer.RequestedBytes = NumBytes;
    InstancePtr->ReceiveBuffer.RemainingBytes = NumBytes;
    InstancePtr->ReceiveBuffer.NextBytePtr = BufferPtr;

    /*
     * Read the Channel Status Register to determine if there is any data in
     * the RX FIFO
     */
    CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
            XUARTPS_SR_OFFSET);

    /*
     * Loop until there is no more data in RX FIFO or the specified
     * number of bytes has been received
     */
    while((ReceivedCount < InstancePtr->ReceiveBuffer.RemainingBytes)&&
            (((CsrRegister & XUARTPS_SR_RXEMPTY) == (u32)0)))
    {
        InstancePtr->ReceiveBuffer.NextBytePtr[ReceivedCount] =
                XUartPs_ReadReg(InstancePtr->Config.BaseAddress, XUARTPS_FIFO_OFFSET);

        ReceivedCount++;

        CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
                XUARTPS_SR_OFFSET);
    }
    InstancePtr->is_rxbs_error = 0;
    /*
     * Update the receive buffer to reflect the number of bytes just
     * received
     */
    if(NULL != InstancePtr->ReceiveBuffer.NextBytePtr){
        InstancePtr->ReceiveBuffer.NextBytePtr += ReceivedCount;
    }
    InstancePtr->ReceiveBuffer.RemainingBytes -= ReceivedCount;

    return ReceivedCount;
}

/**
 * @brief 串口波特率更改
 * 
 * @param UartDeviceId 串口 ID 号
 * @param Baudrate 要设置的波特率
 * @return int 
 */
int UartSetBaudRate(uint16_t UartDeviceId , uint32_t Baudrate)
{
    static XUartPs Uart;
    XUartPs_Config *UartConfigPtr;
    UartConfigPtr = XUartPs_LookupConfig(UartDeviceId);
    if (NULL == UartConfigPtr) {
        return XST_FAILURE;
    }
    XUartPs_CfgInitialize(&Uart, UartConfigPtr, UartConfigPtr->BaseAddress);
    XUartPs_SetBaudRate(&Uart, Baudrate);
    return XST_FAILURE;
}

  • uart.h
/**
 * Copyright (c) 2022-2023,HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#ifndef __UART_H__
#define __UART_H__

#include "xuartps.h"
#include "xscugic.h"

#ifndef INTC_DEVICE_ID
#define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID
#endif

#define UART0_DEVICE_ID     XPAR_XUARTPS_0_DEVICE_ID
#define UART1_DEVICE_ID     XPAR_XUARTPS_1_DEVICE_ID
#define UART0_IRPT_INTR     XPAR_XUARTPS_0_INTR
#define UART1_IRPT_INTR     XPAR_XUARTPS_1_INTR

/*
 * Function declaration
 */
int UartInit(XUartPs* UartInstancePtr, XUartPsFormat* UartFormat, uint16_t UartDeviceId);
int UartIntrInit(XScuGic *IntcInstancePtr,	XUartPs *UartInstancePtr, 
        uint32_t UartIntrId, void(* CallBack)(void *));
int UartPsSend(XUartPs *InstancePtr, uint8_t *BufferPtr, uint32_t NumBytes) ;
int UartPsRev (XUartPs *InstancePtr, uint8_t *BufferPtr, uint32_t NumBytes) ;
int UartSetBaudRate(uint16_t UartDeviceId , uint32_t Baudrate);

#endif

应用实例

  • 初始化串口一

  • app_uart.c

/**
 * Copyright (c) 2022-2023,HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#include "app_uart.h"

#define UART0_BAUDRATE   115200U

extern XScuGic IntcInstPtr;

static XUartPsFormat _Uart0Format = {UART0_BAUDRATE, 8, 0, 1};

/**
 * @brief Interrupt handling functions
 *  
 * @param CallBackRef is a pointer to an upper-level callback reference
 */
static void Uart0Handler(void *CallBackRef)
{
    XUartPs *UartInstancePtr = (XUartPs *) CallBackRef;
    struct uart_msg *_uart0_msg = &g_uart0_msg;
    static uint32_t ReceivedCount = 0;
    static uint32_t UartSrValue = 0;

    _uart0_msg->ReceivedFlag = 0 ;

    /* Read interrupt status and enable bits */
    UartSrValue = XUartPs_ReadReg(UartInstancePtr->Config.BaseAddress, XUARTPS_IMR_OFFSET);
    UartSrValue &= XUartPs_ReadReg(UartInstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET);

    /* check if receiver FIFO trigger */
    if (UartSrValue & XUARTPS_IXR_RXOVR)
    {
        ReceivedCount = UartPsRev(UartInstancePtr, _uart0_msg->ReceivedBufferPtr, UART_MSG_MAX_LEN);
        _uart0_msg->ReceivedByteNum += ReceivedCount;
        _uart0_msg->ReceivedBufferPtr += ReceivedCount;
    }
    /* check if receiver FIFO empty */
    if (UartSrValue & XUARTPS_IXR_RXEMPTY)
    {
        /* do nothing */
    }
    /* check if it is a timeout interrupt */
    if (UartSrValue & XUARTPS_IXR_TOUT)
    {
        _uart0_msg->ReceivedFlag = 1;
    }

    /* clear trigger interrupt */
    XUartPs_WriteReg(UartInstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET, UartSrValue);
}

int app_uart_init(void)
{
    int Status;
    XUartPs *_Uart0_Ps = &Uart0_Ps;

    /* init UART */
    Status = UartInit(&Uart0_Ps, &_Uart0Format, UART0_DEVICE_ID);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /* init UART interrupt */
    Status = UartIntrInit(&IntcInstPtr, &Uart0_Ps, UART0_IRPT_INTR, Uart0Handler);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /* clear trigger interrupt */
    XUartPs_WriteReg(_Uart0_Ps->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_MASK);

    return XST_SUCCESS;
}
  • app_uart.h
/**
 * Copyright (c) 2022-2023,HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#ifndef __APP_UART_H__
#define __APP_UART_H__

#include "uart.h"

/* maximum receiver length */
#define UART_MSG_MAX_LEN    100

struct uart_msg 
{
    uint8_t ReceivedBuffer[UART_MSG_MAX_LEN];
    uint8_t *ReceivedBufferPtr;
    uint32_t ReceivedByteNum;
    char ReceivedFlag;
    uint8_t SendBuffer[UART_MSG_MAX_LEN];
    uint8_t *SendBufferPtr;
    uint32_t SendByteNum;
};

typedef struct uart_msg uart_msg_t;

XUartPs Uart0_Ps;

uart_msg_t g_uart0_msg;

int app_uart_init(void);
int uart_msg_print(void);
int uart_lookback_test(void);
int uart_report(uint8_t dev, uint8_t err);

#endif

参考来源:UG585

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/24301.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

教你一步步使用实现TensorFlow 进行对象检测

在本文中,我们将学习如何使用 TensorFlow Hub 预训练模型执行对象检测。TensorFlow Hub 是一个库和平台,旨在共享、发现和重用预训练的机器学习模型。TensorFlow Hub 的主要目标是简化重用现有模型的过程,从而促进协作、减少冗余工作并加速机器学习的研发。用户可以搜索社区…

Linux内核源码分析-进程调度(五)-组调度

出现的背景 总结来说是希望不同分组的任务在高负载下能分配可控比例的CPU资源。为什么会有这个需求呢&#xff0c;假设多用户计算机系统每个用户的所有任务划分到一个分组中&#xff0c;A用户90个任务&#xff0c;而B用户只有10个任务&#xff08;这100个任务假设都是优先级一…

Python 下载的 11 种姿势,一种比一种高级

今天我们一起学习如何使用不同的Python模块从web下载文件。此外&#xff0c;你将下载常规文件、web页面、Amazon S3和其他资源。 通过本文的学习&#xff0c;你将学到如何克服可能遇到的各种挑战&#xff0c;例如下载重定向的文件、下载大型文件、完成一个多线程下载以及其他策…

C# WPF窗体设计器显示以及App.xaml文件打不开(VS 2022)

问题描述&#xff1a; 在项目中遇到了App.xaml设计器打不开以及窗体设计器不显示&#xff0c;只有代码&#xff0c;如图所示&#xff1a; 可以明显的看见左下角的设计器不见&#xff0c;但是用户控件又有设计器 解决方法&#xff1a; (一、App.xaml不能正常打开) ①清理项…

定薪17K*15,阿里测开岗上岸面经分享....

先简单介绍一下我自己吧&#xff0c;等会大家以为我是什么学历狂人&#xff0c;技术大牛&#xff0c;我毕业于广东一个普通本科院校&#xff0c;绝对不是什么双一流大学&#xff0c;大家不要有距离感&#xff0c;这也是我为什么来分享的原因&#xff0c;因为我觉得我这段经验还…

硬件软件【部署】

开发板和主机 1.功能不同&#xff1a;帮助开发者进行嵌入式系统的开发和调试&#xff0c;具有较强的硬件拓展能力&#xff0c;可以连接各种传感器/执行器等外设。主机为满足一般的计算需求而设计&#xff0c;具备更强的计算和图形处理能力。 2.架构不同&#xff1a;开发板通常…

【接口测试】JMeter测试WebSocket接口

目录 一、WebSocket简介 二、JMeter测试WebSocket接口 三、WebSocket和Socket的区别 最近老被问到WebSocket&#xff0c;突然想到以前大学时上Java课的时候&#xff0c;老师教我们socket连接&#xff0c;一个同学电脑做客户端&#xff0c;一个同学电脑做服务端&#xff0c;…

LAMP平台搭建

文章目录 LAMP概述安装apache安装mysql安装php LAMP概述 LAMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整套系统和相关软件&#xff0c;能够提供动态Web站点服务及其应用开发环境。LAMP是一个缩写词&#xff0c;具体包括Linux操作系统、Apache网站…

Java --- 期末复习卷

一、单选题 1&#xff0e;所有Java应用程序主类必须有一个名叫( )的方法。[ ] A&#xff0e;method B&#xff0e;main() C&#xff0e;java() D&#xff0e;hello 2&#xff0e;编写并保存了一个Java程序文件之后&#xff0c;( )它。[ …

node笔记_http服务搭建(渲染html、json)

文章目录 ⭐前言⭐初始化项目调整npm 的script运行入口搭建hello world的http服务npm run dev执行主函数的http服务 ⭐http返回类型html模板文件返回安装express渲染html的字符串 渲染html文件 sendFile渲染json返回数据类型 res.json ⭐结束 ⭐前言 大家好&#xff0c;我是ym…

TensorFlow 1.x学习(系列二 :4):自实现线性回归

目录 线性回归基本介绍常用的op自实现线性回归预测tensorflow 变量作用域模型的保存和加载 线性回归基本介绍 线性回归&#xff1a; w 1 ∗ x 1 w 2 ∗ x 2 w 3 ∗ x 3 . . . w n ∗ x n b i a s w_1 * x_1 w_2 * x_2 w_3 * x_3 ... w_n * x_n bias w1​∗x1​w2​∗…

MySQL 对日期使用 DATE_FORMAT()函数

文章目录 DATE_FORMAT()函数显示今天是星期几只显示年月显示当前时间的分钟数和秒数 DATE_FORMAT()函数 前面使用日期时间函数&#xff0c;获取到的要么是 yyyy-mm-dd 形式的日期&#xff0c;要么是 hh:MM:ss 形式的时间&#xff0c;或者是 yyyy-mm-dd hh:mm:ss 形式的日期及时…

算法基础学习笔记——⑧堆\哈希表

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨堆 &#x1f353;堆模板&#xff1a; ✨哈希表 &#x1f353;一般哈希模板&#xff1a; &#x1f353;字符串哈希模板&#xff1a; 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需要的看哈O(…

Qt文件系统源码分析—第六篇QSaveFile

深度 本文主要分析Windows平台&#xff0c;Mac、Linux暂不涉及 本文只分析到Win32 API/Windows Com组件/STL库函数层次&#xff0c;再下层代码不做探究 本文QT版本5.15.2 类关系图 QTemporaryFile继承QFile QFile、QSaveFile继承QFileDevice QFileDevice继承QIODevice Q…

远程访问本地jupyter notebook服务 - 无公网IP端口映射

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 转载自远控源码文章&#xff1a;公网远程访问jupyter notebook【cpolar内网穿透】 前言 Jupyter Notebook&am…

网络原理(六):http 协议(上)

目录 HTTP 协议是什么 抓包工具 Fiddler 的下载 使用Fiddler HTTP 请求 (Request) HTTP 请求格式 首行 请求头&#xff08;Header&#xff09; Cookie HTTP 协议是什么 还是老样子&#xff0c;在讲解http 之前我们先来了解以下什么叫做 http 。 HTTP&#xff08;Hyp…

(转载)从0开始学matlab(第14天)—while循环结构

循环(loop) 是一种 matlab 结构&#xff0c;它允许我们多次执行一系列的语句。循环结构有两种基本形式 :while 循环和 for 循环。两者之间的最大不同在于代码的重复是如何控制的。在while 循环中&#xff0c;代码的重复的次数是不能确定的&#xff0c;只要满足用户定义的条件…

整理6个超好用的在线编辑器!

随着 Web 开发对图像可扩展性、响应性、交互性和可编程性的需求增加&#xff0c;SVG 图形成为最适合 Web 开发的图像格式之一。它因文件小、可压缩性强并且无论如何放大或缩小&#xff0c;图像都不会失真而受到欢迎。然而&#xff0c;为了编辑 SVG 图像&#xff0c;需要使用 SV…

《五》 Git 中的标签和分支

标签 tag&#xff1a; Git 可以给仓库中某一次 commit 的提交打上标签。对于重大的版本经常会打上一个标签来表示它的重要性。 创建标签&#xff1a; git tag【tag 名称】&#xff1a;创建标签。 查看标签&#xff1a; git tag&#xff1a;查看标签。 推送标签到远程仓库…

任务40 评奖系统设计

系列文章 任务40 评奖系统设计 为教务处设计一个学生评价老师的程序&#xff1a; 每位学生投一张票&#xff0c;选出自己最喜爱的老师&#xff0c;选票格式为&#xff1a; | 第一喜爱的老师 | 第二喜爱的老师 |第三喜爱的老师 | | 工号 |工号 |工号 | 上述数据存放在一个数据…