feat: 全量同步 254 个常用的 Arduino 扩展库文件

This commit is contained in:
yczpf2019
2026-01-24 16:05:38 +08:00
parent c665ba662b
commit 397b9a23a3
6878 changed files with 2732224 additions and 1 deletions

View File

@@ -0,0 +1,266 @@
/******************************************************************
A library for controling a set of 8x8 LEDs with a MAX7219 or
MAX7221 displays.
This is a plugin for Adafruit's core graphics library, providing
basic graphics primitives (points, lines, circles, etc.).
You need to download and install Adafruit_GFX to use this library.
Adafruit invests time and resources providing this open
source code, please support Adafruit and open-source hardware
by purchasing products from Adafruit!
Written by Mark Ruys.
BSD license, check license.txt for more information.
All text above must be included in any redistribution.
******************************************************************/
#include <Adafruit_GFX.h>
#include "Max72xxPanel.h"
#include <SPI.h>
// The opcodes for the MAX7221 and MAX7219
#define OP_NOOP 0
#define OP_DIGIT0 1
#define OP_DIGIT1 2
#define OP_DIGIT2 3
#define OP_DIGIT3 4
#define OP_DIGIT4 5
#define OP_DIGIT5 6
#define OP_DIGIT6 7
#define OP_DIGIT7 8
#define OP_DECODEMODE 9
#define OP_INTENSITY 10
#define OP_SCANLIMIT 11
#define OP_SHUTDOWN 12
#define OP_DISPLAYTEST 15
Max72xxPanel::Max72xxPanel(byte csPin, byte hDisplays, byte vDisplays) : Adafruit_GFX(hDisplays << 3, vDisplays << 3) {
Max72xxPanel::SPI_CS = csPin;
byte displays = hDisplays * vDisplays;
Max72xxPanel::hDisplays = hDisplays;
Max72xxPanel::bitmapSize = displays << 3;
Max72xxPanel::bitmap = (byte*)malloc(bitmapSize);
Max72xxPanel::matrixRotation = (byte*)malloc(displays);
Max72xxPanel::matrixPosition = (byte*)malloc(displays);
for ( byte display = 0; display < displays; display++ ) {
matrixPosition[display] = display;
matrixRotation[display] = 0;
}
SPI.begin();
//SPI.setBitOrder(MSBFIRST);
//SPI.setDataMode(SPI_MODE0);
pinMode(SPI_CS, OUTPUT);
// Clear the screen
fillScreen(0);
// Make sure we are not in test mode
spiTransfer(OP_DISPLAYTEST, 0);
// We need the multiplexer to scan all segments
spiTransfer(OP_SCANLIMIT, 7);
// We don't want the multiplexer to decode segments for us
spiTransfer(OP_DECODEMODE, 0);
// Enable display
shutdown(false);
// Set the brightness to a medium value
setIntensity(7);
}
void Max72xxPanel::setPosition(byte display, byte x, byte y) {
matrixPosition[x + hDisplays * y] = display;
}
void Max72xxPanel::setRotation(byte display, byte rotation) {
matrixRotation[display] = rotation;
}
void Max72xxPanel::setRotation(uint8_t rotation) {
Adafruit_GFX::setRotation(rotation);
}
void Max72xxPanel::shutdown(boolean b) {
spiTransfer(OP_SHUTDOWN, b ? 0 : 1);
}
void Max72xxPanel::setIntensity(byte intensity) {
spiTransfer(OP_INTENSITY, intensity);
}
void Max72xxPanel::fillScreen(uint16_t color) {
memset(bitmap, color ? 0xff : 0, bitmapSize);
}
void Max72xxPanel::drawPixel(int16_t xx, int16_t yy, uint16_t color) {
// Operating in bytes is faster and takes less code to run. We don't
// need values above 200, so switch from 16 bit ints to 8 bit unsigned
// ints (bytes).
// Keep xx as int16_t so fix 16 panel limit
int16_t x = xx;
byte y = yy;
byte tmp;
if ( rotation ) {
// Implement Adafruit's rotation.
if ( rotation >= 2 ) { // rotation == 2 || rotation == 3
x = _width - 1 - x;
}
if ( rotation == 1 || rotation == 2 ) { // rotation == 1 || rotation == 2
y = _height - 1 - y;
}
if ( rotation & 1 ) { // rotation == 1 || rotation == 3
tmp = x; x = y; y = tmp;
}
}
if ( x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT ) {
// Ignore pixels outside the canvas.
return;
}
// Translate the x, y coordinate according to the layout of the
// displays. They can be ordered and rotated (0, 90, 180, 270).
byte display = matrixPosition[(x >> 3) + hDisplays * (y >> 3)];
x &= 0b111;
y &= 0b111;
byte r = matrixRotation[display];
if ( r >= 2 ) { // 180 or 270 degrees
x = 7 - x;
}
if ( r == 1 || r == 2 ) { // 90 or 180 degrees
y = 7 - y;
}
if ( r & 1 ) { // 90 or 270 degrees
tmp = x; x = y; y = tmp;
}
byte d = display / hDisplays;
x += (display - d * hDisplays) << 3; // x += (display % hDisplays) * 8
y += d << 3; // y += (display / hDisplays) * 8
// Update the color bit in our bitmap buffer.
byte *ptr = bitmap + x + WIDTH * (y >> 3);
byte val = 1 << (y & 0b111);
if ( color ) {
*ptr |= val;
}
else {
*ptr &= ~val;
}
}
void Max72xxPanel::write() {
// Send the bitmap buffer to the displays.
for ( byte row = OP_DIGIT7; row >= OP_DIGIT0; row-- ) {
spiTransfer(row);
}
}
void Max72xxPanel::spiTransfer(byte opcode, byte data) {
// If opcode > OP_DIGIT7, send the opcode and data to all displays.
// If opcode <= OP_DIGIT7, display the column with data in our buffer for all displays.
// We do not support (nor need) to use the OP_NOOP opcode.
// Enable the line
digitalWrite(SPI_CS, LOW);
// Now shift out the data, two bytes per display. The first byte is the opcode,
// the second byte the data.
byte end = opcode - OP_DIGIT0;
byte start = bitmapSize + end;
do {
start -= 8;
SPI.transfer(opcode);
SPI.transfer(opcode <= OP_DIGIT7 ? bitmap[start] : data);
}
while ( start > end );
// Latch the data onto the display(s)
digitalWrite(SPI_CS, HIGH);
}
void Max72xxPanel::scrollMessage(String msg,int displayScrollSpeed) {//滚动显示文本
msg += "";
int refresh=1;
for ( int i = 0 ; i < 6 * msg.length() + WIDTH- 2; i++ ) {
if (refresh == 1)
i = 0;
refresh = 0;
fillScreen(0);
int letter = i / 6;
int x = (WIDTH - 1) - i % 6;
int y = (HEIGHT- 8) / 2; // center the text vertically
while ( x + 6 - 1 >= 0 && letter >= 0 ) {
if ( letter < msg.length() ) {
drawChar(x, y, msg[letter], HIGH, LOW, 1);
}
letter--;
x -= 6;
}
write();
delay(displayScrollSpeed);
}
//matrix.setCursor(0, 0);
}
void Max72xxPanel::scrollMessage(int num,int displayScrollSpeed) {//滚动显示整数
String msg = String("") + num;
msg += "";
int refresh=1;
for ( int i = 0 ; i < 6 * msg.length() + WIDTH- 2; i++ ) {
if (refresh == 1)
i = 0;
refresh = 0;
fillScreen(0);
int letter = i / 6;
int x = (WIDTH - 1) - i % 6;
int y = (HEIGHT- 8) / 2; // center the text vertically
while ( x + 6 - 1 >= 0 && letter >= 0 ) {
if ( letter < msg.length() ) {
drawChar(x, y, msg[letter], HIGH, LOW, 1);
}
letter--;
x -= 6;
}
write();
delay(displayScrollSpeed);
}
//matrix.setCursor(0, 0);
}
void Max72xxPanel::scrollMessage(float num,int displayScrollSpeed) {//滚动显示小数
String msg = String("") + num;
msg += "";
int refresh=1;
for ( int i = 0 ; i < 6 * msg.length() + WIDTH- 2; i++ ) {
if (refresh == 1)
i = 0;
refresh = 0;
fillScreen(0);
int letter = i / 6;
int x = (WIDTH - 1) - i % 6;
int y = (HEIGHT- 8) / 2; // center the text vertically
while ( x + 6 - 1 >= 0 && letter >= 0 ) {
if ( letter < msg.length() ) {
drawChar(x, y, msg[letter], HIGH, LOW, 1);
}
letter--;
x -= 6;
}
write();
delay(displayScrollSpeed);
}
//matrix.setCursor(0, 0);
}

View File

@@ -0,0 +1,122 @@
/******************************************************************
A library for controling a set of 8x8 LEDs with a MAX7219 or
MAX7221 displays.
This is a plugin for Adafruit's core graphics library, providing
basic graphics primitives (points, lines, circles, etc.).
You need to download and install Adafruit_GFX to use this library.
Adafruit invests time and resources providing this open
source code, please support Adafruit and open-source hardware
by purchasing products from Adafruit!
Written by Mark Ruys, 2013.
BSD license, check license.txt for more information.
All text above must be included in any redistribution.
Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf
******************************************************************/
#ifndef Max72xxPanel_h
#define Max72xxPanel_h
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#include "pins_arduino.h"
#endif
class Max72xxPanel : public Adafruit_GFX {
public:
/*
* Create a new controler
* Parameters:
* csPin pin for selecting the device
* hDisplays number of displays horizontally
* vDisplays number of displays vertically
*/
Max72xxPanel(byte csPin, byte hDisplays=1, byte vDisplays=1);
/*
* Define how the displays are ordered. The first display (0)
* is the one closest to the Arduino.
*/
void setPosition(byte display, byte x, byte y);
/*
* Define if and how the displays are rotated. The first display
* (0) is the one closest to the Arduino. rotation can be:
* 0: no rotation
* 1: 90 degrees clockwise
* 2: 180 degrees
* 3: 90 degrees counter clockwise
*/
void setRotation(byte display, byte rotation);
/*
* Implementation of Adafruit's setRotation(). Probably, you don't
* need this function as you can achieve the same result by using
* the previous two functions.
*/
void setRotation(byte rotation);
/*
* Draw a pixel on your canvas. Note that for performance reasons,
* the pixels are not actually send to the displays. Only the internal
* bitmap buffer is modified.
*/
void drawPixel(int16_t x, int16_t y, uint16_t color);
/*
* As we can do this much faster then setting all the pixels one by
* one, we have a dedicated function to clear the screen.
* The color can be 0 (blank) or non-zero (pixel on).
*/
void fillScreen(uint16_t color);
/*
* Set the shutdown (power saving) mode for the device
* Paramaters:
* status If true the device goes into power-down mode. Set to false
* for normal operation.
*/
void shutdown(boolean status);
/*
* Set the brightness of the display.
* Paramaters:
* intensity the brightness of the display. (0..15)
*/
void setIntensity(byte intensity);
/*
* After you're done filling the bitmap buffer with your picture,
* send it to the display(s).
*/
void write();
void scrollMessage(String msg , int displayScrollSpeed);
void scrollMessage(int num , int displayScrollSpeed);
void scrollMessage(float num , int displayScrollSpeed);
private:
byte SPI_CS; /* SPI chip selection */
/* Send out a single command to the device */
void spiTransfer(byte opcode, byte data=0);
/* We keep track of the led-status for 8 devices in this array */
byte *bitmap;
byte bitmapSize;
byte hDisplays;
byte *matrixPosition;
byte *matrixRotation;
};
#endif // Max72xxPanel_h

View File

@@ -0,0 +1,40 @@
Max72xxPanel
============
An Arduino library for controlling a canvas of sets of 8x8 LEDs driven by MAX7219 or MAX7221 controllers. [Kits][hardware] sold for below 10 USD.
This is a plugin for Adafruit's core graphics library GFX, providing basic graphics primitives (points, lines, circles, characters, etc.). So besides this library, you need to download and install [Adafruit_GFX][gfx-download], *dated Jul 5th, 2013 or more recent*.
Written by Mark Ruys, <mark@paracas.nl>, 2013.
Installation
------------
Place the [Max72xxPanel][download] and [Adafruit_GFX][gfx-download] library folders in your `<arduinosketchfolder>/libraries/` folder. You may need to create the `libraries` subfolder if its your first library. Restart the Arduino IDE.
Features
--------
- Double buffering to prevent screen flicker. Check out our example for usage.
- Support for multiple matrix displays, positioned in an arbitrary rectangular layout. You can define both the order as the rotation of each display.
- Uses the [SPI library][spi] to address the display(s) connected in cascade.
- Low memory footprint.
- Fast, no use of NOOP's.
Usage
-----
Read [overview][gfx-docs] for instructions how to use Adafruit_GFX. Check out our [examples][examples] to get some inspiration. Note that to update your displays, you need to explicitely call write().
At YouTube, you'll find a [ticker tape][tickertape] and [snake] demo.
[download]: https://github.com/markruys/arduino-Max72xxPanel/archive/master.zip "Download Max72xxPanel library"
[gfx-download]: https://github.com/adafruit/Adafruit-GFX-Library "Download Adafruit GFX Graphics Library"
[gfx-docs]: http://learn.adafruit.com/adafruit-gfx-graphics-library/overview "Documentation Adafruit GFX Graphics Library"
[examples]: https://github.com/markruys/arduino-Max72xxPanel/tree/master/examples "Show Max72xxPanel examples"
[hardware]: https://www.google.com/search?q=MAX7219+Red+Dot+Matrix+Module "For kits, google MAX7219 Red Dot Matrix Module"
[spi]: http://arduino.cc/en/Reference/SPI "SPI library"
[tickertape]: http://www.youtube.com/watch?v=a8T7ZFeaf1A "Max72xxPanel Arduino library demo (ticker tape)"
[snake]: http://www.youtube.com/watch?v=FbJJyuCwohs "Max72xxPanel Arduino library demo (snake)"

View File

@@ -0,0 +1,55 @@
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
int pinCS = 10; // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI )
int numberOfHorizontalDisplays = 1;
int numberOfVerticalDisplays = 1;
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);
int pinRandom = A0;
int wait = 20; // In milliseconds
void setup() {
matrix.setIntensity(4); // Set brightness between 0 and 15
// Adjust to your own needs
// matrix.setPosition(0, 0, 0); // The first display is at <0, 0>
// matrix.setPosition(1, 1, 0); // The second display is at <1, 0>
// matrix.setPosition(2, 2, 0); // The third display is at <2, 0>
// matrix.setPosition(3, 3, 0); // And the last display is at <3, 0>
// ...
// matrix.setRotation(0, 2); // The first display is position upside down
// matrix.setRotation(3, 2); // The same hold for the last display
randomSeed(analogRead(pinRandom)); // Initialize random generator
}
int x = numberOfHorizontalDisplays * 8 / 2;
int y = numberOfVerticalDisplays * 8 / 2;
int xNext, yNext;
void loop() {
matrix.drawPixel(x, y, HIGH);
matrix.write(); // Send bitmap to display
delay(wait);
matrix.drawPixel(x, y, LOW); // Erase the old position of our dot
do {
switch ( random(4) ) {
case 0: xNext = constrain(x + 1, 0, matrix.width() - 1); yNext = y; break;
case 1: xNext = constrain(x - 1, 0, matrix.width() - 1); yNext = y; break;
case 2: yNext = constrain(y + 1, 0, matrix.height() - 1); xNext = x; break;
case 3: yNext = constrain(y - 1, 0, matrix.height() - 1); xNext = x; break;
}
}
while ( x == xNext && y == yNext ); // Repeat until we find a new coordinate
x = xNext;
y = yNext;
}

View File

@@ -0,0 +1,91 @@
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
int pinCS = 10; // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI )
int numberOfHorizontalDisplays = 1;
int numberOfVerticalDisplays = 1;
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);
const int pinRandom = A0;
const int wait = 100; // In milliseconds
const int length = 8;
int x[length], y[length];
int ptr, nextPtr;
void setup() {
matrix.setIntensity(4); // Set brightness between 0 and 15
// Adjust to your own needs
// matrix.setPosition(0, 0, 0); // The first display is at <0, 0>
// matrix.setPosition(1, 1, 0); // The second display is at <1, 0>
// matrix.setPosition(2, 2, 0); // The third display is at <2, 0>
// matrix.setPosition(3, 3, 0); // And the last display is at <3, 0>
// ...
// matrix.setRotation(0, 2); // The first display is position upside down
// matrix.setRotation(3, 2); // The same hold for the last display
// Reset all variables
for ( ptr = 0; ptr < length; ptr++ ) {
x[ptr] = numberOfHorizontalDisplays * 8 / 2;
y[ptr] = numberOfVerticalDisplays * 8 / 2;
}
nextPtr = 0;
randomSeed(analogRead(pinRandom)); // Initialize random generator
}
void loop() {
// Shift pointer to the next segment
ptr = nextPtr;
nextPtr = next(ptr);
matrix.drawPixel(x[ptr], y[ptr], HIGH); // Draw the head of the snake
matrix.write(); // Send bitmap to display
delay(wait);
if ( ! occupied(nextPtr) ) {
matrix.drawPixel(x[nextPtr], y[nextPtr], LOW); // Remove the tail of the snake
}
for ( int attempt = 0; attempt < 10; attempt++ ) {
// Jump at random one step up, down, left, or right
switch ( random(4) ) {
case 0: x[nextPtr] = constrain(x[ptr] + 1, 0, matrix.width() - 1); y[nextPtr] = y[ptr]; break;
case 1: x[nextPtr] = constrain(x[ptr] - 1, 0, matrix.width() - 1); y[nextPtr] = y[ptr]; break;
case 2: y[nextPtr] = constrain(y[ptr] + 1, 0, matrix.height() - 1); x[nextPtr] = x[ptr]; break;
case 3: y[nextPtr] = constrain(y[ptr] - 1, 0, matrix.height() - 1); x[nextPtr] = x[ptr]; break;
}
if ( ! occupied(nextPtr) ) {
break; // The spot is empty, break out the for loop
}
}
}
boolean occupied(int ptrA) {
for ( int ptrB = 0 ; ptrB < length; ptrB++ ) {
if ( ptrA != ptrB ) {
if ( equal(ptrA, ptrB) ) {
return true;
}
}
}
return false;
}
int next(int ptr) {
return (ptr + 1) % length;
}
boolean equal(int ptrA, int ptrB) {
return x[ptrA] == x[ptrB] && y[ptrA] == y[ptrB];
}

View File

@@ -0,0 +1,46 @@
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
int pinCS = 10; // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI )
int numberOfHorizontalDisplays = 1;
int numberOfVerticalDisplays = 1;
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);
void setup() {
matrix.setIntensity(0);
// Adjust to your own needs
// matrix.setPosition(0, 0, 0); // The first display is at <0, 0>
// matrix.setPosition(1, 1, 0); // The second display is at <1, 0>
// matrix.setPosition(2, 2, 0); // The third display is at <2, 0>
// matrix.setPosition(3, 3, 0); // And the last display is at <3, 0>
// ...
// matrix.setRotation(0, 2); // The first display is position upside down
// matrix.setRotation(3, 2); // The same hold for the last display
}
int wait = 50;
int inc = -2;
void loop() {
for ( int x = 0; x < matrix.width() - 1; x++ ) {
matrix.fillScreen(LOW);
matrix.drawLine(x, 0, matrix.width() - 1 - x, matrix.height() - 1, HIGH);
matrix.write(); // Send bitmap to display
delay(wait);
}
for ( int y = 0; y < matrix.height() - 1; y++ ) {
matrix.fillScreen(LOW);
matrix.drawLine(matrix.width() - 1, y, 0, matrix.height() - 1 - y, HIGH);
matrix.write(); // Send bitmap to display
delay(wait);
}
wait = wait + inc;
if ( wait == 0 ) inc = 2;
if ( wait == 50 ) inc = -2;
}

View File

@@ -0,0 +1,55 @@
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
int pinCS = 10; // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI )
int numberOfHorizontalDisplays = 4;
int numberOfVerticalDisplays = 1;
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);
String tape = "Arduino";
int wait = 20; // In milliseconds
int spacer = 1;
int width = 5 + spacer; // The font width is 5 pixels
void setup() {
matrix.setIntensity(7); // Use a value between 0 and 15 for brightness
// Adjust to your own needs
// matrix.setPosition(0, 0, 0); // The first display is at <0, 0>
// matrix.setPosition(1, 1, 0); // The second display is at <1, 0>
// matrix.setPosition(2, 2, 0); // The third display is at <2, 0>
// matrix.setPosition(3, 3, 0); // And the last display is at <3, 0>
// ...
// matrix.setRotation(0, 2); // The first display is position upside down
// matrix.setRotation(3, 2); // The same hold for the last display
}
void loop() {
for ( int i = 0 ; i < width * tape.length() + matrix.width() - 1 - spacer; i++ ) {
matrix.fillScreen(LOW);
int letter = i / width;
int x = (matrix.width() - 1) - i % width;
int y = (matrix.height() - 8) / 2; // center the text vertically
while ( x + width - spacer >= 0 && letter >= 0 ) {
if ( letter < tape.length() ) {
matrix.drawChar(x, y, tape[letter], HIGH, LOW, 1);
}
letter--;
x -= width;
}
matrix.write(); // Send bitmap to display
delay(wait);
}
}

View File

@@ -0,0 +1,44 @@
#######################################
# Syntax Coloring Map For Max72xxPanel
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Max72xxPanel KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
shutdown KEYWORD2
setIntensity KEYWORD2
invertDisplay KEYWORD2
drawPixel KEYWORD2
drawLine KEYWORD2
drawRect KEYWORD2
fillRect KEYWORD2
fillScreen KEYWORD2
drawCircle KEYWORD2
fillCircle KEYWORD2
drawTriangle KEYWORD2
fillTriangle KEYWORD2
drawRoundRect KEYWORD2
fillRoundRect KEYWORD2
drawBitmap KEYWORD2
drawChar KEYWORD2
write KEYWORD2
setCursor KEYWORD2
setTextColor KEYWORD2
setTextColor KEYWORD2
setTextSize KEYWORD2
setTextWrap KEYWORD2
setPosition KEYWORD2
setRotation KEYWORD2
getRotation KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@@ -0,0 +1,25 @@
Software License Agreement (BSD License)
Copyright (c) 2013, Mark Ruys. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.