51单片机89C516笔记(二)_stc89c516-程序员宅基地

技术标签: 笔记  嵌入式  单片机  

下面的各个标题中的内容都是按照文档及各渠道的学习(主要看的是B站的江科大自化协)查询而记录的不同模块的最基本原理,没有多余的废话,一看就明白,每个实验都是上电通过OK的。由于复杂的操作都是由简单组合而成的,因此像网上的那些教程,比如跑马灯,动态数码管显示,本文均未给出具体代码,因为知道了如何去控制硬件,那上述操作就只需要处理代码逻辑就行了。

所有的实验,原理都是基于单片机STC89C516,同系列的其他单片机可能会有细节上的不同,可自行查询文档,原理基本都是相同的。同时,文章部分细节未记录,比如74H245的数据传送方向由DIR引脚的电平高低控制,而控制DIR引脚电平是用一个跳线帽控制的,需要的可以查询文档。

重要信息:
1.已知默认状态下,所有的IO口默认都是高电平。

2.IO口是弱上拉,就是说高电平的能力比低电平弱(通俗的说就是两个互接,结果为低电平)

1.LED

这个模块在 笔记一 里面已经有实验过了,这里重新放一张原理图,只需要控制:

// P20 ~ P27 一共 8 个 IO 口即可。

在这里插入图片描述

2.独立按键

由原理图可知,四个独立按键由P31到P33控制,当按键按下的时候,对应的IO的高电平被置为低电平,单片机就可以检测到对应IO口的电平高低,从而判断按键是否被按下。
在这里插入图片描述
这里我们写一个小Demo,按下 K1按键 D1(LED)亮,松开D1灭:

#include <REGX52.H>
void main() {
    
	
	while(1) {
    
		 // K1 被按下时,P3_1 电位为低电平。看最前文的重要信息。
		if (P3_1 == 0) {
     
			// 开启 D1 LED 灯。
			P2_0 = 0;   
		}
		 // K1 被松开时,关闭 D1 LED 灯。
		else P2_0 = 1; 
	}
}

也是顺利的通过实验。
在这里插入图片描述

3.动态数码管

每个8字形的数码管,由7个小的横竖短杠灯一个点组成,对应图中的a~g和dp编号。图中蓝色圈圈的是8个数码管的各单独数码管中的一组a到dp的共阴极接入线,如果我们想让哪个数码管显示某个数字,就给上面对应的标号低电平(对于蓝色画圈的)。
在这里插入图片描述
这蓝色圈圈的8个引脚由138译码器的3个IO口来控制,138译码器的原理图:
在这里插入图片描述
数码管对应显示的内容由上上个图红色画圈的地方来控制(红色画圈是给高电平亮,低电平不亮)。

示例,我们这里假如让第2个管显示5,则LED7对应的引脚为低电平(有效),LED7在136译码器中是Y6,则CBA对应值为110(6的二进制),显示内容为5,则对应的a,f,g,c,d要亮就是给高电平,根据画红圈部分可得到值:0110 1101(从下往上看),对应16进制6D,可以撸代码了。

#include <REGX52.H>
void main() {
    
	
	P2_4 = 1;
	P2_3 = 1;
	P2_2 = 0;
	// 0110 1101
	P0 = 0x6D;
	while(1) {
    	
	}
}

顺利通过,具体在哪个位置显示什么数值可定义为函数直接调用。
在这里插入图片描述

但是在同一时刻,我们只能控制一个数码管的显示及内容控制,由于它们的线路都是公用的(这是为了节省IO口而设计的),所以如果我们同时显示两个的话,它们显示的内容也只能是相同的,因为同一时刻,下面的电路只能控制显示某一个确定的内容,上面只能控制哪些灯亮,这样就不能同时让各管显示不同的内容,因此如果需要同时显示多个数码管,可以采用的方法是:利用人的视觉残留现象,轮流显示各管的内容,达到一个扫描的效果,看起来就好像同时在亮一样。

4.LCD1604

将LCD1604安装在单片机上,插槽为短的那一排,使用LCD1602时,与左侧3个LED灯冲突,数码管也不能用了,因为P0口被占用了。
在这里插入图片描述
LCD的驱动代码可以参考对应的文档,如下图所示,鉴于初学者还是优先把东西用起来,我就拿了B站的江科大自化协已经写好的驱动代码,我看了一下,作者的代码逻辑还是很清晰的,注释也很详细,我大概阅读了一下源码再对比一下LCD1604的文档,就差不多明白怎么去驱动了,这里先感谢原作者了,但是自己写肯定还是写不好的,所以先拿过来用了。
在这里插入图片描述

#include <REGX52.H>

// 作者: B站, 江科大自化协
// 地址:https://space.bilibili.com/383400717

//引脚配置:
sbit LCD_RS = P2^ 6;
sbit LCD_RW = P2^ 5;
sbit LCD_EN = P2^ 7;

#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
    
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
    
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
    
	LCD_RS = 0;
	LCD_RW = 0;
	LCD_DataPort = Command;
	LCD_EN = 1;
	LCD_Delay();
	LCD_EN = 0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
    
	LCD_RS = 1;
	LCD_RW = 0;
	LCD_DataPort = Data;
	LCD_EN = 1;
	LCD_Delay();
	LCD_EN= 0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line, unsigned char Column)
{
    
	if(Line==1)
	{
    
		LCD_WriteCommand(0x80 | (Column-1));
	}
	else if(Line==2)
	{
    
		LCD_WriteCommand(0x80 | (Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
    
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line, unsigned char Column, char Char)
{
    
	LCD_SetCursor(Line, Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line, unsigned char Column, char *String)
{
    
	unsigned char i;
	LCD_SetCursor(Line, Column);
	for(i = 0; String[i] != '\0'; i++)
	{
    
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X, int Y)
{
    
	unsigned char i;
	int Result = 1;
	for(i = 0; i < Y; i++)
	{
    
		Result *= X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
    
	unsigned char i;
	LCD_SetCursor(Line, Column);
	for(i = Length; i > 0; i--)
	{
    
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line, unsigned char Column, int Number, unsigned char Length)
{
    
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line, Column);
	if(Number >= 0)
	{
    
		LCD_WriteData('+');
		Number1 = Number;
	}
	else
	{
    
		LCD_WriteData('-');
		Number1 =- Number;
	}
	for(i = Length; i > 0; i--)
	{
    
		LCD_WriteData(Number1 / LCD_Pow(10, i-1) % 10 + '0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
    
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line, Column);
	for(i = Length;i > 0; i--)
	{
    
		SingleNumber = Number / LCD_Pow(16, i-1) % 16;
		if(SingleNumber < 10)
		{
    
			LCD_WriteData(SingleNumber + '0');
		}
		else
		{
    
			LCD_WriteData(SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length)
{
    
	unsigned char i;
	LCD_SetCursor(Line, Column);
	for(i = Length; i > 0; i--)
	{
    
		LCD_WriteData(Number / LCD_Pow(2, i-1) % 2 + '0');
	}
}

下面是给用户的接口(LCD1602.h):

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

具体函数用户,注释已经写的很详细了,这里我们来一个Demo:

#include "LCD1602.H"

void main() {
    
	
	LCD_Init();
	LCD_ShowString(1, 1, "Hello STC89C516");
	while(1) {
    
		
		
	}
}

成功点亮:
在这里插入图片描述

5.(4x4)矩阵键盘

矩阵键盘是使用8个IO口来控制16个按键,因此要检测某个按键也需要持续扫描来实现,因开发板自身的冲突问题,使用逐列扫描。

#include "LCD1602.H"
#include "MATRIXKEY.H"

unsigned char KeyNum = 0;

void main() {
    
	
	LCD_Init();
	LCD_ShowString(1, 1, "ReadKey:");
	
	while(1) {
    
		
		KeyNum = MatrixKey();
		if (KeyNum) {
    
			
			LCD_ShowNum(2, 1, KeyNum, 2);
		}	
	}
}

其中MATRIXKEY:

#include <REGX52.H>
#include "MYFUNCTION.H"

// return the buttion's num
unsigned char MatrixKey() {
    

	unsigned char KeyNumber = 0;
	
	P1 = 0xFF;
	P1_3 = 0;
	if (P1_7 == 0) {
     Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 1;}
	if (P1_6 == 0) {
     Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 5;}
	if (P1_5 == 0) {
     Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 9;}
	if (P1_4 == 0) {
     Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 13;}
	
	P1 = 0xFF;
	P1_2 = 0;
	if (P1_7 == 0) {
     Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 2;}
	if (P1_6 == 0) {
     Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 6;}
	if (P1_5 == 0) {
     Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 10;}
	if (P1_4 == 0) {
     Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 14;}
	
	P1 = 0xFF;
	P1_1 = 0;
	if (P1_7 == 0) {
     Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 3;}
	if (P1_6 == 0) {
     Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 7;}
	if (P1_5 == 0) {
     Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 11;}
	if (P1_4 == 0) {
     Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 15;}
	
	P1 = 0xFF;
	P1_0 = 0;
	if (P1_7 == 0) {
     Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 4;}
	if (P1_6 == 0) {
     Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 8;}
	if (P1_5 == 0) {
     Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 12;}
	if (P1_4 == 0) {
     Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 16;}
	
	
	return KeyNumber;
}

再其中MYFUNCTION:

#include <INTRINS.H>

// Delay for the specified time, in milliseconds
void Delay(unsigned int ms)
{
    
	while (ms--) {
    
		
		unsigned char i, j;

		_nop_();
		i = 2;
		j = 199;
		do
		{
    
			while (--j);
		} while (--i);
	}
}

在这里插入图片描述

5.定时器

内部根据时钟发出的信号进行计数,最终产生中断,执行终端服务。定时器有4种工作模式,我根据B站的教学, 学了16位的定时器。
在这里插入图片描述
工作框图:
在这里插入图片描述
SYSclk:系统时钟,即晶振周期,此开发板11.0592MHz,12T表示12分,代表1微秒。
本机中断资源:外部终端0、定时器0中断、外部中断、定时器1中断、串口中断、外部中断2、外部中断3。
中断优先级:4个。
中断结构:
在这里插入图片描述
定时器和计数器相关的寄存器:
在这里插入图片描述
中断寄存器:
在这里插入图片描述
截图中只给出了一个总体概括上的表,具体原理可查询文档。
下面是示例,先看TMOD寄存器(不可位寻址表示只能整体赋值):
在这里插入图片描述
GATE给0,则TR0单独控制,C/T给0设定工作在定时器模式,M1和M0共同决定工作在哪种模式(给01就是工作在模式1),高4位先不管,全部给0,则得到值为:0000 0001。
再看TCON寄存器,先只看TF0(中断溢出标志位)和TR0(定时器是否开启)。
在这里插入图片描述
计数器为最多为65535,每1微秒计数加1,让初始值为64535,则1ms后满。中断允许控制位ET0置为1,EA置为1(老型号的单片机文档有这个,但是因为单片机向下兼容,这里也可以使用),打通任督二脉后,看中断号:
在这里插入图片描述
综上可写出代码:

#include <REGX52.H>

void Timer0_Init() {
    
 
	// TMOD = 0x01;  //这里可能会影响 高4位。
	// 换成如下写法
	TMOD &= 0xF0; // 低4位清 0 ,高四位不变,利用与门。
	TMOD |= 0x01; // 最低位置 1, 高四位不变,利用或门。
	
	// to escape encoutering _interrupt when initialization
	TF0 = 0; 
	TR0 = 1;
	
	// TH0 = 0xFC;
	// TL0 = 0x18;
	TH0 = 64535 / 256; // High 8 _bit
	TL0 = 64535 % 256 + 1; // Low 8 _bit,65536 才溢出。
	
	ET0 = 1;
	EA = 1;
	PT0 = 0;
}

// 中断服务
void Timer0_Rountine() interrupt 1 {
    
	P2_0 = 0;
}

void main() {
    
	
	Timer0_Init();
	while(1) {
    
		// 这里遇到中断,去执行,中断。
	}
}

实验也测试通过。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Taynpg/article/details/115472743

智能推荐

揭开均线系统的神秘面纱_揭开极限编程的神秘面纱,重新探讨“ XP蒸馏”,第2部分...-程序员宅基地

文章浏览阅读496次。存档日期:2019年5月15日 | 首次发布:2002年9月10日 在本月的Demystifying Extreme Programming中 ,Roy Miller解释了成为XP团队的程序员意味着什么,以及六个程序员的实践如何特别适合这种情况。 虽然所有19种XP实践都很重要,但程序员实践对于团队开发软件至关重要 。 此内容不再被更新或维护。 全文以PDF格式“按原样”提供。 随着技..._解开均线系统的神秘面纱

centos7.5开启ssh服务-程序员宅基地

文章浏览阅读5k次,点赞2次,收藏15次。1,查看是否已安装openssh-server;指令:rpm -qa | grep ssh2,如果未安装,请先安装openssh-serveryum install openssh-server3,编辑ssh参数:vim /etc/ssh/sshd_config修改端口号允许root登录设置需要密码登录修改好后输入“:wqa”保存退出4,开启ss..._centos7.5开启ssh

【2020-11-02】分享一个爬虫小工具,提高你的工作效率_trillworks-程序员宅基地

文章浏览阅读1.2k次,点赞4次,收藏7次。文章目录前言二、地址https://curl.trillworks.com/三、用法3.1这里以有道翻译为例,右键链接>copy>copy as curl(bash)3.2 黏贴到上面的网站,右侧方框会自动填充requests请求总结前言我们在编写爬虫代码的时候,都需要去请求链接,有事还需要一连串的参数,这时候我们是不可能一个一个手打上去的,这次就分享一个快速解析请求的小工具二、地址https://curl.trillworks.com/三、用法3.1这里以有道翻译为例,右键._trillworks

C++——多项式拟合_c++ 多项式拟合-程序员宅基地

文章浏览阅读1.5w次,点赞16次,收藏88次。 C++——多项式拟合目标:利用C++对txt或者xml中的数据,进行高阶或低阶多项式拟合 为方便以后查找,代码以及详细资料已打包,并上传至云盘(链接:https://pan.baidu.com/s/1bvUBIoxv7Avxeq_Cz6xOZQ 密码:u9qe)打包的内容如下: ..._c++ 多项式拟合

农产品:菜鸟、京东、顺丰们争夺冷链物流的新战场?-程序员宅基地

文章浏览阅读204次。互联网时代,一切都有了新玩法。以吃为例,从古至今中国人对“吃”的热爱都没有变过,在现今如何吃的健康、营养为国人新追求,有机蔬菜、无农药水果成为时下新宠。同时,随着国内物流体系的完善,运输技术的提高,农产品正成为新风口。就在今年3月1日,由国家发改委牵头、24部委联合发布的《关于推动物流高质量发展促进形成强大国内市场的意见》中明确指出,要加强农产品物流骨干网络和冷链物流体系建设,发展如“生鲜电..._顺丰冷链物流基础设施落后

Android Studio项目结构详解_andriodstudio的构建在哪-程序员宅基地

文章浏览阅读6.8k次,点赞8次,收藏67次。Android Studio 是谷歌推出的一个Android集成开发工具,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调试。在IDEA的基础上,Android Studio 提供: 1.基于Gradle的构建支持。 2.Android 专属的重构和快速修复。 3. 提示工具以捕获性能、可用性、版本兼容性等问题。 4.支持ProGuard和应用签名。 5.基于模板的向导来生成常用的Android_andriodstudio的构建在哪

随便推点

Android性能优化工具_android 性能优化工具-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏7次。CPU 性能分析器可以在APP运行时,实时检查应用的 CPU 使用率和线程活动,也可以检查记录的方法轨迹、函数轨迹和系统轨迹的详情。(1)Sample JAVA Methods不能采集耗时很短的方法,而Trace JAVA Methods可以采集耗时很短的方法;_android 性能优化工具

大学实训_软件毕设_Java入门实战_商场管理系统_Punrain_java为超市设计管理系统-程序员宅基地

文章浏览阅读5.2k次,点赞8次,收藏41次。需要 演示文档+数据库涉及+需求分析文档和完整源码的,可通过该链接下载https://download.csdn.net/download/Punrain/11980745_java为超市设计管理系统

Runtime-iOS运行时应用篇_initwithframe runtime拦截-程序员宅基地

文章浏览阅读151次。在上篇文章iOS运行时Runtime基础后,本篇将会总结Rutime的具体应用实例,结合其动态特性,Runtime在开发中的应用大致分为以下几个方面:Runtime应用.png相关文章:iOS运行时Runtime基础一、动态方法交换:Method Swizzling实现动态方法交换(Method Swizzling )是Runtime中最具盛名的应用场景,其原理是:通过Runtime获取到方法实现的地址,进而动态交换两个方法的功能。使用到关键方法如下://获取类方法的M.._initwithframe runtime拦截

2020-08-14_20200814_112325010.jpg-程序员宅基地

文章浏览阅读65次。SpringMvc解决跨域问题前言:弄的一个Mvc项目涉及到了跨域问题,以下是我整理的一些解决方法介绍: 跨站 HTTP 请求(Cross-site HTTP request)是指发起请求的资源所在域不同于该请求所指向资源所在的域的 HTTP 请求。比如说,域名A(http://xusong.example)的某 Web 应用程序中通过标签引入了域名B(http://xusong.foo)站点的某图片资源(http://xusong.foo/image.jpg),域名A的那 Web 应用会使浏览器发_20200814_112325010.jpg

阿里云不同账户下的ecs服务器迁移教程_阿里云两个账户的服务器能不能做镜像迁移-程序员宅基地

文章浏览阅读565次。服务器已购买并创建系统盘的情况下只能同步系统盘,数据盘无法同步。系统盘还未创建的可以同步系统盘和数据盘。4、镜像共享给你要迁移的阿里云服务器的账号上,不是登录账号,是账号ID(个人中心->安全设置->账号ID)3、使用最新的快照生成镜像,镜像是分区域的,华南一只能用华南一的镜像。4、在接收镜像的账号里查看镜像,注意选择正确的区域。2、要迁移的实例创建镜像,创建镜像基于快照。5、创建实例,选择镜像,还是要注意区域。1、先停机维护要迁移的服务器。6、选择共享镜像,更换即可。5、新服务器更换系统盘。_阿里云两个账户的服务器能不能做镜像迁移

Fluka 安装及 pydicom_安装 fluka 2023 g11-程序员宅基地

文章浏览阅读1.3k次。Fluka 安装及 pydicom 准备工作1. Fluka安装及配置2.Flair的安装3. Geoviewer的安装3.1 GeoViewer命令安装3.2 flair-geoviewer-x.x.x.src.rpm安装3.3 其他问题4. Rocks Centos6 安装Fluka 2.6版本出错问题5. 安装pydicom, numpy.6. Centos pip安装准备工作Fluka安装相对简单。首先下载Fluka,Flair,Geoviewer最新版本。例如:fluka-2011.2c_安装 fluka 2023 g11

推荐文章

热门文章

相关标签