diff --git a/.gitignore b/.gitignore index fcb74bf..0056bec 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ TestResults **.user **.suo **.sdf +.vs/* # resharper /_[Rr]e[Ss]harper.* @@ -19,4 +20,8 @@ TestResults [Tt]humbs.db [Dd]esktop.ini -RaspberrySharp.System.userprefs \ No newline at end of file +RaspberrySharp.System.userprefs + +# Nuget packages +packages/* +!packages/repositories.config diff --git a/Icon.png b/Icon.png index 5db0375..1dc39dd 100644 Binary files a/Icon.png and b/Icon.png differ diff --git a/README.md b/README.md index 57fc772..44bd2bf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + +[![BCH compliance](https://bettercodehub.com/edge/badge/JTrotta/raspberry-sharp-system?branch=master)](https://bettercodehub.com/) + Raspberry# System ================= @@ -15,8 +18,8 @@ Features ### Raspberry.System Raspberry.System provides utilities for Raspberry Pi boards, while using .NET concepts, syntax and case. -You can easily add a reference to it in your Visual Studio projects using the **[Raspberry.System Nuget](https://www.nuget.org/packages/Raspberry.System)**. +You can easily add a reference to it in your Visual Studio projects using the **[Raspberry.System Nuget](https://www.nuget.org/packages/Raspberry.System3)**. It currently support the following features: -+ Automatic detection of various Raspberry Pi revisions, as of 2013-09, **Raspberry Pi model B rev1 and rev2 and Raspberry Pi model A** -+ High resolution (about 1�s) timers ++ Automatic detection of various Raspberry Pi revisions, as of 2017-11: **Raspberry Pi model B rev1 up to Pi 3 model B** ++ High resolution (about 1µs) timers diff --git a/Raspberry.System.3.0.0.nupkg b/Raspberry.System.3.0.0.nupkg new file mode 100644 index 0000000..0539e37 Binary files /dev/null and b/Raspberry.System.3.0.0.nupkg differ diff --git a/Raspberry.System.nuspec b/Raspberry.System.nuspec deleted file mode 100644 index 23ffa1d..0000000 --- a/Raspberry.System.nuspec +++ /dev/null @@ -1,33 +0,0 @@ - - - - Raspberry.System - 0.0.0 - Raspberry.System - Eric Bézine - Raspberry-Sharp - http://opensource.org/licenses/GPL-2.0 - https://github.com/raspberry-sharp/raspberry-sharp-system/wiki - https://raw.github.com/raspberry-sharp/raspberry-sharp-system/master/Icon.png - true - - Raspberry.System is a Mono/.NET assembly providing access to Raspberry Pi system features. - - It is an initiative of the Raspberry# Community, aimed at providing tools and information concerning use of Raspberry Pi boards with Mono/.NET framework. - Visit project [Github site](https://github.com/raspberry-sharp/raspberry-sharp-system/) for documentation and samples. - - Raspberry.System is a Mono/.NET assembly providing access to Raspberry Pi system features. - en-US - Raspberry Pi Mono System Timers - - - - - - - - - - - - diff --git a/Raspberry.System/Board.cs b/Raspberry.System/Board.cs index 4595242..327f484 100644 --- a/Raspberry.System/Board.cs +++ b/Raspberry.System/Board.cs @@ -1,9 +1,11 @@ #region References using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Text; #endregion @@ -13,29 +15,31 @@ namespace Raspberry /// Represents the Raspberry Pi mainboard. /// /// - /// Version and revisions are based on . + /// Version and revisions are based on . /// for information. + /// NOTES on V3.1.2: + /// + /// IsOverClocked has been removed. This used the "warranty" flag (i.e. void if overclocked) but this policy was changed. + /// Firmware has been changed to RevisionCode. + /// Details about the cores are also loaded. + /// /// public class Board { + #region Fields - private static readonly Lazy board = new Lazy(LoadBoard); - - private readonly Dictionary settings; - private readonly Lazy model; - private readonly Lazy connectorPinout; + private static Board board; + private uint revisionCode; + private IList cores = new List(); #endregion #region Instance Management - private Board(Dictionary settings) + private Board() { - model = new Lazy(LoadModel); - connectorPinout = new Lazy(LoadConnectorPinout); - - this.settings = settings; + LoadBoard(); } #endregion @@ -45,10 +49,7 @@ private Board(Dictionary settings) /// /// Gets the current mainboard configuration. /// - public static Board Current - { - get { return board.Value; } - } + public static Board Current => board ?? ( board = new Board() ); /// /// Gets a value indicating whether this instance is a Raspberry Pi. @@ -56,28 +57,7 @@ public static Board Current /// /// true if this instance is a Raspberry Pi; otherwise, false. /// - public bool IsRaspberryPi - { - get - { - return Processor != Processor.Unknown; - } - } - - /// - /// Gets the processor name. - /// - /// - /// The name of the processor. - /// - public string ProcessorName - { - get - { - string hardware; - return settings.TryGetValue("Hardware", out hardware) ? hardware : null; - } - } + public bool IsRaspberryPi { get; private set; } /// /// Gets the processor. @@ -85,73 +65,41 @@ public string ProcessorName /// /// The processor. /// - public Processor Processor - { - get - { - Processor processor; - return Enum.TryParse(ProcessorName, true, out processor) ? processor : Processor.Unknown; - } - } + public Processor Processor { get; private set; } /// - /// Gets the board firmware version. + /// Gets the board revision code. This is the number from cpuinfo containing many details about the board. /// - public int Firmware + public uint RevisionCode { - get + get => revisionCode; + private set { - string revision; - int firmware; - if (settings.TryGetValue("Revision", out revision) - && !string.IsNullOrEmpty(revision) - && int.TryParse(revision, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out firmware)) - return firmware; - - return 0; + revisionCode = value; + ParseRevisionCode( value, SetBoardParams ); + ConnectorPinout = LoadConnectorPinout(); } } /// - /// Gets the serial number. + /// Gets the board revision, i.e. 1.1 etc. /// - public string SerialNumber - { - get { - string serial; - if (settings.TryGetValue("Serial", out serial) - && !string.IsNullOrEmpty(serial)) - return serial; + public Version Revision { get; private set; } - return null; - } - } + /// + /// Gets the memory size in MB. + /// + public int MemorySize { get; private set; } /// - /// Gets a value indicating whether Raspberry Pi board is overclocked. + /// Gets the serial number. /// - /// - /// true if Raspberry Pi is overclocked; otherwise, false. - /// - public bool IsOverclocked - { - get - { - var firmware = Firmware; - return (firmware & 0xFFFF0000) != 0; - } - } + public string SerialNumber { get; private set; } /// /// Gets the model. /// - /// - /// The model. - /// - public Model Model - { - get { return model.Value; } - } + public Model Model { get; private set; } /// /// Gets the connector revision. @@ -160,97 +108,238 @@ public Model Model /// The connector revision. /// /// See for more information. - public ConnectorPinout ConnectorPinout + public ConnectorPinout ConnectorPinout { get; private set; } + + /// + /// Gets the list of cores on this board. + /// + public IList Cores => cores; + + /// + /// Pretty prints the board info. + /// + public override string ToString() { - get { return connectorPinout.Value; } + var sb = new StringBuilder( $"Board:\t\t{Model.GetDisplayName()}\n" + + $"Revision code:\t0x{RevisionCode:X}\n" + + $"Model:\t\t{Model}\n" + + $"Revision:\t{Revision}\n" + + $"Processor:\t{Processor}\n" + + $"Memory size:\t{MemorySize} MB\n" + + $"Connector:\t{ConnectorPinout}\n" + + $"Serial#:\t{SerialNumber}\n" ); + + foreach ( var core in Cores ) sb.Append( "\n" + core ); + + return sb.ToString(); } #endregion #region Private Helpers - private static Board LoadBoard() + private void LoadBoard() { + IsRaspberryPi = true; + string[] cpuInfo; try { const string filePath = "/proc/cpuinfo"; - - var cpuInfo = File.ReadAllLines(filePath); - var settings = new Dictionary(); - var suffix = string.Empty; - - foreach(var l in cpuInfo) - { - var separator = l.IndexOf(':'); + Tracing.TraceInfo( $"Loading board information from '{filePath}'." ); + cpuInfo = File.ReadAllLines( filePath ); + } + catch ( Exception ex ) + { + Tracing.TraceError( "Unable to read cpuinfo - are you sure this is a Pi? " + ex.Message ); + IsRaspberryPi = false; + return; + } - if (!string.IsNullOrWhiteSpace(l) && separator > 0) - { - var key = l.Substring(0, separator).Trim(); - var val = l.Substring(separator + 1).Trim(); - if (string.Equals(key, "processor", StringComparison.InvariantCultureIgnoreCase)) - suffix = "." + val; + Core currentCore = null; + foreach ( var l in cpuInfo ) + { + var separator = l.IndexOf( ':' ); - settings.Add(key + suffix, val); + if ( !string.IsNullOrWhiteSpace( l ) && separator > 0 ) + { + var key = l.Substring( 0, separator ).Trim().ToLower(); + var val = l.Substring( separator + 1 ).Trim(); + switch ( key ) + { + case "revision": + uint revision; + if ( uint.TryParse( val, NumberStyles.HexNumber, null, out revision ) ) + { + RevisionCode = revision; + } + else + { + Tracing.TraceError( $"Unable to parse revision number string - {val}." ); + } + break; + + case "serial": + SerialNumber = val; + break; + + case "processor": + int coreNum; + if ( int.TryParse( val, out coreNum ) ) + { + currentCore = new Core( coreNum ); + cores.Add( currentCore ); + } + else + { + currentCore = null; + } + break; + + default: + currentCore?.SetState( key, val ); + break; } - else - suffix = ""; } + } + Tracing.TraceInfo( "Board information loading complete." ); + } + + /// + /// Parses the Pi's revision code. + /// + public static void ParseRevisionCode( uint revisionCode, Action setBoardParams ) + { + var oldStyle = ( revisionCode & 0x00800000 ) == 0; // Bit 24 + if ( oldStyle ) + { + ParseOldRevisionCode( revisionCode, setBoardParams ); + return; + } + + var memSizeBits = ( revisionCode & 0x00700000 ) >> 20; // Bits 21-23 + var memSize = 256; + switch ( memSizeBits ) + { + case 1: + memSize = 512; + break; + case 2: + memSize = 1024; + break; + } - return new Board(settings); + var procBits = ( revisionCode & 0x000F000 ) >> 12; // Bits 13-16 + var proc = Processor.Bcm2835; + switch ( procBits ) + { + case 1: + proc = Processor.Bcm2836; + break; + case 2: + proc = Processor.Bcm2837; + break; } - catch + + var typeBits = ( revisionCode & 0x0000FF0 ) >> 4; // Bits 5-12 + var type = Model.Unknown; + switch ( typeBits ) { - return new Board(new Dictionary()); + case 0x00: + type = Model.A; + break; + case 0x02: + type = Model.APlus; + break; + case 0x03: + type = Model.BPlus; + break; + case 0x04: + type = Model.B2; + break; + case 0x06: + type = Model.ComputeModule; + break; + case 0x08: + type = Model.B3; + break; + case 0x09: + type = Model.Zero; + break; + case 0x0a: + type = Model.ComputeModule3; + break; + case 0x0c: + type = Model.ZeroW; + break; } + + var revisionBits = revisionCode & 0x000000F; // Bottom 4 bits + var revision = new Version( 1, (int)revisionBits ); + setBoardParams( type, revision, memSize, proc ); } - private Model LoadModel() + private static void ParseOldRevisionCode( uint revisionCode, Action setBoardParams ) { - var firmware = Firmware; - switch (firmware & 0xFFFF) + switch ( revisionCode & 0xFFFF ) { case 0x2: case 0x3: - return Model.BRev1; + setBoardParams( Model.BRev1, new Version( 1, 0 ), 256, Processor.Bcm2835 ); + break; case 0x4: case 0x5: case 0x6: - case 0xd: - case 0xe: - case 0xf: - return Model.BRev2; + setBoardParams( Model.BRev2, new Version( 2, 0 ), 256, Processor.Bcm2835 ); + break; case 0x7: case 0x8: case 0x9: - return Model.A; + setBoardParams( Model.A, new Version( 2, 0 ), 256, Processor.Bcm2835 ); + break; + + case 0xd: + case 0xe: + case 0xf: + setBoardParams( Model.BRev2, new Version( 2, 0 ), 512, Processor.Bcm2835 ); + break; case 0x10: - return Model.BPlus; + setBoardParams( Model.BPlus, new Version( 1, 0 ), 512, Processor.Bcm2835 ); + break; case 0x11: - return Model.ComputeModule; + case 0x14: + setBoardParams( Model.ComputeModule, new Version( 1, 0 ), 512, Processor.Bcm2835 ); + break; case 0x12: - return Model.APlus; + setBoardParams( Model.APlus, new Version( 1, 1 ), 256, Processor.Bcm2835 ); + break; - case 0x1040: - case 0x1041: - return Model.B2; + case 0x13: + setBoardParams( Model.BPlus, new Version( 1, 2 ), 512, Processor.Bcm2835 ); + break; - case 0x0092: - case 0x0093: - return Model.Zero; - - case 0x2082: - return Model.B3; + case 0x15: + setBoardParams( Model.APlus, new Version( 1, 1 ), 512, Processor.Bcm2835 ); // Ambiguous - could have 256MB. + break; default: - return Model.Unknown; + setBoardParams( Model.Unknown, new Version( 0, 0 ), 0, Processor.Bcm2835 ); + break; } } + private void SetBoardParams(Model model, Version revision, int memSize, Processor proc ) + { + Model = model; + Revision = revision; + MemorySize = memSize; + Processor = proc; + } + private ConnectorPinout LoadConnectorPinout() { switch (Model) @@ -267,7 +356,9 @@ private ConnectorPinout LoadConnectorPinout() case Model.APlus: case Model.B2: case Model.Zero: + case Model.ZeroW: case Model.B3: + case Model.ComputeModule3: return ConnectorPinout.Plus; default: @@ -277,4 +368,4 @@ private ConnectorPinout LoadConnectorPinout() #endregion } -} \ No newline at end of file +} diff --git a/Raspberry.System/Core.cs b/Raspberry.System/Core.cs new file mode 100644 index 0000000..8cd8712 --- /dev/null +++ b/Raspberry.System/Core.cs @@ -0,0 +1,122 @@ + +using System.Globalization; + +namespace Raspberry +{ + /// + /// Contains information about an R-Pi core. + /// + public class Core + { + public Core( int procNumber ) + { + ProcessorNumber = procNumber; + } + + /// + /// The number of this core. + /// + public int ProcessorNumber { get; } + + /// + /// The model name of this core. + /// + public string ModelName { get; internal set; } + + /// + /// Bogus MIPS estimate for this core. + /// + public double BogoMips { get; internal set; } + + /// + /// Features of this core. + /// + public string Features { get; internal set; } + + /// + /// Gets the APU implementer code + /// + public int CpuImplementer { get; internal set; } + + /// + /// Gets the CPU architecture code. + /// + public int CpuArchitecture { get; internal set; } + + /// + /// Gets the CPU variant code. + /// + public int CpuVariant { get; internal set; } + + /// + /// Gets the CPU part code. + /// + public int CpuPart { get; internal set; } + + /// + /// Gets the CPU revision code. + /// + public int CpuRevision { get; internal set; } + + /// + /// Converts core information to string format. + /// + public override string ToString() + { + return $"Core:\t\t{ProcessorNumber}\n" + + $" -> Model:\t{ModelName}\n" + + $" -> BogoMIPS:\t{BogoMips}\n" + + $" -> Features:\t{Features}\n" + + $" -> CPU:\timplementer=0x{CpuImplementer:X}, arch={CpuArchitecture}, variant=0x{CpuVariant:X}, " + + $"part=0x{CpuPart:X}, rev={CpuRevision}\n"; + } + + internal void SetState( string key, string val ) + { + switch ( key ) + { + case "model name": + ModelName = val; + break; + + case "bogomips": + double bmips; + if ( double.TryParse( val, out bmips ) ) + { + BogoMips = bmips; + } + break; + + case "features": + Features = val; + break; + + case "cpu implementer": + CpuImplementer = ParseInt( val, NumberStyles.HexNumber ); + break; + + case "cpu architecture": + CpuArchitecture = ParseInt( val ); + break; + + case "cpu variant": + CpuVariant = ParseInt( val, NumberStyles.HexNumber ); + break; + + case "cpu part": + CpuPart = ParseInt( val, NumberStyles.HexNumber ); + break; + + case "cpu revision": + CpuRevision = ParseInt( val ); + break; + } + } + + private int ParseInt( string val, NumberStyles numberStyle = NumberStyles.Integer ) + { + int.TryParse( val, numberStyle, null, out int ret ); + return ret; + } + } +} diff --git a/Raspberry.System/Model.cs b/Raspberry.System/Model.cs index c16174b..f621c74 100644 --- a/Raspberry.System/Model.cs +++ b/Raspberry.System/Model.cs @@ -53,10 +53,20 @@ public enum Model /// Zero, + /// + /// Pi Zero W. + /// + ZeroW, + /// /// Pi 3 Model B. /// - B3 + B3, + + /// + /// Compute module 3. + /// + ComputeModule3, } /// @@ -74,7 +84,7 @@ public static string GetDisplayName(this Model model) switch (model) { case Model.Unknown: - return null; + return "Unknown - not a Pi!"; case Model.A: return "Raspberry Pi Model A"; case Model.APlus: @@ -91,12 +101,16 @@ public static string GetDisplayName(this Model model) return "Raspberry Pi 2 Model B"; case Model.Zero: return "Raspberry Pi Zero"; + case Model.ZeroW: + return "Raspberry Pi Zero W"; case Model.B3: return "Raspberry Pi 3 Model B"; - + case Model.ComputeModule3: + return "Raspberry Pi Compute Module 3"; + default: throw new ArgumentOutOfRangeException("model"); } } } -} \ No newline at end of file +} diff --git a/Raspberry.System/Processor.cs b/Raspberry.System/Processor.cs index 01e2b69..fd91c05 100644 --- a/Raspberry.System/Processor.cs +++ b/Raspberry.System/Processor.cs @@ -11,13 +11,18 @@ public enum Processor Unknown, /// - /// Processor is a BCM2708. + /// Processor is a BCM2835. (Used to be referred to as BCM2708.) /// - Bcm2708, + Bcm2835, /// - /// Processor is a BCM2709. + /// Processor is a BCM2836. (Used to be referred to as BCM2709.) /// - Bcm2709 + Bcm2836, + + /// + /// Processor is BCM2837 + /// + Bcm2837 } } \ No newline at end of file diff --git a/Raspberry.System/Properties/AssemblyInfo.cs b/Raspberry.System/Properties/AssemblyInfo.cs index e57f5af..bd74171 100644 --- a/Raspberry.System/Properties/AssemblyInfo.cs +++ b/Raspberry.System/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.0.0")] -[assembly: AssemblyFileVersion("1.2.0.0")] +[assembly: AssemblyVersion("3.1.2.0")] +[assembly: AssemblyFileVersion("3.1.2.0")] diff --git a/Raspberry.System/Raspberry.System.csproj b/Raspberry.System/Raspberry.System.csproj index d5982b7..04116f5 100644 --- a/Raspberry.System/Raspberry.System.csproj +++ b/Raspberry.System/Raspberry.System.csproj @@ -39,6 +39,7 @@ + @@ -48,6 +49,7 @@ +