Windows 10 IoT核心版下的GPIO测试应用程序(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/pi/gpio-test-application-under-windows-iot-core-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 19 分钟阅读 - 9418 个词 阅读量 0Windows 10 IoT核心版下的GPIO测试应用程序(译文)
原文地址:https://www.codeproject.com/Articles/1098830/GPIO-Test-Application-under-Windows-IoT-Core
原文作者:ChristianLavigne
译文由本站 robot-v1.0 翻译
前言
As a first application that involves hardware, I decided to keep things simple. The idea is just to hook up a couple Inputs and Outputs and have a program that will work with the GPIO to read/write the values.
作为涉及硬件的第一个应用程序,我决定保持简单.这个想法只是连接几个输入和输出,并具有一个可与GPIO配合使用的程序来读取/写入值.
介绍(Introduction)
作为涉及硬件的第一个应用程序,我决定保持简单.这个想法只是连接几个输入和输出,并具有一个可与GPIO配合使用的程序来读取/写入值.(As a first application that involves hardware, I decided to keep things simple. The idea is just to hook up a couple Inputs and Outputs and have a program that will work with the GPIO to read/write the values.)
要求(Requirements)
我为该应用程序选择的要求将使我们能够探索与在Windows 10 IoT核心版操作系统下的Raspberry Pi 2上创建用户界面相关的GPIO和编程模型.(The requirements that I chose for this application will allow us to explore the GPIO and programming model associated with creating User Interface on the Raspberry Pi 2 under the Windows 10 IoT Core operating system.) 该应用程序应包含:(The application should incorporate:)
- 通过GPIO访问的硬件组件(Hardware components accessed through the GPIO)
- 响应式用户界面,将在更改时显示硬件组件的状态.(A responsive UI that will display the status of the hardware components as it changes.)
- 并由用户控制设置输出的计算方式.(And some control by the user to set how the output are calculated.) 为简单起见,此应用程序假设Raspberry Pi已配置为与用户互动,并且已连接到全尺寸屏幕并具有输入指示设备.我将在其他时间探索不同的外形尺寸以及无头设备开发.(For simplicity, this application assumes the Raspberry Pi is configured for user interactivity and that it is connected to a full size screen and has an input pointing device. I will explore different form factors as well as headless device development at another time.)
解(Solution)
为了满足这些要求,我将创建一个可编程的LogicGate应用程序,该应用程序将从2个按钮获取输入,将基于对2个输入施加的逻辑运算来计算结果,并且将绿色LED点亮表示真,将红色LED点亮表示否. .(To achieve these requirements I will create a programmable LogicGate application that will take input from 2 push buttons, will calculate a result based on a logic operation applied to the 2 inputs, and will light up a green LED for true and a red LED for false.) 此外,我们将包括UI,以通过选择使用哪种逻辑操作来控制计算.我们还将包括UI,以可视方式显示输入和输出的状态.(In addition, we will include UI to control the calculation by selecting which logic operation is used. We will also include UI to visually display the status of the Inputs and Outputs.) 每当输入或逻辑运算参数发生任何变化时,应用程序应更新显示的状态,重新计算输出并点亮相应的LED.(Whenever any of the inputs or logic operation parameter changes, the application should update the displayed status, recalculate the output and light up the appropriate LED.)
源代码(Source Code)
若要进行后续操作,您可能需要下载完整的解决方案:(To follow along, you might want to download the full solution:)
- LogicGate.zip(LogicGate.zip) 您可以在计算机上运行该应用程序,但它将在模拟模式下运行(无GPOI).见(You can run the application on your computer, but it will run in simulation mode (no GPOI). See the) 第一个Windows 10 IoT核心版Windows应用程序(First Windows 10 IoT Core Windows Application) 发布有关如何在Raspberry Pi上运行它的信息.(post for information on how to run it on your Raspberry Pi.) 该项目的源代码具有与我的帖子中所述相同的要求:(The source code for the project has the same requirements as stated in my post:) 设置用于Windows 10 IoT核心版开发的Visual Studio 2015(Setting up Visual Studio 2015 for Windows 10 IoT Core development)
应用程序组件概述(Application Components Overview)
该应用程序将基于(The application will be based on the) 我们先前显示的MVVM模式(MVVM pattern that we have shown earlier) .此模式非常适合于创建反应性WPF应用程序.在这种情况下,应用程序由4个主要组件组成:(. This pattern is well adapted to the creation of reactive WPF applications. It this case, the application is composed of 4 main components that are:)
- 硬件(Hardware)
- 模型(Model)
- 查看模型(View Model)
- 视图(View) 组件组织如下:(The components are organized as follow:)
硬件(Hardware)
硬件包括:(The hardware is composed of:)
- 2个开关(右/左)(2 switches (right / left))
- 1个红色LED(1 red LED)
- 1个绿色LED(1 green LED) 有关电子电路的更多详细信息将在以下GPIO测试应用程序第2部分-硬件中提供.(More details about the electronic circuit will be available in the following post GPIO Test Application Part 2 - Hardware.)
模型(Model)
在此应用程序中,我们将使用该模型封装通过GPIO对硬件的访问.(On this application , we will used the model to encapsulate the access to the hardware through the GPIOs.) 该模型将提供一个全面的Model API,该API允许无缝访问以读取输入和写入输出.该界面将包含以下属性:(The model will provide a comprehensive Model API that allows seamless access to read the Inputs and write to the Outputs. The interface will contain the following Properties:)
- 右切换(SwitchRight)
- 左开关(SwitchLeft)
- 红光LED(RedLED)
- 绿色LED(GreenLED)
视图模型(ViewModel)
ViewModel包含Model的实例,并使其可用于View.它还提供了根据用户在视图上选择的逻辑运算来计算输出的功能.(The ViewModel contains an instance of the Model and makes it available to the View. It also provides the functionality to calculate the Output based on the Logic Operation selected by the user on the View.) 可用的逻辑运算为:(The available Logic Operations are:)
- 和(AND)
- 要么(OR)
- 与非(NAND)
- 也不(NOR)
- 异或(XOR)
视图(View)
该视图提供了用户界面,用户可以通过该界面查看硬件状态并选择确定输出的逻辑操作.用户界面将如下所示:(The View provides the User Interface that the user to view the hardware status and to select the Logic Operation that determines the output. The user interface will look something like this:)
哪里:(Where:)
- 逻辑运算(Logic Operation):允许用户选择使用哪种逻辑运算来计算输出的部分.(: Section that allows the user to select which logic operation is used to calculate the output.)
- 设备接口(Device Interface):显示代表不同硬件组件的符号.该符号仅在按下开关或LED点亮时显示.这些符号是:(: Displays the symboles that represents the different hardware components. The symbol is only displayed when the switch is pressed or the LED is lit. The symbols are:)
- 蓝色方块:用于开关(Blue Square: for the switches)
- 红色圆圈:用于红色LED(Red Circle: for the red LED)
- 绿圈:用于绿色LED(Green Circle: for the green LED)
硬体(The Hardware)
该项目的硬件部分非常简单.它仅使用足够的组件来测试GPIO,输入,输出和事件监视的不同方面.(The hardware portion of this project is quite simple. It uses just enough components to be able to test the different aspects of the GPIO, input, output and events monitoring.) 使用适用于Raspberry Pi 2的Microsoft IoT Pack入门包中提供的组件,可以轻松实现此项目.(This project can be easily realized with the components supplied in the Microsoft IoT Pack for Raspberry Pi 2 starter pack.)
组件(Components)
需要以下组件:(The following components are required:)
- 1-树莓派2(1 - Raspberry Pi 2)
- 1-面包板(1 - Breadboard)
- 2-按钮开关(2 - Push switch)
- 1-绿色LED(1 - Green LED)
- 1-红色LED(1 - Red LED)
- 2-560Ω电阻(2 - 560Ω resistors)
- 电线(Wires)
图表(Diagram)
这是我创建的接线图:(Here is the wiring diagram I created:)
我使用了该应用程序(I used the application) 弗里辛(Fritzing) 画我的电子图.我发现将快速接线图放在一起非常合适.它的好处是它已经包含了Raspberry Pi 2作为一个组件,您只需将电线拖到想要使用的引脚上或从其中拖出即可.(to draw my electronic diagram. I found it to be very adequate to put together a quick wiring diagram. The nice part about it is that it already contains the Raspberry Pi 2 as a component and you can just drag the wires to/from the pin you want to use.)
该模型(The Model)
我们的应用程序模型将提供该应用程序使用的数据.在这种情况下,数据表示在GPOI上发送和接收的信息.(The model of our application will provide the data used by the application. The data in this case represents the information that is sent and received on the GPOI.)
模型应用界面(Model Application Interface)
该模型向应用程序公开了一个简单易用的界面.该接口隐藏了使用GPIO的所有复杂性.接口的属性看起来像INotifyPropertyChange对象的常规内容属性.(The Model exposes a simple and easy to use interface to the application. This interface hides all the complexity of working with the GPIO. The properties for the interface look like regular content properties of an INotifyPropertyChange object.) 以下属性构成了此接口:(The following properties compose this interface:)
- RightSwitch:布尔值,当按下右侧开关时返回true.(RightSwitch: bool value that returns true when the right switch is pressed.)
- LeftSwitch:布尔值,当按下左侧开关时返回true.(LeftSwitch: bool value that returns true when the left switch is pressed.)
- RedLED:bool值,表示红色LED是否亮/灭.设置该值将打开绿色LED.(RedLED: bool value that represents if the red LED is on / off. Setting this value will turn the green LED on.)
- GreenLED:表示绿色LED亮/灭的布尔值.设置该值将打开绿色LED.(GreenLED: bool value that represents if the green LED is on / off. Setting this value will turn the green LED on.)
- IsEnabled:只读bool值,用于指定GPIO是否已成功初始化并可用.此属性用于进入仿真模式.在模拟模式下,该应用程序在屏幕上添加按钮以模拟屏幕上的左右按钮.这对于调试计算机上的应用程序很有用.(IsEnabled: read only bool value that specify if the GPIOs have been successfully initialized and are available. This property is used to enter simulation mode. In simulation mode, the application adds buttons on the screen to simulate the left and right push buttons on the screen. This is useful for debugging the application on your computer.) 开关属性如下所示:(The switch properties looks like this:)
private bool m_RightSwitch = true;
/// <summary>
/// Provides access to the value of the Right push switch on the hardware
/// </summary>
public bool RightSwitch
{
get
{
return m_RightSwitch;
}
set
{
if (value != m_RightSwitch)
{
m_RightSwitch = value;
OnPropertyChanged();
}
}
}
LED属性如下所示:(The LED properties looks like this:)
private bool m_RedLED = false;
/// <summary>
/// Reads and Sets the value of the red LED
/// </summary>
public bool RedLED
{
get
{
return m_RedLED;
}
set
{
if (value != m_RedLED)
{
m_RedLED = value;
UpdateGPIO(m_RedLEDPin, value);
OnPropertyChanged();
}
}
}
注意,在LED属性的设置器中,调用UpdateGPIO将值发送到GPIO.(Notice that in the setter of the LED properties, a call is made to UpdateGPIO to send the value to the GPIO.)
GPIO处理(GPIO processing)
在我们的应用程序中,模型封装了所有的GPIO访问. GPIO的流程如下所示:(In the case of our application, the Model is encapsulating all the GPIO access. The flow of GPIO runs something like this:)
-
初始化GPIO(Initialize the GPIO)
-
在应用程序运行时:(While the application is running:)
- 等待输入引脚上的事件(Wait for events on the Input pins)
- 将输出写入输出引脚(Write Outputs to the Output pins)
-
关闭GPIO(Shut down the GPIO) 我们为使用的每个引脚保留GpioPin的实例. GpioPin在初始化过程中创建,并在处置中释放.为简单起见,我们将引脚号保留在常量字段中.(We keep the instance of the GpioPin for each pin that we use. The GpioPin are created in the initialization process and released in the dispose. For simplicity, we keep the pin numbers in constants fields.)
private const int GPIO_LEFT_SWITCH = 21;
private const int GPIO_RIGHT_SWITCH = 20;
private const int GPIO_RED_LED = 6;
private const int GPIO_GREEN_LED = 5;
private GpioController m_Controller = null;
private GpioPin m_LeftSwitchPin = null;
private GpioPin m_RightSwitchPin = null;
private GpioPin m_RedLEDPin = null;
private GpioPin m_GreenLEDPin = null;
初始化/关闭GPIO(Initializing / Shutting down the GPIO)
在初始化过程中,将发生以下顺序:(During the initialization process the following sequence occurs:)
- 应用程序尝试检索默认的GpioController.如果无法检索控制器,则意味着该设备不支持GPIO.(The application attempts to retrieve the default GpioController. If the controller cannot be retrieved, this means that the device does not support GPIO.)
- 所有引脚均已初始化.(All the pins are initialized.)
- 如果所有引脚均正确初始化,则IsEnabled属性设置为true.(If all the pins are initialized correctly, the IsEnabled property is set to true.)
- 如果其中一个引脚无法初始化,则调用Release()函数以释放所有分配的资源.(If one of the pins fails to initialize, the Release() function is called to free up all the allocated resources.)
private void Initialize()
{
Logger.AddLogEntry("Initializing GPIO on LogicGateModel");
m_Controller = GpioController.GetDefault();
if (m_Controller != null)
{
string startupMessage = "Startup:\r\n";
bool success = true;
success &= InitializeLEDPin(GPIO_GREEN_LED, "green LED", out m_GreenLEDPin, ref startupMessage);
success &= InitializeLEDPin(GPIO_RED_LED, "red LED", out m_RedLEDPin, ref startupMessage);
success &= InitializeSwitchPin(GPIO_LEFT_SWITCH, "left switch", out m_LeftSwitchPin, ref startupMessage);
success &= InitializeSwitchPin(GPIO_RIGHT_SWITCH, "right switch", out m_RightSwitchPin, ref startupMessage);
Message = startupMessage;
if (!success)
{
Release();
}
else
{
// Read the current values...
LeftSwitch = m_LeftSwitchPin.Read() == GpioPinValue.High;
RightSwitch = m_RightSwitchPin.Read() == GpioPinValue.High;
IsEnabled = true;
}
}
else
{
Message = "Unable to access the GpioController!";
}
Logger.AddLogEntry("Initialization of GPIO on LogicGateModel complete!");
}
创建LED引脚后:(When the LED pins are created:)
- 我们使用控制器尝试打开销钉(We use the controller to try to open up the pin)
- 如果成功,则确保引脚支持输出驱动模式.(If that succeeds, we make sure the pins supports the Output Drive Mode.)
- 然后我们将驱动模式设置为输出(Then we set the Drive Mode to Output)
- 如果任何操作都不成功,则将结果设置为false,以指示初始化GPIO时出错(If any of the operations do not succeed, we set the result to false to indicate that there was an error initializing the GPIO)
private bool InitializeLEDPin(int pin, string pinName, out GpioPin output, ref string message)
{
bool result = true;
Logger.AddLogEntry("Initializing Pin #{0}, {1}", pin, pinName);
GpioOpenStatus status;
if (m_Controller.TryOpenPin(pin, GpioSharingMode.Exclusive, out output, out status))
{
if (output.IsDriveModeSupported(GpioPinDriveMode.Output))
{
output.SetDriveMode(GpioPinDriveMode.Output);
message += string.Format(" Pin {0} initialized: status = {1}\r\n", pinName, status.ToString());
}
else
{
message += string.Format(" Pin {0} error: Drive mode {1} not supported.\r\n", pinName, status.ToString());
result = false;
}
}
else
{
message += string.Format(" Can't initialize {0}: status = {1}\r\n", pinName, status.ToString());
result = false;
}
return result;
}
创建开关引脚后:(When the switch pins are created:)
- 我们使用控制器尝试打开销钉(We use the controller to try to open up the pin)
- 如果成功,则确保引脚支持输入驱动模式.(If that succeeds, we make sure the pins supports the Input Drive Mode.)
- 然后我们将驱动器模式设置为输入(Then we set the Drive Mode to Input)
- 然后,我们将事件处理程序附加到GpioPinto的ValueChanged事件,以在引脚状态发生变化时接收通知.(Then we attach an event handler to the ValueChanged event of the GpioPinto to receive notification whenever there is a change of state on the pin.)
- 如果任何操作都不成功,则将结果设置为false,以指示初始化GPIO时出错(If any of the operations do not succeed, we set the result to false to indicate that there was an error initializing the GPIO)
private bool InitializeSwitchPin(int pin, string pinName, out GpioPin output, ref string message)
{
bool result = true;
Logger.AddLogEntry("Initializing Pin #{0}, {1}", pin, pinName);
GpioOpenStatus status;
if (m_Controller.TryOpenPin(pin, GpioSharingMode.Exclusive, out output, out status))
{
if (output.IsDriveModeSupported(GpioPinDriveMode.Input))
{
output.SetDriveMode(GpioPinDriveMode.Input);
//Intentional, we do not use debounce this time but this is where you would set it
//output.DebounceTimeout = TimeSpan.FromMilliseconds(5);
output.ValueChanged += PinValueChanged;
message += string.Format(" Pin {0} initialized: status = {1}\r\n", pinName, status.ToString());
}
else
{
message += string.Format(" Pin {0} error: Drive mode {1} not supported.\r\n", pinName, status.ToString());
result = false;
}
}
else
{
message += string.Format(" Can't initialize {0}: status = {1}\r\n", pinName, status.ToString());
result = false;
}
return result;
}
Release函数向前移动,它将事件处理程序与输入引脚断开连接,并在每个引脚上调用Dispose.(The Release function is strait forward, it disconnects the event handler from the input pins and calls Dispose on each of the pins.)
private void Release()
{
Logger.AddLogEntry("Releasing GPIO on LogicGateModel");
if (m_Controller != null)
{
// Release all the GPIO pins used
if (m_LeftSwitchPin != null)
{
m_LeftSwitchPin.ValueChanged -= PinValueChanged;
m_LeftSwitchPin.Dispose();
}
if (m_RightSwitchPin != null)
{
m_RightSwitchPin.ValueChanged -= PinValueChanged;
m_RightSwitchPin.Dispose();
}
if (m_GreenLEDPin != null)
{
m_GreenLEDPin.Dispose();
}
if (m_RedLEDPin != null)
{
m_RedLEDPin.Dispose();
}
}
Logger.AddLogEntry("Completed Releasing GPIO on LogicGateModel");
}
处理输入PIN上的OnChange事件(Handling OnChange event on the input PINs)
初始化引脚时,我们为输入引脚的ValueChanged事件分配了一个事件处理程序.以下代码显示了OnValueChanged函数:(When initializing the pins we assigned an event handler to the ValueChanged event of the input pins. The following code shows the OnValueChanged function:)
private void PinValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
// normally we would use the arg to get the value, as follow:
// (args.Edge == GpioPinEdge.FallingEdge);
// but at this point it is completely unreliable and returns garbage
bool value = sender.Read() == GpioPinValue.High;
if (sender == m_LeftSwitchPin)
{
CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
LeftSwitch = value;
});
}
else if (sender == m_RightSwitchPin)
{
CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
RightSwitch = value;
});
}
}
我们首先通过调用Read()函数读取引脚的值.引发事件的GpioPin作为发送者参数被接收,因此我们使用它来识别按下了哪个按钮.然后,我们只需在适当的属性上设置值.(We first read the value of the pin by calling the Read() function. The GpioPin that raised the event is received as the sender parameter, so we use it to identify which button was pressed. Then we just set the value on the appropriate property.) 该代码的一个特殊性是ValueChange事件在单独的线程上引发.为了与用户界面进行交互,我们需要将调用返回到UI线程上.这是通过CoreApplication.MainView.Dispatcher.RunAsync调用完成的.(One particularity of this code is that the ValueChange event is raised on a separate thread. In order to interact with the User Interface, we need to bring the call back on the UI thread. That is done here with the CoreApplication.MainView.Dispatcher.RunAsync call.)
打开/关闭LED(Turning LEDs On/Off)
当ViewModel设置RedLED或GreenLED的值时,我们需要发送该值GPIO,UpdateGPOI函数会为我们执行以下操作:(When the ViewModel sets the value of the RedLED or GreenLED, we need to send that value GPIO, the UpdateGPOI function does that for us:)
private void UpdateGPIO(GpioPin pin, bool value)
{
if (IsEnabled && pin != null)
{
GpioPinValue v = value ? GpioPinValue.Low : GpioPinValue.High;
pin.Write(v);
}
}
ViewModel(The ViewModel)
ViewModel(VM)封装了应用程序提供的所有功能.每个页面,用户控件或对话框都应具有自己的VM,以实现页面,用户控件或对话框的功能.(The ViewModel (VM) encapsulate all the functionality provided by the application.Each page, user control or dialog should have it’s own VM to implement the page, user control or dialog functionality.) 对于我们的主页,我们有一个MainPageVM,它是此页面的视图模型.(For our Main Page, we have a MainPageVM which is the view model for this page.)
MainPageVM与模型(MainPageVM vs Model)
MainPageVM创建并维护LogicGateModel的当前实例,并将其提供给UI. MainPageVM还对模型执行操作.(The MainPageVM creates and maintains the current instance of the LogicGateModel and makes it available to the UI. MainPageVM also performs operations on the Model.) 在这里,我们看到创建模型的初始化代码:(Here, we see the intialization code that creates the Model:)
private void Initialize()
{
Logger.AddLogEntry("Initializing Main Window VM!");
InitializeCommands();
m_Model = new LogicGateModel();
m_Model.PropertyChanged += Model_PropertyChanged;
RecalculateOutput();
Logger.AddLogEntry("Done Initializing Main Window VM!");
}
...
private LogicGateModel m_Model = null;
/// <summary>
/// This is the instance of the Model.
/// </summary>
public LogicGateModel Model
{
get
{
return m_Model;
}
}
创建模型后,MainPageVM将事件处理程序附加到LogicGateModel的PropertyChanged事件.(When the model is created, the MainPageVM attaches an event handler to the PropertyChanged event of the LogicGateModel.) 该处理程序用于检测和处理LeftSwitch和RightSwitch值中的上行变化.更改了这些属性后,MainPageVM将重新计算所需的输出并相应地更新模型.它还对IsEnabled函数的更改进行重新计算.(The handler is used to detect and act uppon changes in the values of LeftSwitch and RightSwitch. When those properties are changed, the MainPageVM recalculates the required outputs and updates the Model accordingly. It also recalculates on changes to the IsEnabled function.) 下面是Model PropertyChanged事件处理程序代码:(Bellow is the Model_PropertyChanged event handler code:)
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "LeftSwitch" || e.PropertyName == "RightSwitch")
{
RecalculateOutput();
}
else if(e.PropertyName== "IsEnabled")
{
if(Model.IsEnabled)
{
OnPropertyChanged("ShowSimulatedSwitches");
RecalculateOutput();
}
}
}
MainPageVM功能(MainPageVM functionality)
计算方式(Calculations)
MainPageVM的计算部分非常简单.一个case语句对模型的RightSwitch和LeftSwitch执行实际的逻辑运算,然后将结果推回到模型的RedLED(false)和GreenLED(true)属性.(The calculation portion of the MainPageVM is pretty simple. A case statement performs the actual logic operation on the RightSwitch and LeftSwitch of the Model, then the result is pushed back to the RedLED (false) and GreenLED (true) properties of the model.)
private void RecalculateOutput()
{
Logger.AddLogEntry("Recalculating output!");
bool result = false;
switch(Logic)
{
case LogicTypeEnum.AND:
result = Model.LeftSwitch && Model.RightSwitch;
break;
case LogicTypeEnum.OR:
result = Model.LeftSwitch || Model.RightSwitch;
break;
case LogicTypeEnum.NAND:
result = !(Model.LeftSwitch && Model.RightSwitch);
break;
case LogicTypeEnum.NOR:
result = !(Model.LeftSwitch || Model.RightSwitch);
break;
case LogicTypeEnum.XOR:
result = Model.LeftSwitch ^ Model.RightSwitch;
break;
}
Model.GreenLED = result;
Model.RedLED = !result;
}
注意:我们可以使用功能更强大的模式(例如"策略"模式)来选择和执行逻辑运算.但是本着使代码可读的精神,用case语句更好(Note: We could have used a more powerful pattern, such as the Strategy pattern, to select and perform the logic operation. But in the spirit of keeping the code readable a case statement is better).(.)
逻辑属性(Logic Property)
用于计算输出的公式取决于用户输入.用户选择要执行的逻辑运算,然后应用程序使用它来计算输出. MainPageVM的Logic属性用于定义当前使用的操作. MainPageVM还提供了可用操作的列表,以便视图能够向用户显示列表.以下是与Logic属性相关的代码.(The formula used to calculate the output is dependent on user input. The user selects the logic operation to perform then the application uses it to calculate the output. The Logic property of the MainPageVM is used to define the operation currently used. The MainPageVM also provides a list of available operations so that the View is able to display a list to the user. Following is the code related to the Logic property.) LogicTypeEnum表示可用的操作.(LogicTypeEnum represents the available operations.)
public enum LogicTypeEnum
{
AND,
OR,
NAND,
NOR,
XOR
}
Logic和LogicList属性为视图提供功能.注意在逻辑设置器上对RecalculateOutput的调用.(Logic and LogicList properties provide the functionality to the View. Notice the call to RecalculateOutput on the Logic setter.)
private LogicTypeEnum m_Logic = LogicTypeEnum.AND;
/// <summary>
/// This is the logic applied to the inputs to define how calculation is done.
/// </summary>
public LogicTypeEnum Logic
{
get
{
return m_Logic;
}
set
{
if(value != m_Logic)
{
m_Logic = value;
OnPropertyChanged();
RecalculateOutput();
}
}
}
private List<LogicTypeEnum> m_LogicList = null;
/// <summary>
/// This is the list to populate the list or ddl used to select
/// the logic to apply on the inputs.
/// </summary>
public List<LogicTypeEnum> LogicList
{
get
{
if(m_LogicList == null)
{
m_LogicList = new List<LogicTypeEnum>();
Array a = Enum.GetValues(typeof(LogicTypeEnum));
foreach(var v in a)
{
m_LogicList.Add((LogicTypeEnum)v);
}
}
return m_LogicList;
}
}
其他功能(Other Functionality)
其他功能也已添加到MainFormVM.为了进行调试,UI可以在主页上显示模拟左右开关的按钮.这些按钮仅在GPIO无法初始化时才可用,从而无法在VM上启用ShowSimulatedSwitches属性.(Other functionality have also been added to the MainFormVM. For debugging purpose the UI can display buttons on the Main Page simulating the right and left switches. Those buttons are only available if the GPIO fails to initialize which in turn enables the ShowSimulatedSwitches property on the VM.) 还有一个退出命令将关闭应用程序.这对于从主页停止调试会话很有用.(There is also an Exit command that will close the application. This is useful to stop debugging sessions from the Main Page.)
风景(The View)
视图在屏幕上显示信息.它允许用户与系统进行交互,并提供有关GPIO上最新情况的最新视图.它是用XAML编写的,并且直接绑定到Model和ViewModel.(The View presents the information on screen. It allows the user to interact with the system and offers an up to date view of what is happening on the GPIO. It is written in XAML and binds directly to the Model and ViewModel.) 许多屏幕代码是关于创建不同控件在屏幕上的组织方式的布局.我们将尽可能地忽略布局以专注于功能.(A lot of the screen code is about creating the layout of how the different controls are organized on screen. We will ignore the layout as much as possible to focus on the functionality.)
查看模型(View Model)
正如所见(As seen) 之前(before) ,将视图模型声明为页面资源,并实例化为页面的顶级布局容器的DataContext:(, the view model is declared as a page resource and is instantiated as the DataContext of the top level layout container for the page:)
<Page.Resources>
<vm:MainPageVM x:Key="PageViewModel" />
<!-- ... -->
</Page.Resources>
<Grid DataContext="{StaticResource PageViewModel}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
逻辑运算选择(Logic Operation Selection)
用户用于选择逻辑运算的列表非常简单.看起来像这样:(The list used for the selection of the Logic Operation by the user is quite simple. It looks like this:)
有用于生成该视图的代码.请注意,我必须提供不同的模板来控制项目的显示方式.(There is the code used to generate that view. Notice that I had to supply different templates to control the way the items are displayed.)
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Margin="5" Text="Logic Operation" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold"/>
<ListBox Grid.Row="1" ItemsSource="{Binding LogicList}" SelectedItem="{Binding Logic, Mode=TwoWay}" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.ColumnSpan="4" ItemTemplate="{StaticResource LogicItemTemplate}" ItemsPanel="{StaticResource LogicLayoutTemplate}" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="Black" />
</Grid>
逻辑选择的View和ViewModel之间的连接仅包含2个绑定:(The connection between the View and the ViewModel for the Logic Selection consist only in 2 bindings:)
- ItemsSource到MainPageVM.LogicList(ItemsSource to MainPageVM.LogicList)
- SelectedItem到MainPageVM.Logic(SelectedItem to the MainPageVM.Logic)
设备可视界面(Device Visual Interface)
设备输入和输出的可视界面如下所示:(The visual interface for the device inputs and outputs looks like this:)
它由以下组成:(It is composed of:)
- 红色和绿色的两个椭圆分别代表红色LED(未显示)和绿色LED.(Two ellipses, red and green, that represents the red LED (not shown), and the green LED.)
- 两个蓝色矩形代表开关.(Two blue rectangles that represents the switches.) 请注意,“可见性"上的数据绑定直接指向"模型"属性.转换器用于将布尔值转换为UIElement.Visibility.(Notice the databinding on the Visibility points directly to the Model properties. A converter is used to convert the Boolean value to a UIElement.Visibility.) 这是用于生成屏幕的设备接口部分的代码:(Here is the code used to generate the Device Interface portion of the screen:)
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="100"/>
<RowDefinition Height="40"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4" Margin="5" Text="Device Interface" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold"/>
<!--Following are the objects representing the different hardware components-->
<!--Notice the Visibility property binds directly to the Model properties-->
<Rectangle Grid.Row="1" Grid.Column="1" Margin="15" Fill="Blue" Visibility="{Binding Model.LeftSwitch, Converter={StaticResource VisibilityValueConverter}}" />
<Ellipse Grid.Row="1" Grid.Column="2" Margin="15" Fill="Red" Visibility="{Binding Model.RedLED, Converter={StaticResource VisibilityValueConverter}}" />
<Ellipse Grid.Row="1" Grid.Column="3" Margin="15" Fill="Lime" Visibility="{Binding Model.GreenLED, Converter={StaticResource VisibilityValueConverter}}" />
<Rectangle Grid.Row="1" Grid.Column="4" Margin="15" Fill="Blue" Visibility="{Binding Model.RightSwitch, Converter={StaticResource VisibilityValueConverter}}" />
<!--Labels associated with the hardware components-->
<TextBlock Grid.Row="2" Grid.Column="1" Text="Left Switch" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Grid.Row="2" Grid.Column="2" Text="Red LED" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Grid.Row="2" Grid.Column="3" Text="Green LED" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Grid.Row="2" Grid.Column="4" Text="Right Switch" HorizontalAlignment="Center" VerticalAlignment="Center" />
<!--Hardware simulation switches. Only visible when GPIO is not available IE when not running on the Raspberry Pi-->
<ToggleButton Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Content="Sim Left Switch" Visibility="{Binding ShowSimulatedSwitches, Converter={StaticResource VisibilityValueConverter}}" IsChecked="{Binding Model.LeftSwitch, Mode=TwoWay}" HorizontalAlignment="Center" Margin="5" />
<ToggleButton Grid.Row="3" Grid.Column="4" Grid.ColumnSpan="2" Content="Sim Right Switch" Visibility="{Binding ShowSimulatedSwitches, Converter={StaticResource VisibilityValueConverter}}" IsChecked="{Binding Model.RightSwitch, Mode=TwoWay}" HorizontalAlignment="Center" Margin="5" />
</Grid>
其他(Other)
该应用程序在屏幕上提供了更多实用的信息.有一些针对用户的说明,GPIO状态的描述,还有一个事件日志部分,其中显示了应用程序内部发生的所有事件.(The application offers more information on screen that are utilitarian in nature. There are some instructions for the user, a description of the status of the GPIO and there is an event log portion the shows all the events that occur inside the application.)
兴趣点(Points of Interest)
陷阱(Gotchas)
这个项目为我强调的陷阱之一是,GpioPin上的ValueUpdated事件不是最可靠的事情.在奇怪的情况下,出于某种不明显的原因,有时根本不会触发该事件???它适用于大多数非关键性应用程序,但绝对不可靠.(One of the gotchas that this project highlighted for me is that the ValueUpdated event on the GpioPin is not the most reliable thing. On odd occasions and for not apparent reason, sometimes the event doesn’t get triggered at all??? Its good for most, non-critical applications, but it’s by no means reliable.) 这也给了我玩DebounceTimeout值的机会.这样做是禁止ValueUpdated事件.这意味着,它可能会错过某些状态更改.这对于控制较慢的系统以防止噪声很有用,但在接近实时的系统(如用户控制的按钮)上,不提供值是最佳选择.(It also gave me a chance to play around with the DebounceTimeout value. What this does is that is inhibits the ValueUpdated event. Meaning, it might miss some of the state change. That will be usefull on slower more controlled system to prevent noise, but on near-real time system like a user controlled push button, not supplying a value was the best option.)
结论(Conclusion)
我想让这个项目与事件一起工作. .NET中的事件非常有用,因为您不必费力地寻找值的变化,只需注册一个事件并在值变化时得到通知.我对GPIO引脚上的事件不太可靠感到失望,或者我的电子设备过于简单且缺少某些东西???(I wanted to make this project work with event. Events in .NET are very useful because you don’t have to hang around trying to look for changes in values, you just register for an event and get notified when the value changes. I’m a bit disappointed that the event on the GPIO pins is not more reliable, or maybe my electronic is too simple and missing something???) 不过,我发现这是一个有趣的项目,结果非常不错.我学到了很多东西,我必须玩一个新颖的小设备:Raspberry Pi.(Still, this was a fun project that, I find, turned out pretty good. I learned a number of things and I got to play with a neat new little device: Raspberry Pi.) 希望下次,我们将它提高一个档次,并将其提升到一个新的水平!!!(Hopefully, next time around we’ll crank it up a notch and take this to the next level!!!)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# Raspberry VS2013 XAML IoT 新闻 翻译