Reorganizing library structure, prep for 2.0.0
This commit is contained in:
@@ -0,0 +1,598 @@
|
||||
#include "AES.h"
|
||||
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
1. source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
2. binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation;
|
||||
|
||||
3. the name of the copyright holder is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue 09/09/2006
|
||||
|
||||
This is an AES implementation that uses only 8-bit byte operations on the
|
||||
cipher state (there are options to use 32-bit types if available).
|
||||
|
||||
The combination of mix columns and byte substitution used here is based on
|
||||
that developed by Karl Malbrain. His contribution is acknowledged.
|
||||
*/
|
||||
|
||||
/* This version derived by Mark Tillotson 2012-01-23, tidied up, slimmed down
|
||||
and tailored to 8-bit microcontroller abilities and Arduino datatypes.
|
||||
|
||||
The s-box and inverse s-box were retained as tables (0.5kB PROGMEM) but all
|
||||
the other transformations are coded to save table space. Many efficiency
|
||||
improvments to the routines mix_sub_columns() and inv_mix_sub_columns()
|
||||
(mainly common sub-expression elimination).
|
||||
|
||||
Only the routines with precalculated subkey schedule are retained (together
|
||||
with set_key() - this does however mean each AES object takes 240 bytes of
|
||||
RAM, alas)
|
||||
|
||||
The CBC routines side-effect the iv argument (so that successive calls work
|
||||
together correctly).
|
||||
|
||||
All the encryption and decryption routines work with plain == cipher for
|
||||
in-place encryption, note.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* functions for finite field multiplication in the AES Galois field */
|
||||
|
||||
/* code was modified by george spanos <spaniakos@gmail.com>
|
||||
* 16/12/14
|
||||
*/
|
||||
|
||||
// GF(2^8) stuff
|
||||
|
||||
#define WPOLY 0x011B
|
||||
#define DPOLY 0x008D
|
||||
|
||||
const static byte s_fwd [0x100] PROGMEM =
|
||||
{
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
||||
} ;
|
||||
|
||||
const static byte s_inv [0x100] PROGMEM =
|
||||
{
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
||||
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
||||
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
||||
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
||||
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
|
||||
} ;
|
||||
|
||||
// times 2 in the GF(2^8)
|
||||
#define f2(x) ((x) & 0x80 ? (x << 1) ^ WPOLY : x << 1)
|
||||
#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0))
|
||||
|
||||
static byte s_box (byte x)
|
||||
{
|
||||
// return fwd_affine (pgm_read_byte (&inv [x])) ;
|
||||
return pgm_read_byte (& s_fwd [x]) ;
|
||||
}
|
||||
|
||||
// Inverse Sbox
|
||||
static byte is_box (byte x)
|
||||
{
|
||||
// return pgm_read_byte (&inv [inv_affine (x)]) ;
|
||||
return pgm_read_byte (& s_inv [x]) ;
|
||||
}
|
||||
|
||||
|
||||
static void xor_block (byte * d, byte * s)
|
||||
{
|
||||
for (byte i = 0 ; i < N_BLOCK ; i += 4)
|
||||
{
|
||||
*d++ ^= *s++ ; // some unrolling
|
||||
*d++ ^= *s++ ;
|
||||
*d++ ^= *s++ ;
|
||||
*d++ ^= *s++ ;
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_and_key (byte * d, byte * s, byte * k)
|
||||
{
|
||||
for (byte i = 0 ; i < N_BLOCK ; i += 4)
|
||||
{
|
||||
*d++ = *s++ ^ *k++ ; // some unrolling
|
||||
*d++ = *s++ ^ *k++ ;
|
||||
*d++ = *s++ ^ *k++ ;
|
||||
*d++ = *s++ ^ *k++ ;
|
||||
}
|
||||
}
|
||||
|
||||
// #define add_round_key(d, k) xor_block (d, k)
|
||||
|
||||
/* SUB ROW PHASE */
|
||||
|
||||
static void shift_sub_rows (byte st [N_BLOCK])
|
||||
{
|
||||
st [0] = s_box (st [0]) ; st [4] = s_box (st [4]) ;
|
||||
st [8] = s_box (st [8]) ; st [12] = s_box (st [12]) ;
|
||||
|
||||
byte tt = st [1] ;
|
||||
st [1] = s_box (st [5]) ; st [5] = s_box (st [9]) ;
|
||||
st [9] = s_box (st [13]) ; st [13] = s_box (tt) ;
|
||||
|
||||
tt = st[2] ; st [2] = s_box (st [10]) ; st [10] = s_box (tt) ;
|
||||
tt = st[6] ; st [6] = s_box (st [14]) ; st [14] = s_box (tt) ;
|
||||
|
||||
tt = st[15] ;
|
||||
st [15] = s_box (st [11]) ; st [11] = s_box (st [7]) ;
|
||||
st [7] = s_box (st [3]) ; st [3] = s_box (tt) ;
|
||||
}
|
||||
|
||||
static void inv_shift_sub_rows (byte st[N_BLOCK])
|
||||
{
|
||||
st [0] = is_box (st[0]) ; st [4] = is_box (st [4]);
|
||||
st [8] = is_box (st[8]) ; st [12] = is_box (st [12]);
|
||||
|
||||
byte tt = st[13] ;
|
||||
st [13] = is_box (st [9]) ; st [9] = is_box (st [5]) ;
|
||||
st [5] = is_box (st [1]) ; st [1] = is_box (tt) ;
|
||||
|
||||
tt = st [2] ; st [2] = is_box (st [10]) ; st [10] = is_box (tt) ;
|
||||
tt = st [6] ; st [6] = is_box (st [14]) ; st [14] = is_box (tt) ;
|
||||
|
||||
tt = st [3] ;
|
||||
st [3] = is_box (st [7]) ; st [7] = is_box (st [11]) ;
|
||||
st [11] = is_box (st [15]) ; st [15] = is_box (tt) ;
|
||||
}
|
||||
|
||||
/* SUB COLUMNS PHASE */
|
||||
|
||||
static void mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK])
|
||||
{
|
||||
byte j = 5 ;
|
||||
byte k = 10 ;
|
||||
byte l = 15 ;
|
||||
for (byte i = 0 ; i < N_BLOCK ; i += N_COL)
|
||||
{
|
||||
byte a = st [i] ;
|
||||
byte b = st [j] ; j = (j+N_COL) & 15 ;
|
||||
byte c = st [k] ; k = (k+N_COL) & 15 ;
|
||||
byte d = st [l] ; l = (l+N_COL) & 15 ;
|
||||
byte a1 = s_box (a), b1 = s_box (b), c1 = s_box (c), d1 = s_box (d) ;
|
||||
byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
|
||||
dt[i] = a2 ^ b2^b1 ^ c1 ^ d1 ;
|
||||
dt[i+1] = a1 ^ b2 ^ c2^c1 ^ d1 ;
|
||||
dt[i+2] = a1 ^ b1 ^ c2 ^ d2^d1 ;
|
||||
dt[i+3] = a2^a1 ^ b1 ^ c1 ^ d2 ;
|
||||
}
|
||||
}
|
||||
|
||||
static void inv_mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK])
|
||||
{
|
||||
for (byte i = 0 ; i < N_BLOCK ; i += N_COL)
|
||||
{
|
||||
byte a1 = st [i] ;
|
||||
byte b1 = st [i+1] ;
|
||||
byte c1 = st [i+2] ;
|
||||
byte d1 = st [i+3] ;
|
||||
byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
|
||||
byte a4 = f2(a2), b4 = f2(b2), c4 = f2(c2), d4 = f2(d2) ;
|
||||
byte a8 = f2(a4), b8 = f2(b4), c8 = f2(c4), d8 = f2(d4) ;
|
||||
byte a9 = a8 ^ a1,b9 = b8 ^ b1,c9 = c8 ^ c1,d9 = d8 ^ d1 ;
|
||||
byte ac = a8 ^ a4,bc = b8 ^ b4,cc = c8 ^ c4,dc = d8 ^ d4 ;
|
||||
|
||||
dt[i] = is_box (ac^a2 ^ b9^b2 ^ cc^c1 ^ d9) ;
|
||||
dt[(i+5)&15] = is_box (a9 ^ bc^b2 ^ c9^c2 ^ dc^d1) ;
|
||||
dt[(i+10)&15] = is_box (ac^a1 ^ b9 ^ cc^c2 ^ d9^d2) ;
|
||||
dt[(i+15)&15] = is_box (a9^a2 ^ bc^b1 ^ c9 ^ dc^d2) ;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
AES::AES(){
|
||||
byte ar_iv[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 };
|
||||
memcpy(iv,ar_iv,8);
|
||||
memcpy(iv+8,ar_iv,8);
|
||||
arr_pad[0] = 0x01;
|
||||
arr_pad[1] = 0x02;
|
||||
arr_pad[2] = 0x03;
|
||||
arr_pad[3] = 0x04;
|
||||
arr_pad[4] = 0x05;
|
||||
arr_pad[5] = 0x06;
|
||||
arr_pad[6] = 0x07;
|
||||
arr_pad[7] = 0x08;
|
||||
arr_pad[8] = 0x09;
|
||||
arr_pad[9] = 0x0a;
|
||||
arr_pad[10] = 0x0b;
|
||||
arr_pad[11] = 0x0c;
|
||||
arr_pad[12] = 0x0d;
|
||||
arr_pad[13] = 0x0e;
|
||||
arr_pad[14] = 0x0f;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
byte AES::set_key (byte key [], int keylen)
|
||||
{
|
||||
byte hi ;
|
||||
switch (keylen)
|
||||
{
|
||||
case 16:
|
||||
case 128:
|
||||
keylen = 16; // 10 rounds
|
||||
round = 10 ;
|
||||
break;
|
||||
case 24:
|
||||
case 192:
|
||||
keylen = 24; // 12 rounds
|
||||
round = 12 ;
|
||||
break;
|
||||
case 32:
|
||||
case 256:
|
||||
keylen = 32; // 14 rounds
|
||||
round = 14 ;
|
||||
break;
|
||||
default:
|
||||
round = 0;
|
||||
return AES_FAILURE;
|
||||
}
|
||||
hi = (round + 1) << 4 ;
|
||||
copy_n_bytes (key_sched, key, keylen) ;
|
||||
byte t[4] ;
|
||||
byte next = keylen ;
|
||||
for (byte cc = keylen, rc = 1 ; cc < hi ; cc += N_COL)
|
||||
{
|
||||
for (byte i = 0 ; i < N_COL ; i++)
|
||||
t[i] = key_sched [cc-4+i] ;
|
||||
if (cc == next)
|
||||
{
|
||||
next += keylen ;
|
||||
byte ttt = t[0] ;
|
||||
t[0] = s_box (t[1]) ^ rc ;
|
||||
t[1] = s_box (t[2]) ;
|
||||
t[2] = s_box (t[3]) ;
|
||||
t[3] = s_box (ttt) ;
|
||||
rc = f2 (rc) ;
|
||||
}
|
||||
else if (keylen == 32 && (cc & 31) == 16)
|
||||
{
|
||||
for (byte i = 0 ; i < 4 ; i++)
|
||||
t[i] = s_box (t[i]) ;
|
||||
}
|
||||
byte tt = cc - keylen ;
|
||||
for (byte i = 0 ; i < N_COL ; i++)
|
||||
key_sched [cc + i] = key_sched [tt + i] ^ t[i] ;
|
||||
}
|
||||
return AES_SUCCESS ;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::clean ()
|
||||
{
|
||||
for (byte i = 0 ; i < KEY_SCHEDULE_BYTES ; i++)
|
||||
key_sched [i] = 0 ;
|
||||
round = 0 ;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::copy_n_bytes (byte * d, byte * s, byte nn)
|
||||
{
|
||||
while (nn >= 4)
|
||||
{
|
||||
*d++ = *s++ ; // some unrolling
|
||||
*d++ = *s++ ;
|
||||
*d++ = *s++ ;
|
||||
*d++ = *s++ ;
|
||||
nn -= 4 ;
|
||||
}
|
||||
while (nn--)
|
||||
*d++ = *s++ ;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
byte AES::encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK])
|
||||
{
|
||||
if (round)
|
||||
{
|
||||
byte s1 [N_BLOCK], r ;
|
||||
copy_and_key (s1, plain, (byte*) (key_sched)) ;
|
||||
|
||||
for (r = 1 ; r < round ; r++)
|
||||
{
|
||||
byte s2 [N_BLOCK] ;
|
||||
mix_sub_columns (s2, s1) ;
|
||||
copy_and_key (s1, s2, (byte*) (key_sched + r * N_BLOCK)) ;
|
||||
}
|
||||
shift_sub_rows (s1) ;
|
||||
copy_and_key (cipher, s1, (byte*) (key_sched + r * N_BLOCK)) ;
|
||||
}
|
||||
else
|
||||
return AES_FAILURE ;
|
||||
return AES_SUCCESS ;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK])
|
||||
{
|
||||
while (n_block--)
|
||||
{
|
||||
xor_block (iv, plain) ;
|
||||
if (encrypt (iv, iv) != AES_SUCCESS)
|
||||
return AES_FAILURE ;
|
||||
copy_n_bytes (cipher, iv, N_BLOCK) ;
|
||||
plain += N_BLOCK ;
|
||||
cipher += N_BLOCK ;
|
||||
}
|
||||
return AES_SUCCESS ;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block)
|
||||
{
|
||||
while (n_block--)
|
||||
{
|
||||
xor_block (iv, plain) ;
|
||||
if (encrypt (iv, iv) != AES_SUCCESS)
|
||||
return AES_FAILURE ;
|
||||
copy_n_bytes (cipher, iv, N_BLOCK) ;
|
||||
plain += N_BLOCK ;
|
||||
cipher += N_BLOCK ;
|
||||
}
|
||||
return AES_SUCCESS ;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
byte AES::decrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK])
|
||||
{
|
||||
if (round)
|
||||
{
|
||||
byte s1 [N_BLOCK] ;
|
||||
copy_and_key (s1, plain, (byte*) (key_sched + round * N_BLOCK)) ;
|
||||
inv_shift_sub_rows (s1) ;
|
||||
|
||||
for (byte r = round ; --r ; )
|
||||
{
|
||||
byte s2 [N_BLOCK] ;
|
||||
copy_and_key (s2, s1, (byte*) (key_sched + r * N_BLOCK)) ;
|
||||
inv_mix_sub_columns (s1, s2) ;
|
||||
}
|
||||
copy_and_key (cipher, s1, (byte*) (key_sched)) ;
|
||||
}
|
||||
else
|
||||
return AES_FAILURE ;
|
||||
return AES_SUCCESS ;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK])
|
||||
{
|
||||
while (n_block--)
|
||||
{
|
||||
byte tmp [N_BLOCK] ;
|
||||
copy_n_bytes (tmp, cipher, N_BLOCK) ;
|
||||
if (decrypt (cipher, plain) != AES_SUCCESS)
|
||||
return AES_FAILURE ;
|
||||
xor_block (plain, iv) ;
|
||||
copy_n_bytes (iv, tmp, N_BLOCK) ;
|
||||
plain += N_BLOCK ;
|
||||
cipher += N_BLOCK;
|
||||
}
|
||||
return AES_SUCCESS ;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block)
|
||||
{
|
||||
while (n_block--)
|
||||
{
|
||||
byte tmp [N_BLOCK] ;
|
||||
copy_n_bytes (tmp, cipher, N_BLOCK) ;
|
||||
if (decrypt (cipher, plain) != AES_SUCCESS)
|
||||
return AES_FAILURE ;
|
||||
xor_block (plain, iv) ;
|
||||
copy_n_bytes (iv, tmp, N_BLOCK) ;
|
||||
plain += N_BLOCK ;
|
||||
cipher += N_BLOCK;
|
||||
}
|
||||
return AES_SUCCESS ;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void AES::set_IV(unsigned long long int IVCl){
|
||||
memcpy(iv,&IVCl,8);
|
||||
memcpy(iv+8,&IVCl,8);
|
||||
IVC = IVCl;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::iv_inc(){
|
||||
IVC += 1;
|
||||
memcpy(iv,&IVC,8);
|
||||
memcpy(iv+8,&IVC,8);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int AES::get_size(){
|
||||
return size;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::set_size(int sizel){
|
||||
size = sizel;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::get_IV(byte *out){
|
||||
memcpy(out,&IVC,8);
|
||||
memcpy(out+8,&IVC,8);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::calc_size_n_pad(int p_size){
|
||||
int s_of_p = p_size - 1;
|
||||
if ( s_of_p % N_BLOCK == 0){
|
||||
size = s_of_p;
|
||||
}else{
|
||||
size = s_of_p + (N_BLOCK-(s_of_p % N_BLOCK));
|
||||
}
|
||||
pad = size - s_of_p;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::padPlaintext(void* in,byte* out)
|
||||
{
|
||||
memcpy(out,in,size);
|
||||
for (int i = size-pad; i < size; i++){;
|
||||
out[i] = arr_pad[pad - 1];
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
bool AES::CheckPad(byte* in,int lsize){
|
||||
if (in[lsize-1] <= 0x0f){
|
||||
int lpad = (int)in[lsize-1];
|
||||
for (int i = lsize - 1; i >= lsize-lpad; i--){
|
||||
if (arr_pad[lpad - 1] != in[i]){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::printArray(byte output[],bool p_pad)
|
||||
{
|
||||
uint8_t i,j;
|
||||
uint8_t loops = size/N_BLOCK;
|
||||
uint8_t outp = N_BLOCK;
|
||||
for (j = 0; j < loops; j += 1){
|
||||
if (p_pad && (j == (loops - 1)) ) { outp = N_BLOCK - pad; }
|
||||
for (i = 0; i < outp; i++)
|
||||
{
|
||||
printf_P(PSTR("%c"),output[j*N_BLOCK + i]);
|
||||
}
|
||||
}
|
||||
printf_P(PSTR("\n"));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::printArray(byte output[],int sizel)
|
||||
{
|
||||
for (int i = 0; i < sizel; i++)
|
||||
{
|
||||
printf_P(PSTR("%x"),output[i]);
|
||||
}
|
||||
printf_P(PSTR("\n"));
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits, byte ivl [N_BLOCK]){
|
||||
calc_size_n_pad(size_p);
|
||||
byte plain_p[get_size()];
|
||||
padPlaintext(plain,plain_p);
|
||||
int blocks = get_size() / N_BLOCK;
|
||||
set_key (key, bits) ;
|
||||
cbc_encrypt (plain_p, cipher, blocks, ivl);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits){
|
||||
calc_size_n_pad(size_p);
|
||||
byte plain_p[get_size()];
|
||||
padPlaintext(plain,plain_p);
|
||||
int blocks = get_size() / N_BLOCK;
|
||||
set_key (key, bits) ;
|
||||
cbc_encrypt (plain_p, cipher, blocks);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits, byte ivl [N_BLOCK]){
|
||||
set_size(size_c);
|
||||
int blocks = size_c / N_BLOCK;
|
||||
set_key (key, bits);
|
||||
cbc_decrypt (cipher,plain, blocks, ivl);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits){
|
||||
set_size(size_c);
|
||||
int blocks = size_c / N_BLOCK;
|
||||
set_key (key, bits);
|
||||
cbc_decrypt (cipher,plain, blocks);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#if defined(AES_LINUX)
|
||||
double AES::millis(){
|
||||
gettimeofday(&tv, NULL);
|
||||
return (tv.tv_sec + 0.000001 * tv.tv_usec);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,416 @@
|
||||
#ifndef __AES_H__
|
||||
#define __AES_H__
|
||||
|
||||
#include "AES_config.h"
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
1. source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
2. binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation;
|
||||
|
||||
3. the name of the copyright holder is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue 09/09/2006
|
||||
|
||||
This is an AES implementation that uses only 8-bit byte operations on the
|
||||
cipher state.
|
||||
*/
|
||||
|
||||
/* code was modified by george spanos <spaniakos@gmail.com>
|
||||
* 16/12/14
|
||||
*/
|
||||
/** AES class */
|
||||
class AES
|
||||
{
|
||||
public:
|
||||
|
||||
/* The following calls are for a precomputed key schedule
|
||||
|
||||
NOTE: If the length_type used for the key length is an
|
||||
unsigned 8-bit character, a key length of 256 bits must
|
||||
be entered as a length in bytes (valid inputs are hence
|
||||
128, 192, 16, 24 and 32).
|
||||
*/
|
||||
/** \fn AES()
|
||||
* \brief AES constructor
|
||||
*
|
||||
* This function initialized an instance of AES.
|
||||
*/
|
||||
AES();
|
||||
|
||||
/** Set the cipher key for the pre-keyed version.
|
||||
* @param key[] pointer to the key string.
|
||||
* @param keylen Integer that indicates the length of the key.
|
||||
* @note NOTE: If the length_type used for the key length is an unsigned 8-bit character,
|
||||
* a key length of 256 bits must be entered as a length in bytes
|
||||
* (valid inputs are hence 128, 192, 16, 24 and 32).
|
||||
*
|
||||
*/
|
||||
byte set_key (byte key[], int keylen) ;
|
||||
|
||||
/** clean up subkeys after use.
|
||||
*
|
||||
*/
|
||||
void clean () ; // delete key schedule after use
|
||||
|
||||
/** copying and xoring utilities.
|
||||
* @param *AESt byte pointer of the AEStination array.
|
||||
* @param *src byte pointer of the source array.
|
||||
* @param n byte, indicating the sizeof the bytes to be copied.
|
||||
* @note this is an alternative for memcpy(void *s1,const void *s2, site_t n),
|
||||
* i have not updated the function in the implementation yet, but it is considered a future plan.
|
||||
*
|
||||
*/
|
||||
void copy_n_bytes (byte * AESt, byte * src, byte n) ;
|
||||
|
||||
/** Encrypt a single block of 16 bytes .
|
||||
* @param plain Array of the plaintext.
|
||||
* @param cipher Array of the ciphertext.
|
||||
* @note The N_BLOCK is defined in AES_config.h as,
|
||||
* @code #define N_ROW 4
|
||||
* #define N_COL 4
|
||||
* #define N_BLOCK (N_ROW * N_COL)
|
||||
* @endcode
|
||||
* Changed to that will change the Block_size.
|
||||
* @return 0 if SUCCESS or -1 if FAILURE
|
||||
*
|
||||
*/
|
||||
byte encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) ;
|
||||
|
||||
/** CBC encrypt a number of blocks (input and return an IV).
|
||||
*
|
||||
* @param *plain Pointer, points to the plaintex.
|
||||
* @param *cipher Pointer, points to the ciphertext that will be created.
|
||||
* @param n_block integer, indicated the number of blocks to be ciphered.
|
||||
* @param iv byte Array that holds the IV (initialization vector).
|
||||
* @return 0 if SUCCESS or -1 if FAILURE
|
||||
*
|
||||
*/
|
||||
byte cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK]) ;
|
||||
|
||||
/** CBC encrypt a number of blocks (input and return an IV).
|
||||
*
|
||||
* @param *plain Pointer, points to the plaintex.
|
||||
* @param *cipher Pointer, points to the ciphertext that will be created.
|
||||
* @param n_block integer, indicated the number of blocks to be ciphered.
|
||||
* @return 0 if SUCCESS or -1 if FAILURE
|
||||
*
|
||||
*/
|
||||
byte cbc_encrypt (byte * plain, byte * cipher, int n_block) ;
|
||||
|
||||
|
||||
/** Decrypt a single block of 16 bytes
|
||||
* @param cipher Array of the ciphertext.
|
||||
* @param plain Array of the plaintext.
|
||||
* @note The N_BLOCK is defined in AES_config.h as,
|
||||
* @code #define N_ROW 4
|
||||
* #define N_COL 4
|
||||
* #define N_BLOCK (N_ROW * N_COL)
|
||||
* @endcode
|
||||
* Changed to that will change the Block_size.
|
||||
* @return 0 if SUCCESS or -1 if FAILURE
|
||||
*
|
||||
*/
|
||||
byte decrypt (byte cipher [N_BLOCK], byte plain [N_BLOCK]) ;
|
||||
|
||||
/** CBC decrypt a number of blocks (input and return an IV)
|
||||
*
|
||||
* @param *cipher Pointer, points to the ciphertext that will be created.
|
||||
* @param *plain Pointer, points to the plaintex.
|
||||
* @param n_block integer, indicated the number of blocks to be ciphered.
|
||||
* @param iv byte Array that holds the IV (initialization vector).
|
||||
* @return 0 if SUCCESS or -1 if FAILURE
|
||||
*
|
||||
*/
|
||||
byte cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK]) ;
|
||||
|
||||
/** CBC decrypt a number of blocks (input and return an IV)
|
||||
*
|
||||
* @param *cipher Pointer, points to the ciphertext that will be created.
|
||||
* @param *plain Pointer, points to the plaintex.
|
||||
* @param n_block integer, indicated the number of blocks to be ciphered.
|
||||
* @return 0 if SUCCESS or -1 if FAILURE
|
||||
*
|
||||
*/
|
||||
byte cbc_decrypt (byte * cipher, byte * plain, int n_block) ;
|
||||
|
||||
/** Sets IV (initialization vector) and IVC (IV counter).
|
||||
* This function changes the ivc and iv variables needed for AES.
|
||||
*
|
||||
* @param IVCl int or hex value of iv , ex. 0x0000000000000001
|
||||
* @note example:
|
||||
* @code unsigned long long int my_iv = 01234567; @endcode
|
||||
*/
|
||||
void set_IV(unsigned long long int IVCl);
|
||||
|
||||
/** increase the iv (initialization vector) and IVC (IV counter) by 1
|
||||
*
|
||||
* This function increased the VI by one step in order to have a different IV each time
|
||||
*
|
||||
*/
|
||||
void iv_inc();
|
||||
|
||||
/** Getter method for size
|
||||
*
|
||||
* This function return the size
|
||||
* @return an integer, that is the size of the of the padded plaintext,
|
||||
* thus, the size of the ciphertext.
|
||||
*/
|
||||
int get_size();
|
||||
|
||||
/** Setter method for size
|
||||
*
|
||||
* This function sets the size of the plaintext+pad
|
||||
*
|
||||
*/
|
||||
void set_size(int sizel);
|
||||
|
||||
/** Getter method for IV
|
||||
*
|
||||
* This function return the IV
|
||||
* @param out byte pointer that gets the IV.
|
||||
* @return none, the IV is writed to the out pointer.
|
||||
*/
|
||||
void get_IV(byte *out);
|
||||
|
||||
/** Calculates the size of the plaintext and the padding.
|
||||
*
|
||||
* Calculates the size of theplaintext with the padding
|
||||
* and the size of the padding needed. Moreover it stores them in their class variables.
|
||||
*
|
||||
* @param p_size the size of the byte array ex sizeof(plaintext)
|
||||
*/
|
||||
void calc_size_n_pad(int p_size);
|
||||
|
||||
/** Pads the plaintext
|
||||
*
|
||||
* This function pads the plaintext and returns an char array with the
|
||||
* plaintext and the padding in order for the plaintext to be compatible with
|
||||
* 16bit size blocks required by AES
|
||||
*
|
||||
* @param in the string of the plaintext in a byte array
|
||||
* @param out The string of the out array.
|
||||
* @return no return, The padded plaintext is stored in the out pointer.
|
||||
*/
|
||||
void padPlaintext(void* in,byte* out);
|
||||
|
||||
/** Check the if the padding is correct.
|
||||
*
|
||||
* This functions checks the padding of the plaintext.
|
||||
*
|
||||
* @param in the string of the plaintext in a byte array
|
||||
* @param size the size of the string
|
||||
* @return true if correct / false if not
|
||||
*/
|
||||
bool CheckPad(byte* in,int size);
|
||||
|
||||
/** Prints the array given.
|
||||
*
|
||||
* This function prints the given array and pad,
|
||||
* It is mainlly used for debugging purpuses or to output the string.
|
||||
*
|
||||
* @param output[] the string of the text in a byte array
|
||||
* @param p_pad optional, used to print with out the padding characters
|
||||
*/
|
||||
void printArray(byte output[],bool p_pad = true);
|
||||
|
||||
/** Prints the array given.
|
||||
*
|
||||
* This function prints the given array in Hexadecimal.
|
||||
*
|
||||
* @param output[] the string of the text in a byte array
|
||||
* @param sizel the size of the array.
|
||||
*/
|
||||
void printArray(byte output[],int sizel);
|
||||
|
||||
/** User friendly implementation of AES-CBC encryption.
|
||||
*
|
||||
* @param *plain pointer to the plaintext
|
||||
* @param size_p size of the plaintext
|
||||
* @param *cipher pointer to the ciphertext
|
||||
* @param *key pointer to the key that will be used.
|
||||
* @param bits bits of the encryption/decrpytion
|
||||
* @param ivl the initialization vector IV that will be used for encryption.
|
||||
* @note The key will be stored in class variable.
|
||||
*/
|
||||
void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits, byte ivl [N_BLOCK]);
|
||||
|
||||
/** User friendly implementation of AES-CBC encryption.
|
||||
*
|
||||
* @param *plain pointer to the plaintext
|
||||
* @param size_p size of the plaintext
|
||||
* @param *cipher pointer to the ciphertext
|
||||
* @param *key pointer to the key that will be used.
|
||||
* @param bits bits of the encryption/decrpytion
|
||||
* @note The key will be stored in class variable.
|
||||
*/
|
||||
void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits);
|
||||
|
||||
/** User friendly implementation of AES-CBC decryption.
|
||||
*
|
||||
* @param *cipher pointer to the ciphertext
|
||||
* @param size_c size of the ciphertext
|
||||
* @param *plain pointer to the plaintext
|
||||
* @param *key pointer to the key that will be used.
|
||||
* @param bits bits of the encryption/decrpytion
|
||||
* @param ivl the initialization vector IV that will be used for decryption.
|
||||
* @note The key will be stored in class variable.
|
||||
*/
|
||||
void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits, byte ivl [N_BLOCK]);
|
||||
|
||||
/** User friendly implementation of AES-CBC decryption.
|
||||
*
|
||||
* @param *cipher pointer to the ciphertext
|
||||
* @param size_c size of the ciphertext
|
||||
* @param *plain pointer to the plaintext
|
||||
* @param *key pointer to the key that will be used.
|
||||
* @param bits bits of the encryption/decrpytion
|
||||
* @note The key will be stored in class variable.
|
||||
*/
|
||||
void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits);
|
||||
|
||||
#if defined(AES_LINUX)
|
||||
/**
|
||||
* used in linux in order to retrieve the time in milliseconds.
|
||||
*
|
||||
* @return returns the milliseconds in a double format.
|
||||
*/
|
||||
double millis();
|
||||
#endif
|
||||
private:
|
||||
int round ;/**< holds the number of rounds to be used. */
|
||||
byte key_sched [KEY_SCHEDULE_BYTES] ;/**< holds the pre-computed key for the encryption/decrpytion. */
|
||||
unsigned long long int IVC;/**< holds the initialization vector counter in numerical format. */
|
||||
byte iv[16];/**< holds the initialization vector that will be used in the cipher. */
|
||||
int pad;/**< holds the size of the padding. */
|
||||
int size;/**< hold the size of the plaintext to be ciphered */
|
||||
#if defined(AES_LINUX)
|
||||
timeval tv;/**< holds the time value on linux */
|
||||
byte arr_pad[15];/**< holds the hexadecimal padding values on linux */
|
||||
#else
|
||||
byte arr_pad[15];// = { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };/**< holds the hexadecimal padding values */
|
||||
#endif
|
||||
} ;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @example aes.pde
|
||||
* <b>For Arduino</b><br>
|
||||
* <b>Updated: spaniakos 2015 </b><br>
|
||||
*
|
||||
* This is an example of how to use AES in CBC mode easily.
|
||||
* The text and keys can be either in HEX or String format.<br />
|
||||
*/
|
||||
|
||||
/**
|
||||
* @example aes.cpp
|
||||
* <b>For Rasberry pi</b><br>
|
||||
* <b>Updated: spaniakos 2015 </b><br>
|
||||
*
|
||||
* This is an example of how to use AES in CBC mode easily.
|
||||
* The text and keys can be either in HEX or String format.<br />
|
||||
*/
|
||||
|
||||
/**
|
||||
* @example test_vectors.pde
|
||||
* <b>For Arduino</b><br>
|
||||
* <b>Updated: spaniakos 2015 </b><br>
|
||||
*
|
||||
* This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.<br />
|
||||
* plus is a classical approach to the AES encryption library with out the easy to use add-on modifications.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @example test_vectors.cpp
|
||||
* <b>For Rasberry pi</b><br>
|
||||
* <b>Updated: spaniakos 2015 </b><br>
|
||||
*
|
||||
* This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.<br />
|
||||
* plus is a classical approach to the AES encryption library with out the easy to use add-on modifications.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup aeslib AES library for Arduino and Raspberry pi
|
||||
* @ingroup internals
|
||||
*
|
||||
* @section AesGoals design Goals
|
||||
*
|
||||
* This library is AESigned to be...
|
||||
* @li Fast and efficient.
|
||||
* @li Able to effectively encrypt and decrypt any size of string.
|
||||
* @li Able to encrypt and decrypt using AES
|
||||
* @li Able to encrypt and decrypt using AES-CBC
|
||||
* @li Easy for the user to use in his programs.
|
||||
*
|
||||
* @section Acknowledgements Acknowledgements
|
||||
* This is an AES library for the Arduino, based on tzikis's AES library, which you can find <a href= "https://github.com/tzikis/arduino">here:</a>.<br />
|
||||
* Tzikis library was based on scottmac`s library, which you can find <a href="https://github.com/scottmac/arduino">here:</a><br />
|
||||
*
|
||||
* @section Installation Installation
|
||||
* <h3>Arduino</h3>
|
||||
* Create a folder named _AES_ in the _libraries_ folder inside your Arduino sketch folder. If the
|
||||
* libraries folder doesn't exist, create it. Then copy everything inside. (re)launch the Arduino IDE.<br />
|
||||
* You're done. Time for a mojito
|
||||
*
|
||||
* <h3>Raspberry pi</h3>
|
||||
* <b>install</b><br /><br />
|
||||
*
|
||||
* sudo make install<br />
|
||||
* cd examples_Rpi<br />
|
||||
* make<br /><br />
|
||||
*
|
||||
* <b>What to do after changes to the library</b><br /><br />
|
||||
* sudo make clean<br />
|
||||
* sudo make install<br />
|
||||
* cd examples_Rpi<br />
|
||||
* make clean<br />
|
||||
* make<br /><br />
|
||||
* <b>What to do after changes to a sketch</b><br /><br />
|
||||
* cd examples_Rpi<br />
|
||||
* make \<sketch\><br /><br />
|
||||
* or <br />
|
||||
* make clean<br />
|
||||
* make<br /><br /><br />
|
||||
* <b>How to start a sketch</b><br /><br />
|
||||
* cd examples_Rpi<br />
|
||||
* sudo ./\<sketch\><br /><br />
|
||||
*
|
||||
* @section AesNews News
|
||||
*
|
||||
* If issues are discovered with the documentation, please report them <a href="https://github.com/spaniakos/spaniakos.github.io/issues"> here</a>
|
||||
* @section AesUseful Useful References
|
||||
*
|
||||
* Please refer to:
|
||||
*
|
||||
* @li <a href="http://spaniakos.github.io/AES/classAES.html"><b>AES</b> Class Documentation</a>
|
||||
* @li <a href="https://github.com/spaniakos/AES/archive/master.zip"><b>Download</b></a>
|
||||
* @li <a href="https://github.com/spaniakos/AES/"><b>Source Code</b></a>
|
||||
* @li <a href="http://spaniakos.github.io/">All spaniakos Documentation Main Page</a>
|
||||
*
|
||||
* @section AesBoard_Support Board Support
|
||||
*
|
||||
* Most standard Arduino based boards are supported:
|
||||
* - Arduino
|
||||
* - Intel Galileo support
|
||||
* - Raspberry Pi Support
|
||||
*
|
||||
* - The library has not been tested to other boards, but it should suppport ATMega 328 based boards,Mega Boards,Arduino Due,ATTiny board
|
||||
*/
|
||||
@@ -0,0 +1,48 @@
|
||||
/* code was modified by george spanos <spaniakos@gmail.com>
|
||||
* 16/12/14
|
||||
*/
|
||||
|
||||
#ifndef __AES_CONFIG_H__
|
||||
#define __AES_CONFIG_H__
|
||||
|
||||
#if (defined(__linux) || defined(linux)) && !defined(__ARDUINO_X86__)
|
||||
|
||||
#define AES_LINUX
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__ARDUINO_X86__) || (defined (__linux) || defined (linux))
|
||||
#undef PROGMEM
|
||||
#define PROGMEM __attribute__(( section(".progmem.data") ))
|
||||
#define pgm_read_byte(p) (*(p))
|
||||
typedef unsigned char byte;
|
||||
#define printf_P printf
|
||||
#define PSTR(x) (x)
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
#include <pgmspace.h>
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#define printf_P printf
|
||||
#else
|
||||
#include <avr/pgmspace.h>
|
||||
#endif
|
||||
|
||||
#define N_ROW 4
|
||||
#define N_COL 4
|
||||
#define N_BLOCK (N_ROW * N_COL)
|
||||
#define N_MAX_ROUNDS 14
|
||||
#define KEY_SCHEDULE_BYTES ((N_MAX_ROUNDS + 1) * N_BLOCK)
|
||||
#define AES_SUCCESS (0)
|
||||
#define AES_FAILURE (-1)
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
#############################################################################
|
||||
#
|
||||
# Makefile for AES on Raspberry Pi
|
||||
#
|
||||
# License: GPL (General Public License)
|
||||
# Author: Georgios Spanos (spaniakos) <spaniakos@gmail.com>
|
||||
# Date: 02/12/2014
|
||||
#
|
||||
# Description:
|
||||
# ------------
|
||||
# use make all and mak install to install the library
|
||||
# You can change the install directory by editing the LIBDIR line
|
||||
#
|
||||
PREFIX=/usr/local
|
||||
|
||||
# Library parameters
|
||||
# where to put the lib
|
||||
LIBDIR=$(PREFIX)/lib
|
||||
# lib name
|
||||
LIB=libAES
|
||||
# shared library name
|
||||
LIBNAME=$(LIB).so.1.0
|
||||
|
||||
# Where to put the header files
|
||||
HEADER_DIR=${PREFIX}/include/AES
|
||||
|
||||
# The recommended compiler flags for the Raspberry Pi
|
||||
CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
|
||||
|
||||
# make all
|
||||
# reinstall the library after each recompilation
|
||||
all: libAES
|
||||
|
||||
# Make the library
|
||||
libAES: AES.o
|
||||
g++ -shared -Wl,-soname,$@.so.1 ${CCFLAGS} -o ${LIBNAME} $^
|
||||
|
||||
# Library parts
|
||||
AES.o: AES.cpp
|
||||
g++ -Wall -fPIC ${CCFLAGS} -c $^
|
||||
|
||||
# clear build files
|
||||
clean:
|
||||
rm -rf *.o ${LIB}.*
|
||||
rm -rf ${LIBDIR}/${LIB}.*
|
||||
rm -rf ${HEADER_DIR}
|
||||
|
||||
install: all install-libs install-headers
|
||||
|
||||
# Install the library to LIBPATH
|
||||
install-libs:
|
||||
@echo "[Installing Libs]"
|
||||
@if ( test ! -d $(PREFIX)/lib ) ; then mkdir -p $(PREFIX)/lib ; fi
|
||||
@install -m 0755 ${LIBNAME} ${LIBDIR}
|
||||
@ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so.1
|
||||
@ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so
|
||||
@ldconfig
|
||||
|
||||
install-headers:
|
||||
@echo "[Installing Headers]"
|
||||
@if ( test ! -d ${HEADER_DIR} ) ; then mkdir -p ${HEADER_DIR} ; fi
|
||||
@install -m 0644 *.h ${HEADER_DIR}
|
||||
@@ -0,0 +1,91 @@
|
||||
### Arduino
|
||||
just run the sketched aftel loading the libraries.
|
||||
|
||||
### Raspberry pi
|
||||
install
|
||||
```
|
||||
sudo make install
|
||||
cd examples_Rpi
|
||||
make
|
||||
```
|
||||
|
||||
What to do after changes to the library
|
||||
```
|
||||
sudo make clean
|
||||
sudo make install
|
||||
cd examples_Rpi
|
||||
make clean
|
||||
make
|
||||
```
|
||||
|
||||
What to do after changes to a sketch
|
||||
```
|
||||
cd examples_Rpi
|
||||
make <sketch>
|
||||
|
||||
or
|
||||
make clean
|
||||
make
|
||||
```
|
||||
|
||||
How to start a sketch
|
||||
```
|
||||
cd examples_Rpi
|
||||
sudo ./<sketch>
|
||||
```
|
||||
|
||||
This is a derived work from Brian Gladman's AES implementation for byte-oriented
|
||||
processors, here's the licence terms:
|
||||
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
1. source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
2. binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation;
|
||||
|
||||
3. the name of the copyright holder is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue 09/09/2006
|
||||
|
||||
This is an AES implementation that uses only 8-bit byte operations on the
|
||||
cipher state (there are options to use 32-bit types if available).
|
||||
|
||||
The combination of mix columns and byte substitution used here is based on
|
||||
that developed by Karl Malbrain. His contribution is acknowledged.
|
||||
*/
|
||||
|
||||
/* This version derived by Mark Tillotson 2012-01-23, tidied up, slimmed down
|
||||
and tailored to 8-bit microcontroller abilities and Arduino datatypes.
|
||||
|
||||
The s-box and inverse s-box were retained as tables (0.5kB PROGMEM) but all
|
||||
the other transformations are coded to save table space. Many efficiency
|
||||
improvments to the routines mix_sub_columns() and inv_mix_sub_columns()
|
||||
(mainly common sub-expression elimination).
|
||||
|
||||
Only the routines with precalculated subkey schedule are retained (together
|
||||
with set_key() - this does however mean each AES object takes 240 bytes of
|
||||
RAM, alas)
|
||||
|
||||
The CBC routines side-effect the iv argument (so that successive calls work
|
||||
together correctly).
|
||||
|
||||
All the encryption and decryption routines work with plain == cipher for
|
||||
in-place encryption, note.
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <AES.h>
|
||||
#include "./printf.h"
|
||||
|
||||
AES aes ;
|
||||
|
||||
byte *key = (unsigned char*)"0123456789010123";
|
||||
|
||||
byte plain[] = "Add NodeAdd NodeAdd NodeAdd NodeAdd Node";
|
||||
|
||||
//real iv = iv x2 ex: 01234567 = 0123456701234567
|
||||
unsigned long long int my_iv = 36753562;
|
||||
|
||||
void setup ()
|
||||
{
|
||||
Serial.begin (57600) ;
|
||||
printf_begin();
|
||||
delay(500);
|
||||
printf("\n===testng mode\n") ;
|
||||
|
||||
// otfly_test () ;
|
||||
// otfly_test256 () ;
|
||||
}
|
||||
|
||||
void loop ()
|
||||
{
|
||||
prekey_test () ;
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
void prekey (int bits)
|
||||
{
|
||||
aes.iv_inc();
|
||||
byte iv [N_BLOCK] ;
|
||||
byte plain_p[48];
|
||||
byte cipher [48] ;
|
||||
byte check [48] ;
|
||||
unsigned long ms = micros ();
|
||||
aes.set_IV(my_iv);
|
||||
aes.get_IV(iv);
|
||||
aes.do_aes_encrypt(plain,41,cipher,key,bits,iv);
|
||||
Serial.print("Encryption took: ");
|
||||
Serial.println(micros() - ms);
|
||||
ms = micros ();
|
||||
aes.set_IV(my_iv);
|
||||
aes.get_IV(iv);
|
||||
aes.do_aes_decrypt(cipher,48,check,key,bits,iv);
|
||||
Serial.print("Decryption took: ");
|
||||
Serial.println(micros() - ms);
|
||||
printf("\n\nPLAIN :");
|
||||
aes.printArray(plain,(bool)true);
|
||||
printf("\nCIPHER:");
|
||||
aes.printArray(cipher,(bool)false);
|
||||
printf("\nCHECK :");
|
||||
aes.printArray(check,(bool)true);
|
||||
printf("\nIV :");
|
||||
aes.printArray(iv,16);
|
||||
printf("\n============================================================\n");
|
||||
}
|
||||
|
||||
void prekey_test ()
|
||||
{
|
||||
prekey (128) ;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
Monte Carlo test vectors:
|
||||
http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmct.zip
|
||||
|
||||
(Compare the Monte Carlo output to the ECBMCT128.rsp file)
|
||||
|
||||
Known Answer Tests:
|
||||
http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip
|
||||
|
||||
- Compare the other test outputs to
|
||||
ECBVarTxt128.rsp
|
||||
ECBVarKey128.rsp
|
||||
ECBVarTxt192.rsp
|
||||
ECBVarKey192.rsp
|
||||
ECBVarTxt256.rsp
|
||||
ECBVarKey256.rsp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,191 @@
|
||||
#include <AES.h>
|
||||
|
||||
AES aes ;
|
||||
|
||||
byte key [2*N_BLOCK] ;
|
||||
byte plain [N_BLOCK] ;
|
||||
byte iv [N_BLOCK] ;
|
||||
byte cipher [N_BLOCK] ;
|
||||
byte check [N_BLOCK] ;
|
||||
|
||||
void loop ()
|
||||
{}
|
||||
|
||||
|
||||
void setup ()
|
||||
{
|
||||
Serial.begin (57600) ;
|
||||
Serial.print ("AES library test vectors") ;
|
||||
|
||||
monte_carlo (128) ;
|
||||
|
||||
for (int keysize = 128 ; keysize <= 256 ; keysize += 64)
|
||||
{
|
||||
prekey_test_var_plaintext (keysize) ;
|
||||
prekey_test_var_key (keysize) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void prekey_test_var_plaintext (int bits)
|
||||
{
|
||||
Serial.println () ;
|
||||
Serial.print ("ECB Varying Plaintext ") ;
|
||||
Serial.print (bits) ;
|
||||
Serial.println (" bits") ;
|
||||
Serial.println () ;
|
||||
|
||||
byte succ ;
|
||||
set_bits (bits, key, 0) ; // all zero key
|
||||
succ = aes.set_key (key, bits) ;
|
||||
if (succ != SUCCESS)
|
||||
Serial.println ("Failure set_key") ;
|
||||
|
||||
|
||||
for (int bitcount = 1 ; bitcount <= 128 ; bitcount++)
|
||||
{
|
||||
Serial.print ("COUNT = ") ; Serial.println (bitcount-1) ;
|
||||
print_value ("KEY = ", key, bits) ;
|
||||
set_bits (128, plain, bitcount) ;
|
||||
|
||||
print_value ("PLAINTEXT = ", plain, 128) ;
|
||||
|
||||
succ = aes.encrypt (plain, cipher) ;
|
||||
if (succ != SUCCESS)
|
||||
Serial.println ("Failure encrypt") ;
|
||||
|
||||
print_value ("CIPHERTEXT = ", cipher, 128) ;
|
||||
|
||||
succ = aes.decrypt (cipher, check) ;
|
||||
if (succ != SUCCESS)
|
||||
Serial.println ("Failure decrypt") ;
|
||||
|
||||
//print_value ("CHECK = ", check, 128) ;
|
||||
check_same (plain, check, 128) ;
|
||||
Serial.println () ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void prekey_test_var_key (int bits)
|
||||
{
|
||||
Serial.println () ;
|
||||
Serial.print ("ECB Varying Key ") ;
|
||||
Serial.print (bits) ;
|
||||
Serial.println (" bits") ;
|
||||
Serial.println () ;
|
||||
|
||||
byte succ ;
|
||||
set_bits (128, plain, 0) ;
|
||||
|
||||
for (int bitcount = 1 ; bitcount <= bits ; bitcount++)
|
||||
{
|
||||
set_bits (bits, key, bitcount) ; // all zero key
|
||||
succ = aes.set_key (key, bits) ;
|
||||
if (succ != SUCCESS)
|
||||
Serial.println ("Failure set_key") ;
|
||||
Serial.print ("COUNT = ") ; Serial.println (bitcount-1) ;
|
||||
print_value ("KEY = ", key, bits) ;
|
||||
|
||||
print_value ("PLAINTEXT = ", plain, 128) ;
|
||||
|
||||
succ = aes.encrypt (plain, cipher) ;
|
||||
if (succ != SUCCESS)
|
||||
Serial.println ("Failure encrypt") ;
|
||||
|
||||
print_value ("CIPHERTEXT = ", cipher, 128) ;
|
||||
|
||||
succ = aes.decrypt (cipher, check) ;
|
||||
if (succ != SUCCESS)
|
||||
Serial.println ("Failure decrypt") ;
|
||||
|
||||
check_same (plain, check, 128) ;
|
||||
Serial.println () ;
|
||||
}
|
||||
}
|
||||
|
||||
void set_bits (int bits, byte * a, int count)
|
||||
{
|
||||
bits >>= 3 ;
|
||||
byte bcount = count >> 3 ;
|
||||
for (byte i = 0 ; i < bcount ; i++)
|
||||
a [i] = 0xFF ;
|
||||
if ((count & 7) != 0)
|
||||
a [bcount++] = 0xFF & (0xFF00 >> (count & 7)) ;
|
||||
for (byte i = bcount ; i < bits ; i++)
|
||||
a [i] = 0x00 ;
|
||||
}
|
||||
|
||||
void check_same (byte * a, byte * b, int bits)
|
||||
{
|
||||
bits >>= 3 ;
|
||||
for (byte i = 0 ; i < bits ; i++)
|
||||
if (a[i] != b[i])
|
||||
{
|
||||
Serial.println ("Failure plain != check") ;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
char hex[] = "0123456789abcdef" ;
|
||||
|
||||
|
||||
void print_value (const char * str, byte * a, int bits)
|
||||
{
|
||||
Serial.print (str) ;
|
||||
bits >>= 3 ;
|
||||
for (int i = 0 ; i < bits ; i++)
|
||||
{
|
||||
byte b = a[i] ;
|
||||
Serial.print (hex [b >> 4]) ;
|
||||
Serial.print (hex [b & 15]) ;
|
||||
}
|
||||
Serial.println () ;
|
||||
}
|
||||
|
||||
byte monteplain [] =
|
||||
{ 0xb9, 0x14, 0x5a, 0x76, 0x8b, 0x7d, 0xc4, 0x89,
|
||||
0xa0, 0x96, 0xb5, 0x46, 0xf4, 0x3b, 0x23, 0x1f } ;
|
||||
byte montekey [] =
|
||||
{ 0x13, 0x9a, 0x35, 0x42, 0x2f, 0x1d, 0x61, 0xde,
|
||||
0x3c, 0x91, 0x78, 0x7f, 0xe0, 0x50, 0x7a, 0xfd } ;
|
||||
|
||||
void monte_carlo (int bits)
|
||||
{
|
||||
Serial.println () ;
|
||||
Serial.print ("Monte Carlo ") ;
|
||||
Serial.print (bits) ;
|
||||
Serial.println (" bits") ;
|
||||
Serial.println () ;
|
||||
byte succ ;
|
||||
for (int i = 0 ; i < 16 ; i++)
|
||||
{
|
||||
plain [i] = monteplain [i] ;
|
||||
key [i] = montekey [i] ;
|
||||
}
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
Serial.print ("COUNT = ") ; Serial.println (i) ;
|
||||
print_value ("KEY = ", key, bits) ;
|
||||
print_value ("PLAINTEXT = ", plain, 128) ;
|
||||
succ = aes.set_key (key, bits) ;
|
||||
for (int j = 0 ; j < 1000 ; j++)
|
||||
{
|
||||
succ = aes.encrypt (plain, cipher) ;
|
||||
aes.copy_n_bytes (plain, cipher, 16) ;
|
||||
}
|
||||
print_value ("CIPHERTEXT = ", cipher, 128) ;
|
||||
Serial.println () ;
|
||||
if (bits == 128)
|
||||
{
|
||||
for (byte k = 0 ; k < 16 ; k++)
|
||||
key [k] ^= cipher [k] ;
|
||||
}
|
||||
else if (bits == 192)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#############################################################################
|
||||
#
|
||||
# Makefile for AES examples on Raspberry Pi
|
||||
#
|
||||
# License: GPL (General Public License)
|
||||
# Author: gnulnulf <arco@appeltaart.mine.nu>
|
||||
# Date: 2013/02/07 (version 1.0)
|
||||
#
|
||||
# Description:
|
||||
# ------------
|
||||
# use make all and make install to install the examples
|
||||
# You can change the install directory by editing the prefix line
|
||||
#
|
||||
prefix := /usr/local
|
||||
|
||||
# The recommended compiler flags for the Raspberry Pi
|
||||
CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
|
||||
#CCFLAGS=
|
||||
|
||||
# define all programs
|
||||
PROGRAMS = aes test_vectors
|
||||
SOURCES = ${PROGRAMS:=.cpp}
|
||||
|
||||
all: ${PROGRAMS}
|
||||
|
||||
${PROGRAMS}: ${SOURCES}
|
||||
g++ ${CCFLAGS} -Wall -I../ -lAES $@.cpp -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(PROGRAMS)
|
||||
|
||||
install: all
|
||||
test -d $(prefix) || mkdir $(prefix)
|
||||
test -d $(prefix)/bin || mkdir $(prefix)/bin
|
||||
for prog in $(PROGRAMS); do \
|
||||
install -m 0755 $$prog $(prefix)/bin; \
|
||||
done
|
||||
|
||||
.PHONY: install
|
||||
@@ -0,0 +1,16 @@
|
||||
Monte Carlo test vectors:
|
||||
http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmct.zip
|
||||
|
||||
(Compare the Monte Carlo output to the ECBMCT128.rsp file)
|
||||
|
||||
Known Answer Tests:
|
||||
http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip
|
||||
|
||||
- Compare the other test outputs to
|
||||
ECBVarTxt128.rsp
|
||||
ECBVarKey128.rsp
|
||||
ECBVarTxt192.rsp
|
||||
ECBVarKey192.rsp
|
||||
ECBVarTxt256.rsp
|
||||
ECBVarKey256.rsp
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#include <AES.h>
|
||||
#include "printf.h"
|
||||
|
||||
AES aes;
|
||||
|
||||
void prekey_test ();
|
||||
void prekey (int bits, int blocks);
|
||||
|
||||
byte key[] = "01234567899876543210012345678998";
|
||||
|
||||
byte plain[] = "TESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTEST";
|
||||
|
||||
//real iv = iv x2 ex: 01234567 = 0123456701234567
|
||||
unsigned long long int my_iv = 01234567;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("\n===testng mode\n") ;
|
||||
|
||||
for (int i=0;i<1;i++){
|
||||
prekey_test () ;
|
||||
}
|
||||
}
|
||||
|
||||
void prekey (int bits)
|
||||
{
|
||||
byte iv [N_BLOCK] ;
|
||||
byte plain_p[sizeof(plain) + (N_BLOCK - (sizeof(plain) % 16)) - 1];
|
||||
byte cipher[sizeof(plain_p)];
|
||||
aes.do_aes_encrypt(plain,sizeof(plain),cipher,key,bits);
|
||||
aes.get_IV(iv);
|
||||
aes.do_aes_dencrypt(cipher,aes.get_size(),plain_p,key,bits,iv);
|
||||
//normally u have sizeof(cipher) but if its in the same sketch you cannot determin it dynamically
|
||||
|
||||
printf("\n\nPLAIN :");
|
||||
aes.printArray(plain);
|
||||
printf("\nCIPHER:");
|
||||
aes.printArray(cipher);
|
||||
printf("\nPlain2:");
|
||||
aes.printArray(plain_p);
|
||||
printf("\n============================================================\n");
|
||||
}
|
||||
|
||||
void prekey_test ()
|
||||
{
|
||||
prekey (128) ;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,183 @@
|
||||
#include <AES.h>
|
||||
#include "printf.h"
|
||||
|
||||
AES aes ;
|
||||
|
||||
byte key [2*N_BLOCK] ;
|
||||
byte plain [N_BLOCK] ;
|
||||
byte iv [N_BLOCK] ;
|
||||
byte cipher [N_BLOCK] ;
|
||||
byte check [N_BLOCK] ;
|
||||
|
||||
void print_value (const char * str, byte * a, int bits);
|
||||
void prekey_test_var_plaintext (int bits);
|
||||
void prekey_test_var_key (int bits);
|
||||
void set_bits (int bits, byte * a, int count);
|
||||
void check_same (byte * a, byte * b, int bits);
|
||||
void monte_carlo (int bits);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf ("AES library test vectors") ;
|
||||
|
||||
monte_carlo (128) ;
|
||||
|
||||
for (int keysize = 128 ; keysize <= 256 ; keysize += 64)
|
||||
{
|
||||
prekey_test_var_plaintext (keysize) ;
|
||||
prekey_test_var_key (keysize) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void prekey_test_var_plaintext (int bits)
|
||||
{
|
||||
printf ("\nECB Varying Plaintext %i bits\n",bits) ;
|
||||
|
||||
byte succ ;
|
||||
set_bits (bits, key, 0) ; // all zero key
|
||||
succ = aes.set_key (key, bits) ;
|
||||
if (succ != SUCCESS)
|
||||
printf("Failure set_key\n") ;
|
||||
|
||||
|
||||
for (int bitcount = 1 ; bitcount <= 128 ; bitcount++)
|
||||
{
|
||||
printf ("COUNT = %i \n",bitcount-1);
|
||||
print_value ("KEY = ", key, bits) ;
|
||||
set_bits (128, plain, bitcount) ;
|
||||
|
||||
print_value ("PLAINTEXT = ", plain, 128) ;
|
||||
|
||||
succ = aes.encrypt (plain, cipher) ;
|
||||
if (succ != SUCCESS)
|
||||
printf ("Failure encrypt\n") ;
|
||||
|
||||
print_value ("CIPHERTEXT = ", cipher, 128) ;
|
||||
|
||||
succ = aes.decrypt (cipher, check) ;
|
||||
if (succ != SUCCESS)
|
||||
printf ("Failure decrypt\n") ;
|
||||
|
||||
//print_value ("CHECK = ", check, 128) ;
|
||||
check_same (plain, check, 128) ;
|
||||
printf ("\n") ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void prekey_test_var_key (int bits)
|
||||
{
|
||||
printf ("\nECB Varying key %i bits\n",bits);
|
||||
|
||||
byte succ ;
|
||||
set_bits (128, plain, 0) ;
|
||||
|
||||
for (int bitcount = 1 ; bitcount <= bits ; bitcount++)
|
||||
{
|
||||
set_bits (bits, key, bitcount) ; // all zero key
|
||||
succ = aes.set_key (key, bits) ;
|
||||
if (succ != SUCCESS)
|
||||
printf ("Failure set_key\n") ;
|
||||
printf ("COUNT = %i\n",bitcount-1);
|
||||
print_value ("KEY = ", key, bits) ;
|
||||
|
||||
print_value ("PLAINTEXT = ", plain, 128) ;
|
||||
|
||||
succ = aes.encrypt (plain, cipher) ;
|
||||
if (succ != SUCCESS)
|
||||
printf ("Failure encrypt\n") ;
|
||||
|
||||
print_value ("CIPHERTEXT = ", cipher, 128) ;
|
||||
|
||||
succ = aes.decrypt (cipher, check) ;
|
||||
if (succ != SUCCESS)
|
||||
printf ("Failure decrypt\n") ;
|
||||
|
||||
check_same (plain, check, 128) ;
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void set_bits (int bits, byte * a, int count)
|
||||
{
|
||||
bits >>= 3 ;
|
||||
byte bcount = count >> 3 ;
|
||||
for (byte i = 0 ; i < bcount ; i++)
|
||||
a [i] = 0xFF ;
|
||||
if ((count & 7) != 0)
|
||||
a [bcount++] = 0xFF & (0xFF00 >> (count & 7)) ;
|
||||
for (byte i = bcount ; i < bits ; i++)
|
||||
a [i] = 0x00 ;
|
||||
}
|
||||
|
||||
void check_same (byte * a, byte * b, int bits)
|
||||
{
|
||||
bits >>= 3 ;
|
||||
for (byte i = 0 ; i < bits ; i++)
|
||||
if (a[i] != b[i])
|
||||
{
|
||||
printf ("Failure plain != check\n") ;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
char hex[] = "0123456789abcdef" ;
|
||||
|
||||
|
||||
void print_value (const char * str, byte * a, int bits)
|
||||
{
|
||||
printf ("%s",str) ;
|
||||
bits >>= 3 ;
|
||||
for (int i = 0 ; i < bits ; i++)
|
||||
{
|
||||
byte b = a[i] ;
|
||||
printf("%c%c",hex[b >> 4],hex [b & 15]);
|
||||
//Serial.print (hex [b >> 4]) ;
|
||||
//Serial.print (hex [b & 15]) ;
|
||||
}
|
||||
printf("\n") ;
|
||||
}
|
||||
|
||||
byte monteplain [] =
|
||||
{ 0xb9, 0x14, 0x5a, 0x76, 0x8b, 0x7d, 0xc4, 0x89,
|
||||
0xa0, 0x96, 0xb5, 0x46, 0xf4, 0x3b, 0x23, 0x1f } ;
|
||||
byte montekey [] =
|
||||
{ 0x13, 0x9a, 0x35, 0x42, 0x2f, 0x1d, 0x61, 0xde,
|
||||
0x3c, 0x91, 0x78, 0x7f, 0xe0, 0x50, 0x7a, 0xfd } ;
|
||||
|
||||
void monte_carlo (int bits)
|
||||
{
|
||||
printf ("\nMonte Carlo %i bits\n",bits);
|
||||
byte succ;
|
||||
for (int i = 0 ; i < 16 ; i++)
|
||||
{
|
||||
plain [i] = monteplain [i] ;
|
||||
key [i] = montekey [i] ;
|
||||
}
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
printf ("COUNT = %i\n",i);
|
||||
print_value ("KEY = ", key, bits) ;
|
||||
print_value ("PLAINTEXT = ", plain, 128) ;
|
||||
succ = aes.set_key (key, bits) ;
|
||||
for (int j = 0 ; j < 1000 ; j++)
|
||||
{
|
||||
succ = aes.encrypt (plain, cipher) ;
|
||||
aes.copy_n_bytes (plain, cipher, 16) ;
|
||||
}
|
||||
print_value ("CIPHERTEXT = ", cipher, 128) ;
|
||||
printf("\n");
|
||||
if (bits == 128)
|
||||
{
|
||||
for (byte k = 0 ; k < 16 ; k++)
|
||||
key [k] ^= cipher [k] ;
|
||||
}
|
||||
else if (bits == 192)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
AES KEYWORD1
|
||||
set_key KEYWORD2
|
||||
clean KEYWORD2
|
||||
encrypt KEYWORD2
|
||||
decrypt KEYWORD2
|
||||
cbc_encrypt KEYWORD2
|
||||
cbc_decrypt KEYWORD2
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
/* Galileo support from spaniakos <spaniakos@gmail.com> */
|
||||
|
||||
/**
|
||||
* @file printf.h
|
||||
*
|
||||
* Setup necessary to direct stdout to the Arduino Serial library, which
|
||||
* enables 'printf'
|
||||
*/
|
||||
|
||||
#ifndef __PRINTF_H__
|
||||
#define __PRINTF_H__
|
||||
|
||||
#if defined (ARDUINO) && !defined (__arm__) && !defined(__ARDUINO_X86__)
|
||||
|
||||
int serial_putc( char c, FILE * )
|
||||
{
|
||||
Serial.write( c );
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void printf_begin(void)
|
||||
{
|
||||
fdevopen( &serial_putc, 0 );
|
||||
}
|
||||
|
||||
#elif defined (__arm__)
|
||||
|
||||
void printf_begin(void){}
|
||||
|
||||
#elif defined(__ARDUINO_X86__)
|
||||
int serial_putc( char c, FILE * )
|
||||
{
|
||||
Serial.write( c );
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void printf_begin(void)
|
||||
{
|
||||
//JESUS - For reddirect stdout to /dev/ttyGS0 (Serial Monitor port)
|
||||
stdout = freopen("/dev/ttyGS0","w",stdout);
|
||||
delay(500);
|
||||
printf("redirecting to Serial...");
|
||||
|
||||
//JESUS -----------------------------------------------------------
|
||||
}
|
||||
#else
|
||||
#error This example is only for use on Arduino.
|
||||
#endif // ARDUINO
|
||||
|
||||
#endif // __PRINTF_H__
|
||||
@@ -0,0 +1,600 @@
|
||||
#include "Arduino.h"
|
||||
#include "ATSHA204.h"
|
||||
|
||||
/* Local data and function prototypes */
|
||||
|
||||
static uint8_t device_pin;
|
||||
#ifdef ARDUINO_ARCH_AVR
|
||||
static volatile uint8_t *device_port_DDR, *device_port_OUT, *device_port_IN;
|
||||
#endif
|
||||
static void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc);
|
||||
static uint8_t sha204c_check_crc(uint8_t *response);
|
||||
static void swi_set_signal_pin(uint8_t is_high);
|
||||
static uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer);
|
||||
static uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer);
|
||||
static uint8_t swi_send_byte(uint8_t value);
|
||||
static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response);
|
||||
static uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address);
|
||||
static uint8_t sha204c_resync(uint8_t size, uint8_t *response);
|
||||
static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout);
|
||||
|
||||
/* SWI bit bang functions */
|
||||
|
||||
static void swi_set_signal_pin(uint8_t is_high)
|
||||
{
|
||||
SHA204_SET_OUTPUT();
|
||||
|
||||
if (is_high)
|
||||
SHA204_POUT_HIGH();
|
||||
else
|
||||
SHA204_POUT_LOW();
|
||||
}
|
||||
|
||||
static uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
{
|
||||
uint8_t i, bit_mask;
|
||||
|
||||
// Disable interrupts while sending.
|
||||
noInterrupts(); //swi_disable_interrupts();
|
||||
|
||||
// Set signal pin as output.
|
||||
SHA204_POUT_HIGH();
|
||||
SHA204_SET_OUTPUT();
|
||||
|
||||
|
||||
// Wait turn around time.
|
||||
delayMicroseconds(RX_TX_DELAY); //RX_TX_DELAY;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1)
|
||||
{
|
||||
if (bit_mask & buffer[i])
|
||||
{
|
||||
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
|
||||
delayMicroseconds(7*BIT_DELAY); //BIT_DELAY_7;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send a zero bit.
|
||||
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
|
||||
delayMicroseconds(5*BIT_DELAY); //BIT_DELAY_5;
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupts(); //swi_enable_interrupts();
|
||||
return SWI_FUNCTION_RETCODE_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t swi_send_byte(uint8_t value)
|
||||
{
|
||||
return swi_send_bytes(1, &value);
|
||||
}
|
||||
|
||||
static uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
{
|
||||
uint8_t status = SWI_FUNCTION_RETCODE_SUCCESS;
|
||||
uint8_t i;
|
||||
uint8_t bit_mask;
|
||||
uint8_t pulse_count;
|
||||
uint8_t timeout_count;
|
||||
|
||||
// Disable interrupts while receiving.
|
||||
noInterrupts(); //swi_disable_interrupts();
|
||||
|
||||
// Configure signal pin as input.
|
||||
SHA204_SET_INPUT();
|
||||
|
||||
// Receive bits and store in buffer.
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1)
|
||||
{
|
||||
pulse_count = 0;
|
||||
|
||||
// Make sure that the variable below is big enough.
|
||||
// Change it to uint16_t if 255 is too small, but be aware that
|
||||
// the loop resolution decreases on an 8-bit controller in that case.
|
||||
timeout_count = START_PULSE_TIME_OUT;
|
||||
|
||||
// Detect start bit.
|
||||
while (--timeout_count > 0)
|
||||
{
|
||||
// Wait for falling edge.
|
||||
if (SHA204_PIN_READ() == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (timeout_count == 0)
|
||||
{
|
||||
status = SWI_FUNCTION_RETCODE_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// Wait for rising edge.
|
||||
if (SHA204_PIN_READ() != 0)
|
||||
{
|
||||
// For an Atmel microcontroller this might be faster than "pulse_count++".
|
||||
pulse_count = 1;
|
||||
break;
|
||||
}
|
||||
} while (--timeout_count > 0);
|
||||
|
||||
if (pulse_count == 0)
|
||||
{
|
||||
status = SWI_FUNCTION_RETCODE_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Trying to measure the time of start bit and calculating the timeout
|
||||
// for zero bit detection is not accurate enough for an 8 MHz 8-bit CPU.
|
||||
// So let's just wait the maximum time for the falling edge of a zero bit
|
||||
// to arrive after we have detected the rising edge of the start bit.
|
||||
timeout_count = ZERO_PULSE_TIME_OUT;
|
||||
|
||||
// Detect possible edge indicating zero bit.
|
||||
do
|
||||
{
|
||||
if (SHA204_PIN_READ() == 0)
|
||||
{
|
||||
// For an Atmel microcontroller this might be faster than "pulse_count++".
|
||||
pulse_count = 2;
|
||||
break;
|
||||
}
|
||||
} while (--timeout_count > 0);
|
||||
|
||||
// Wait for rising edge of zero pulse before returning. Otherwise we might interpret
|
||||
// its rising edge as the next start pulse.
|
||||
if (pulse_count == 2)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (SHA204_PIN_READ() != 0)
|
||||
break;
|
||||
} while (timeout_count-- > 0);
|
||||
}
|
||||
|
||||
// Update byte at current buffer index.
|
||||
else
|
||||
buffer[i] |= bit_mask; // received "one" bit
|
||||
}
|
||||
|
||||
if (status != SWI_FUNCTION_RETCODE_SUCCESS)
|
||||
break;
|
||||
}
|
||||
interrupts(); //swi_enable_interrupts();
|
||||
|
||||
if (status == SWI_FUNCTION_RETCODE_TIMEOUT)
|
||||
{
|
||||
if (i > 0)
|
||||
// Indicate that we timed out after having received at least one byte.
|
||||
status = SWI_FUNCTION_RETCODE_RX_FAIL;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Physical functions */
|
||||
|
||||
static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
{
|
||||
uint8_t count_byte;
|
||||
uint8_t i;
|
||||
uint8_t ret_code;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
response[i] = 0;
|
||||
|
||||
(void) swi_send_byte(SHA204_SWI_FLAG_TX);
|
||||
|
||||
ret_code = swi_receive_bytes(size, response);
|
||||
if (ret_code == SWI_FUNCTION_RETCODE_SUCCESS || ret_code == SWI_FUNCTION_RETCODE_RX_FAIL)
|
||||
{
|
||||
count_byte = response[SHA204_BUFFER_POS_COUNT];
|
||||
if ((count_byte < SHA204_RSP_SIZE_MIN) || (count_byte > size))
|
||||
return SHA204_INVALID_SIZE;
|
||||
|
||||
return SHA204_SUCCESS;
|
||||
}
|
||||
|
||||
// Translate error so that the Communication layer
|
||||
// can distinguish between a real error or the
|
||||
// device being busy executing a command.
|
||||
if (ret_code == SWI_FUNCTION_RETCODE_TIMEOUT)
|
||||
return SHA204_RX_NO_RESPONSE;
|
||||
else
|
||||
return SHA204_RX_FAIL;
|
||||
}
|
||||
|
||||
/* Communication functions */
|
||||
|
||||
static uint8_t sha204c_resync(uint8_t size, uint8_t *response)
|
||||
{
|
||||
// Try to re-synchronize without sending a Wake token
|
||||
// (step 1 of the re-synchronization process).
|
||||
delay(SHA204_SYNC_TIMEOUT);
|
||||
uint8_t ret_code = sha204p_receive_response(size, response);
|
||||
if (ret_code == SHA204_SUCCESS)
|
||||
return ret_code;
|
||||
|
||||
// We lost communication. Send a Wake pulse and try
|
||||
// to receive a response (steps 2 and 3 of the
|
||||
// re-synchronization process).
|
||||
atsha204_sleep();
|
||||
ret_code = atsha204_wakeup(response);
|
||||
|
||||
// Translate a return value of success into one
|
||||
// that indicates that the device had to be woken up
|
||||
// and might have lost its TempKey.
|
||||
return (ret_code == SHA204_SUCCESS ? SHA204_RESYNC_WITH_WAKEUP : ret_code);
|
||||
}
|
||||
|
||||
static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout)
|
||||
{
|
||||
uint8_t ret_code = SHA204_FUNC_FAIL;
|
||||
uint8_t ret_code_resync;
|
||||
uint8_t n_retries_send;
|
||||
uint8_t n_retries_receive;
|
||||
uint8_t i;
|
||||
uint8_t status_byte;
|
||||
uint8_t count = tx_buffer[SHA204_BUFFER_POS_COUNT];
|
||||
uint8_t count_minus_crc = count - SHA204_CRC_SIZE;
|
||||
uint16_t execution_timeout_us = (uint16_t) (execution_timeout * 1000) + SHA204_RESPONSE_TIMEOUT;
|
||||
volatile uint16_t timeout_countdown;
|
||||
|
||||
// Append CRC.
|
||||
sha204c_calculate_crc(count_minus_crc, tx_buffer, tx_buffer + count_minus_crc);
|
||||
|
||||
// Retry loop for sending a command and receiving a response.
|
||||
n_retries_send = SHA204_RETRY_COUNT + 1;
|
||||
|
||||
while ((n_retries_send-- > 0) && (ret_code != SHA204_SUCCESS))
|
||||
{
|
||||
// Send command.
|
||||
ret_code = swi_send_byte(SHA204_SWI_FLAG_CMD);
|
||||
if (ret_code != SWI_FUNCTION_RETCODE_SUCCESS)
|
||||
ret_code = SHA204_COMM_FAIL;
|
||||
else
|
||||
ret_code = swi_send_bytes(count, tx_buffer);
|
||||
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
{
|
||||
if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE)
|
||||
return ret_code; // The device seems to be dead in the water.
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wait minimum command execution time and then start polling for a response.
|
||||
delay(execution_delay);
|
||||
|
||||
// Retry loop for receiving a response.
|
||||
n_retries_receive = SHA204_RETRY_COUNT + 1;
|
||||
while (n_retries_receive-- > 0)
|
||||
{
|
||||
// Reset response buffer.
|
||||
for (i = 0; i < rx_size; i++)
|
||||
rx_buffer[i] = 0;
|
||||
|
||||
// Poll for response.
|
||||
timeout_countdown = execution_timeout_us;
|
||||
do
|
||||
{
|
||||
ret_code = sha204p_receive_response(rx_size, rx_buffer);
|
||||
timeout_countdown -= SHA204_RESPONSE_TIMEOUT;
|
||||
}
|
||||
while ((timeout_countdown > SHA204_RESPONSE_TIMEOUT) && (ret_code == SHA204_RX_NO_RESPONSE));
|
||||
|
||||
if (ret_code == SHA204_RX_NO_RESPONSE)
|
||||
{
|
||||
// We did not receive a response. Re-synchronize and send command again.
|
||||
if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE)
|
||||
// The device seems to be dead in the water.
|
||||
return ret_code;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Check whether we received a valid response.
|
||||
if (ret_code == SHA204_INVALID_SIZE)
|
||||
{
|
||||
// We see 0xFF for the count when communication got out of sync.
|
||||
ret_code_resync = sha204c_resync(rx_size, rx_buffer);
|
||||
if (ret_code_resync == SHA204_SUCCESS)
|
||||
// We did not have to wake up the device. Try receiving response again.
|
||||
continue;
|
||||
if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP)
|
||||
// We could re-synchronize, but only after waking up the device.
|
||||
// Re-send command.
|
||||
break;
|
||||
else
|
||||
// We failed to re-synchronize.
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
// We received a response of valid size.
|
||||
// Check the consistency of the response.
|
||||
ret_code = sha204c_check_crc(rx_buffer);
|
||||
if (ret_code == SHA204_SUCCESS)
|
||||
{
|
||||
// Received valid response.
|
||||
if (rx_buffer[SHA204_BUFFER_POS_COUNT] > SHA204_RSP_SIZE_MIN)
|
||||
// Received non-status response. We are done.
|
||||
return ret_code;
|
||||
|
||||
// Received status response.
|
||||
status_byte = rx_buffer[SHA204_BUFFER_POS_STATUS];
|
||||
|
||||
// Translate the three possible device status error codes
|
||||
// into library return codes.
|
||||
if (status_byte == SHA204_STATUS_BYTE_PARSE)
|
||||
return SHA204_PARSE_ERROR;
|
||||
if (status_byte == SHA204_STATUS_BYTE_EXEC)
|
||||
return SHA204_CMD_FAIL;
|
||||
if (status_byte == SHA204_STATUS_BYTE_COMM)
|
||||
{
|
||||
// In case of the device status byte indicating a communication
|
||||
// error this function exits the retry loop for receiving a response
|
||||
// and enters the overall retry loop
|
||||
// (send command / receive response).
|
||||
ret_code = SHA204_STATUS_CRC;
|
||||
break;
|
||||
}
|
||||
|
||||
// Received status response from CheckMAC, DeriveKey, GenDig,
|
||||
// Lock, Nonce, Pause, UpdateExtra, or Write command.
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Received response with incorrect CRC.
|
||||
ret_code_resync = sha204c_resync(rx_size, rx_buffer);
|
||||
if (ret_code_resync == SHA204_SUCCESS)
|
||||
// We did not have to wake up the device. Try receiving response again.
|
||||
continue;
|
||||
if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP)
|
||||
// We could re-synchronize, but only after waking up the device.
|
||||
// Re-send command.
|
||||
break;
|
||||
else
|
||||
// We failed to re-synchronize.
|
||||
return ret_code;
|
||||
} // block end of check response consistency
|
||||
|
||||
} // block end of receive retry loop
|
||||
|
||||
} // block end of send and receive retry loop
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
|
||||
/* Marshaling functions */
|
||||
|
||||
static uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address)
|
||||
{
|
||||
uint8_t rx_size;
|
||||
|
||||
address >>= 2;
|
||||
|
||||
tx_buffer[SHA204_COUNT_IDX] = READ_COUNT;
|
||||
tx_buffer[SHA204_OPCODE_IDX] = SHA204_READ;
|
||||
tx_buffer[READ_ZONE_IDX] = zone;
|
||||
tx_buffer[READ_ADDR_IDX] = (uint8_t) (address & SHA204_ADDRESS_MASK);
|
||||
tx_buffer[READ_ADDR_IDX + 1] = 0;
|
||||
|
||||
rx_size = (zone & SHA204_ZONE_COUNT_FLAG) ? READ_32_RSP_SIZE : READ_4_RSP_SIZE;
|
||||
|
||||
return sha204c_send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0], READ_DELAY, READ_EXEC_MAX - READ_DELAY);
|
||||
}
|
||||
|
||||
/* CRC Calculator and Checker */
|
||||
|
||||
static void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc)
|
||||
{
|
||||
uint8_t counter;
|
||||
uint16_t crc_register = 0;
|
||||
uint16_t polynom = 0x8005;
|
||||
uint8_t shift_register;
|
||||
uint8_t data_bit, crc_bit;
|
||||
|
||||
for (counter = 0; counter < length; counter++)
|
||||
{
|
||||
for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1)
|
||||
{
|
||||
data_bit = (data[counter] & shift_register) ? 1 : 0;
|
||||
crc_bit = crc_register >> 15;
|
||||
|
||||
// Shift CRC to the left by 1.
|
||||
crc_register <<= 1;
|
||||
|
||||
if ((data_bit ^ crc_bit) != 0)
|
||||
crc_register ^= polynom;
|
||||
}
|
||||
}
|
||||
crc[0] = (uint8_t) (crc_register & 0x00FF);
|
||||
crc[1] = (uint8_t) (crc_register >> 8);
|
||||
}
|
||||
|
||||
static uint8_t sha204c_check_crc(uint8_t *response)
|
||||
{
|
||||
uint8_t crc[SHA204_CRC_SIZE];
|
||||
uint8_t count = response[SHA204_BUFFER_POS_COUNT];
|
||||
|
||||
count -= SHA204_CRC_SIZE;
|
||||
sha204c_calculate_crc(count, response, crc);
|
||||
|
||||
return (crc[0] == response[count] && crc[1] == response[count + 1])
|
||||
? SHA204_SUCCESS : SHA204_BAD_CRC;
|
||||
}
|
||||
|
||||
/* Public functions */
|
||||
|
||||
void atsha204_init(uint8_t pin)
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
device_pin = digitalPinToBitMask(pin); // Find the bit value of the pin
|
||||
uint8_t port = digitalPinToPort(pin); // temoporarily used to get the next three registers
|
||||
|
||||
// Point to data direction register port of pin
|
||||
device_port_DDR = portModeRegister(port);
|
||||
// Point to output register of pin
|
||||
device_port_OUT = portOutputRegister(port);
|
||||
// Point to input register of pin
|
||||
device_port_IN = portInputRegister(port);
|
||||
#else
|
||||
device_pin = pin;
|
||||
#endif
|
||||
}
|
||||
|
||||
void atsha204_idle(void)
|
||||
{
|
||||
swi_send_byte(SHA204_SWI_FLAG_IDLE);
|
||||
}
|
||||
|
||||
void atsha204_sleep(void)
|
||||
{
|
||||
swi_send_byte(SHA204_SWI_FLAG_SLEEP);
|
||||
}
|
||||
|
||||
uint8_t atsha204_wakeup(uint8_t *response)
|
||||
{
|
||||
swi_set_signal_pin(0);
|
||||
delayMicroseconds(10*SHA204_WAKEUP_PULSE_WIDTH);
|
||||
swi_set_signal_pin(1);
|
||||
delay(SHA204_WAKEUP_DELAY);
|
||||
|
||||
uint8_t ret_code = sha204p_receive_response(SHA204_RSP_SIZE_MIN, response);
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
return ret_code;
|
||||
|
||||
// Verify status response.
|
||||
if (response[SHA204_BUFFER_POS_COUNT] != SHA204_RSP_SIZE_MIN)
|
||||
ret_code = SHA204_INVALID_SIZE;
|
||||
else if (response[SHA204_BUFFER_POS_STATUS] != SHA204_STATUS_BYTE_WAKEUP)
|
||||
ret_code = SHA204_COMM_FAIL;
|
||||
else
|
||||
{
|
||||
if ((response[SHA204_RSP_SIZE_MIN - SHA204_CRC_SIZE] != 0x33)
|
||||
|| (response[SHA204_RSP_SIZE_MIN + 1 - SHA204_CRC_SIZE] != 0x43))
|
||||
ret_code = SHA204_BAD_CRC;
|
||||
}
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
delay(SHA204_COMMAND_EXEC_MAX);
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer)
|
||||
{
|
||||
uint8_t poll_delay, poll_timeout, response_size;
|
||||
uint8_t *p_buffer;
|
||||
uint8_t len;
|
||||
(void)tx_size;
|
||||
|
||||
// Supply delays and response size.
|
||||
switch (op_code)
|
||||
{
|
||||
case SHA204_GENDIG:
|
||||
poll_delay = GENDIG_DELAY;
|
||||
poll_timeout = GENDIG_EXEC_MAX - GENDIG_DELAY;
|
||||
response_size = GENDIG_RSP_SIZE;
|
||||
break;
|
||||
|
||||
case SHA204_HMAC:
|
||||
poll_delay = HMAC_DELAY;
|
||||
poll_timeout = HMAC_EXEC_MAX - HMAC_DELAY;
|
||||
response_size = HMAC_RSP_SIZE;
|
||||
break;
|
||||
|
||||
case SHA204_NONCE:
|
||||
poll_delay = NONCE_DELAY;
|
||||
poll_timeout = NONCE_EXEC_MAX - NONCE_DELAY;
|
||||
response_size = param1 == NONCE_MODE_PASSTHROUGH
|
||||
? NONCE_RSP_SIZE_SHORT : NONCE_RSP_SIZE_LONG;
|
||||
break;
|
||||
|
||||
case SHA204_RANDOM:
|
||||
poll_delay = RANDOM_DELAY;
|
||||
poll_timeout = RANDOM_EXEC_MAX - RANDOM_DELAY;
|
||||
response_size = RANDOM_RSP_SIZE;
|
||||
break;
|
||||
|
||||
case SHA204_SHA:
|
||||
poll_delay = SHA_DELAY;
|
||||
poll_timeout = SHA_EXEC_MAX - SHA_DELAY;
|
||||
response_size = param1 == SHA_INIT
|
||||
? SHA_RSP_SIZE_SHORT : SHA_RSP_SIZE_LONG;
|
||||
break;
|
||||
|
||||
case SHA204_WRITE:
|
||||
poll_delay = WRITE_DELAY;
|
||||
poll_timeout = WRITE_EXEC_MAX - WRITE_DELAY;
|
||||
response_size = WRITE_RSP_SIZE;
|
||||
break;
|
||||
|
||||
default:
|
||||
poll_delay = 0;
|
||||
poll_timeout = SHA204_COMMAND_EXEC_MAX;
|
||||
response_size = rx_size;
|
||||
}
|
||||
|
||||
// Assemble command.
|
||||
len = datalen1 + SHA204_CMD_SIZE_MIN;
|
||||
p_buffer = tx_buffer;
|
||||
*p_buffer++ = len;
|
||||
*p_buffer++ = op_code;
|
||||
*p_buffer++ = param1;
|
||||
*p_buffer++ = param2 & 0xFF;
|
||||
*p_buffer++ = param2 >> 8;
|
||||
|
||||
if (datalen1 > 0) {
|
||||
memcpy(p_buffer, data1, datalen1);
|
||||
p_buffer += datalen1;
|
||||
}
|
||||
|
||||
sha204c_calculate_crc(len - SHA204_CRC_SIZE, tx_buffer, p_buffer);
|
||||
|
||||
// Send command and receive response.
|
||||
return sha204c_send_and_receive(&tx_buffer[0], response_size,
|
||||
&rx_buffer[0], poll_delay, poll_timeout);
|
||||
}
|
||||
|
||||
uint8_t atsha204_getSerialNumber(uint8_t * response)
|
||||
{
|
||||
uint8_t readCommand[READ_COUNT];
|
||||
uint8_t readResponse[READ_4_RSP_SIZE];
|
||||
|
||||
/* read from bytes 0->3 of config zone */
|
||||
uint8_t returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03);
|
||||
if (!returnCode)
|
||||
{
|
||||
for (int i=0; i<4; i++) // store bytes 0-3 into respones array
|
||||
response[i] = readResponse[SHA204_BUFFER_POS_DATA+i];
|
||||
|
||||
/* read from bytes 8->11 of config zone */
|
||||
returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN47);
|
||||
|
||||
for (int i=4; i<8; i++) // store bytes 4-7 of SN into response array
|
||||
response[i] = readResponse[SHA204_BUFFER_POS_DATA+(i-4)];
|
||||
|
||||
if (!returnCode)
|
||||
{ /* Finally if last two reads were successful, read byte 8 of the SN */
|
||||
returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN8);
|
||||
response[8] = readResponse[SHA204_BUFFER_POS_DATA]; // Byte 8 of SN should always be 0xEE
|
||||
}
|
||||
}
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
#ifndef ATSHA204_H
|
||||
#define ATSHA204_H
|
||||
#if !DOXYGEN
|
||||
#include "Arduino.h"
|
||||
|
||||
/* This is a scaled down variant of the ATSHA204 library, tweaked to meet the specific needs of the MySensors library. */
|
||||
|
||||
/* Library return codes */
|
||||
#define SHA204_SUCCESS ((uint8_t) 0x00) //!< Function succeeded.
|
||||
#define SHA204_PARSE_ERROR ((uint8_t) 0xD2) //!< response status byte indicates parsing error
|
||||
#define SHA204_CMD_FAIL ((uint8_t) 0xD3) //!< response status byte indicates command execution error
|
||||
#define SHA204_STATUS_CRC ((uint8_t) 0xD4) //!< response status byte indicates CRC error
|
||||
#define SHA204_STATUS_UNKNOWN ((uint8_t) 0xD5) //!< response status byte is unknown
|
||||
#define SHA204_FUNC_FAIL ((uint8_t) 0xE0) //!< Function could not execute due to incorrect condition / state.
|
||||
#define SHA204_GEN_FAIL ((uint8_t) 0xE1) //!< unspecified error
|
||||
#define SHA204_BAD_PARAM ((uint8_t) 0xE2) //!< bad argument (out of range, null pointer, etc.)
|
||||
#define SHA204_INVALID_ID ((uint8_t) 0xE3) //!< invalid device id, id not set
|
||||
#define SHA204_INVALID_SIZE ((uint8_t) 0xE4) //!< Count value is out of range or greater than buffer size.
|
||||
#define SHA204_BAD_CRC ((uint8_t) 0xE5) //!< incorrect CRC received
|
||||
#define SHA204_RX_FAIL ((uint8_t) 0xE6) //!< Timed out while waiting for response. Number of bytes received is > 0.
|
||||
#define SHA204_RX_NO_RESPONSE ((uint8_t) 0xE7) //!< Not an error while the Command layer is polling for a command response.
|
||||
#define SHA204_RESYNC_WITH_WAKEUP ((uint8_t) 0xE8) //!< re-synchronization succeeded, but only after generating a Wake-up
|
||||
|
||||
#define SHA204_COMM_FAIL ((uint8_t) 0xF0) //!< Communication with device failed. Same as in hardware dependent modules.
|
||||
#define SHA204_TIMEOUT ((uint8_t) 0xF1) //!< Timed out while waiting for response. Number of bytes received is 0.
|
||||
|
||||
/* bitbang_config.h */
|
||||
|
||||
#define PORT_ACCESS_TIME (630) //! time it takes to toggle the pin at CPU clock of 16 MHz (ns)
|
||||
#define START_PULSE_WIDTH (4340) //! width of start pulse (ns)
|
||||
#define BIT_DELAY (4) //! delay macro for width of one pulse (start pulse or zero pulse, in ns)
|
||||
#define RX_TX_DELAY (15) //! turn around time when switching from receive to transmit
|
||||
#define START_PULSE_TIME_OUT (255) //! This value is decremented while waiting for the falling edge of a start pulse.
|
||||
#define ZERO_PULSE_TIME_OUT (26) //! This value is decremented while waiting for the falling edge of a zero pulse.
|
||||
|
||||
/* swi_phys.h */
|
||||
|
||||
#define SWI_FUNCTION_RETCODE_SUCCESS ((uint8_t) 0x00) //!< Communication with device succeeded.
|
||||
#define SWI_FUNCTION_RETCODE_TIMEOUT ((uint8_t) 0xF1) //!< Communication timed out.
|
||||
#define SWI_FUNCTION_RETCODE_RX_FAIL ((uint8_t) 0xF9) //!< Communication failed after at least one byte was received.
|
||||
|
||||
/* sha204_physical.h */
|
||||
|
||||
#define SHA204_RSP_SIZE_MIN ((uint8_t) 4) //!< minimum number of bytes in response
|
||||
#define SHA204_RSP_SIZE_MAX ((uint8_t) 35) //!< maximum size of response packet
|
||||
#define SHA204_BUFFER_POS_COUNT (0) //!< buffer index of count byte in command or response
|
||||
#define SHA204_BUFFER_POS_DATA (1) //!< buffer index of data in response
|
||||
#define SHA204_WAKEUP_PULSE_WIDTH (uint8_t) (6.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5) //! width of Wakeup pulse in 10 us units
|
||||
#define SHA204_WAKEUP_DELAY (uint8_t) (3.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5) //! delay between Wakeup pulse and communication in ms
|
||||
|
||||
/* sha204_swi.c */
|
||||
#define SHA204_SWI_FLAG_CMD ((uint8_t) 0x77) //!< flag preceding a command
|
||||
#define SHA204_SWI_FLAG_TX ((uint8_t) 0x88) //!< flag requesting a response
|
||||
#define SHA204_SWI_FLAG_IDLE ((uint8_t) 0xBB) //!< flag requesting to go into Idle mode
|
||||
#define SHA204_SWI_FLAG_SLEEP ((uint8_t) 0xCC) //!< flag requesting to go into Sleep mode
|
||||
|
||||
/* sha204_comm_marshaling.h */
|
||||
// command op-code definitions
|
||||
#define SHA204_GENDIG ((uint8_t) 0x15) //!< GenDig command op-code
|
||||
#define SHA204_HMAC ((uint8_t) 0x11) //!< HMAC command op-code
|
||||
#define SHA204_NONCE ((uint8_t) 0x16) //!< Nonce command op-code
|
||||
#define SHA204_RANDOM ((uint8_t) 0x1B) //!< Random command op-code
|
||||
#define SHA204_READ ((uint8_t) 0x02) //!< Read command op-code
|
||||
#define SHA204_SHA ((uint8_t) 0x47) //!< SHA command op-code
|
||||
#define SHA204_WRITE ((uint8_t) 0x12) //!< Write command op-code
|
||||
|
||||
// packet size definitions
|
||||
#define SHA204_RSP_SIZE_VAL ((uint8_t) 7) //!< size of response packet containing four bytes of data
|
||||
|
||||
// definitions for command packet indexes common to all commands
|
||||
#define SHA204_COUNT_IDX ( 0) //!< command packet index for count
|
||||
#define SHA204_OPCODE_IDX ( 1) //!< command packet index for op-code
|
||||
#define SHA204_PARAM1_IDX ( 2) //!< command packet index for first parameter
|
||||
#define SHA204_PARAM2_IDX ( 3) //!< command packet index for second parameter
|
||||
#define SHA204_DATA_IDX ( 5) //!< command packet index for second parameter
|
||||
|
||||
// zone definitions
|
||||
#define SHA204_ZONE_CONFIG ((uint8_t) 0x00) //!< Configuration zone
|
||||
#define SHA204_ZONE_OTP ((uint8_t) 0x01) //!< OTP (One Time Programming) zone
|
||||
#define SHA204_ZONE_DATA ((uint8_t) 0x02) //!< Data zone
|
||||
#define SHA204_ZONE_MASK ((uint8_t) 0x03) //!< Zone mask
|
||||
#define SHA204_ZONE_COUNT_FLAG ((uint8_t) 0x80) //!< Zone bit 7 set: Access 32 bytes, otherwise 4 bytes.
|
||||
#define SHA204_ZONE_ACCESS_4 ((uint8_t) 4) //!< Read or write 4 bytes.
|
||||
#define SHA204_ZONE_ACCESS_32 ((uint8_t) 32) //!< Read or write 32 bytes.
|
||||
#define SHA204_ADDRESS_MASK_CONFIG ( 0x001F) //!< Address bits 5 to 7 are 0 for Configuration zone.
|
||||
#define SHA204_ADDRESS_MASK_OTP ( 0x000F) //!< Address bits 4 to 7 are 0 for OTP zone.
|
||||
#define SHA204_ADDRESS_MASK ( 0x007F) //!< Address bit 7 to 15 are always 0.
|
||||
|
||||
// GenDig command definitions
|
||||
#define GENDIG_ZONE_IDX SHA204_PARAM1_IDX //!< GenDig command index for zone
|
||||
#define GENDIG_KEYID_IDX SHA204_PARAM2_IDX //!< GenDig command index for key id
|
||||
#define GENDIG_DATA_IDX SHA204_DATA_IDX //!< GenDig command index for optional data
|
||||
#define GENDIG_COUNT SHA204_CMD_SIZE_MIN //!< GenDig command packet size without "other data"
|
||||
#define GENDIG_COUNT_DATA (11) //!< GenDig command packet size with "other data"
|
||||
#define GENDIG_OTHER_DATA_SIZE (4) //!< GenDig size of "other data"
|
||||
#define GENDIG_ZONE_CONFIG ((uint8_t) 0) //!< GenDig zone id config
|
||||
#define GENDIG_ZONE_OTP ((uint8_t) 1) //!< GenDig zone id OTP
|
||||
#define GENDIG_ZONE_DATA ((uint8_t) 2) //!< GenDig zone id data
|
||||
|
||||
// HMAC command definitions
|
||||
#define HMAC_MODE_IDX SHA204_PARAM1_IDX //!< HMAC command index for mode
|
||||
#define HMAC_KEYID_IDX SHA204_PARAM2_IDX //!< HMAC command index for key id
|
||||
#define HMAC_COUNT SHA204_CMD_SIZE_MIN //!< HMAC command packet size
|
||||
#define HMAC_MODE_MASK ((uint8_t) 0x74) //!< HMAC mode bits 0, 1, 3, and 7 are 0.
|
||||
#define HMAC_MODE_SOURCE_FLAG_MATCH ((uint8_t) 0x04) //!< HMAC mode bit 2: match TempKey.SourceFlag
|
||||
|
||||
// Nonce command definitions
|
||||
#define NONCE_MODE_IDX SHA204_PARAM1_IDX //!< Nonce command index for mode
|
||||
#define NONCE_PARAM2_IDX SHA204_PARAM2_IDX //!< Nonce command index for 2. parameter
|
||||
#define NONCE_INPUT_IDX SHA204_DATA_IDX //!< Nonce command index for input data
|
||||
#define NONCE_COUNT_SHORT (27) //!< Nonce command packet size for 20 bytes of data
|
||||
#define NONCE_COUNT_LONG (39) //!< Nonce command packet size for 32 bytes of data
|
||||
#define NONCE_MODE_MASK ((uint8_t) 3) //!< Nonce mode bits 2 to 7 are 0.
|
||||
#define NONCE_MODE_SEED_UPDATE ((uint8_t) 0x00) //!< Nonce mode: update seed
|
||||
#define NONCE_MODE_NO_SEED_UPDATE ((uint8_t) 0x01) //!< Nonce mode: do not update seed
|
||||
#define NONCE_MODE_INVALID ((uint8_t) 0x02) //!< Nonce mode 2 is invalid.
|
||||
#define NONCE_MODE_PASSTHROUGH ((uint8_t) 0x03) //!< Nonce mode: pass-through
|
||||
#define NONCE_NUMIN_SIZE (20) //!< Nonce data length
|
||||
#define NONCE_NUMIN_SIZE_PASSTHROUGH (32) //!< Nonce data length in pass-through mode (mode = 3)
|
||||
|
||||
// Random command definitions
|
||||
#define RANDOM_MODE_IDX SHA204_PARAM1_IDX //!< Random command index for mode
|
||||
#define RANDOM_PARAM2_IDX SHA204_PARAM2_IDX //!< Random command index for 2. parameter
|
||||
#define RANDOM_COUNT SHA204_CMD_SIZE_MIN //!< Random command packet size
|
||||
#define RANDOM_SEED_UPDATE ((uint8_t) 0x00) //!< Random mode for automatic seed update
|
||||
#define RANDOM_NO_SEED_UPDATE ((uint8_t) 0x01) //!< Random mode for no seed update
|
||||
|
||||
// Read command definitions
|
||||
#define READ_ZONE_IDX SHA204_PARAM1_IDX //!< Read command index for zone
|
||||
#define READ_ADDR_IDX SHA204_PARAM2_IDX //!< Read command index for address
|
||||
#define READ_COUNT SHA204_CMD_SIZE_MIN //!< Read command packet size
|
||||
#define READ_ZONE_MASK ((uint8_t) 0x83) //!< Read zone bits 2 to 6 are 0.
|
||||
#define READ_ZONE_MODE_32_BYTES ((uint8_t) 0x80) //!< Read mode: 32 bytes
|
||||
|
||||
// SHA command definitions
|
||||
#define SHA_MODE_IDX SHA204_PARAM1_IDX //!< SHA command index for mode
|
||||
#define SHA_PARAM2_IDX SHA204_PARAM2_IDX //!< SHA command index for 2. parameter
|
||||
#define SHA_COUNT_SHORT SHA204_CMD_SIZE_MIN //!< SHA command packet size for init
|
||||
#define SHA_COUNT_LONG (71) //!< SHA command packet size for calculation
|
||||
#define SHA_MSG_SIZE (64) //!< SHA message data size
|
||||
#define SHA_INIT ((uint8_t) 0x00) //!< SHA mode for init
|
||||
#define SHA_CALC ((uint8_t) 0x01) //!< SHA mode for calculation
|
||||
|
||||
// Write command definitions
|
||||
#define WRITE_ZONE_IDX SHA204_PARAM1_IDX //!< Write command index for zone
|
||||
#define WRITE_ADDR_IDX SHA204_PARAM2_IDX //!< Write command index for address
|
||||
#define WRITE_VALUE_IDX SHA204_DATA_IDX //!< Write command index for data
|
||||
#define WRITE_MAC_VS_IDX ( 9) //!< Write command index for MAC following short data
|
||||
#define WRITE_MAC_VL_IDX (37) //!< Write command index for MAC following long data
|
||||
#define WRITE_COUNT_SHORT (11) //!< Write command packet size with short data and no MAC
|
||||
#define WRITE_COUNT_LONG (39) //!< Write command packet size with long data and no MAC
|
||||
#define WRITE_COUNT_SHORT_MAC (43) //!< Write command packet size with short data and MAC
|
||||
#define WRITE_COUNT_LONG_MAC (71) //!< Write command packet size with long data and MAC
|
||||
#define WRITE_MAC_SIZE (32) //!< Write MAC size
|
||||
#define WRITE_ZONE_MASK ((uint8_t) 0xC3) //!< Write zone bits 2 to 5 are 0.
|
||||
#define WRITE_ZONE_WITH_MAC ((uint8_t) 0x40) //!< Write zone bit 6: write encrypted with MAC
|
||||
|
||||
// Response size definitions
|
||||
#define GENDIG_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of GenDig command
|
||||
#define HMAC_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of HMAC command
|
||||
#define NONCE_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of Nonce command with mode[0:1] = 3
|
||||
#define NONCE_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of Nonce command
|
||||
#define RANDOM_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Random command
|
||||
#define READ_4_RSP_SIZE SHA204_RSP_SIZE_VAL //!< response size of Read command when reading 4 bytes
|
||||
#define READ_32_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Read command when reading 32 bytes
|
||||
#define SHA_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of SHA command with mode[0:1] = 0
|
||||
#define SHA_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of SHA command
|
||||
#define WRITE_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of Write command
|
||||
|
||||
// command timing definitions for minimum execution times (ms)
|
||||
#define GENDIG_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define HMAC_DELAY ((uint8_t) (27.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define NONCE_DELAY ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define RANDOM_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define READ_DELAY ((uint8_t) ( 0.4 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define SHA_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define WRITE_DELAY ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
|
||||
// command timing definitions for maximum execution times (ms)
|
||||
#define GENDIG_EXEC_MAX ((uint8_t) (43.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define HMAC_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define NONCE_EXEC_MAX ((uint8_t) (60.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define RANDOM_EXEC_MAX ((uint8_t) (50.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define READ_EXEC_MAX ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define SHA_EXEC_MAX ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define WRITE_EXEC_MAX ((uint8_t) (42.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
|
||||
/* from sha204_config.h */
|
||||
|
||||
#define CPU_CLOCK_DEVIATION_POSITIVE (1.01)
|
||||
#define CPU_CLOCK_DEVIATION_NEGATIVE (0.99)
|
||||
#define SHA204_RETRY_COUNT (1)
|
||||
#define SWI_RECEIVE_TIME_OUT ((uint16_t) 163) //! #START_PULSE_TIME_OUT in us instead of loop counts
|
||||
#define SWI_US_PER_BYTE ((uint16_t) 313) //! It takes 312.5 us to send a byte (9 single-wire bits / 230400 Baud * 8 flag bits).
|
||||
#define SHA204_SYNC_TIMEOUT ((uint8_t) 85)//! delay before sending a transmit flag in the synchronization routine
|
||||
#define SHA204_RESPONSE_TIMEOUT ((uint16_t) SWI_RECEIVE_TIME_OUT + SWI_US_PER_BYTE) //! SWI response timeout is the sum of receive timeout and the time it takes to send the TX flag.
|
||||
|
||||
/* from sha204_comm.h */
|
||||
|
||||
#define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay
|
||||
#define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte)
|
||||
#ifndef SHA204_CMD_SIZE_MAX
|
||||
#define SHA204_CMD_SIZE_MAX ((uint8_t) SHA_COUNT_LONG) //! maximum size of command packet (SHA)
|
||||
#endif
|
||||
#define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes
|
||||
#define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response
|
||||
#define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response
|
||||
#define SHA204_STATUS_BYTE_WAKEUP ((uint8_t) 0x11) //! command parse error
|
||||
#define SHA204_STATUS_BYTE_PARSE ((uint8_t) 0x03) //! command parse error
|
||||
#define SHA204_STATUS_BYTE_EXEC ((uint8_t) 0x0F) //! command execution error
|
||||
#define SHA204_STATUS_BYTE_COMM ((uint8_t) 0xFF) //! communication error
|
||||
|
||||
/* EEPROM Addresses */
|
||||
/* Configuration Zone */
|
||||
#define ADDRESS_SN03 0 // SN[0:3] are bytes 0->3 of configuration zone
|
||||
#define ADDRESS_RevNum 4 // bytes 4->7 of config zone are RevNum
|
||||
#define ADDRESS_SN47 8 // SN[4:7] are bytes 8->11 of config zone
|
||||
#define ADDRESS_SN8 12 // SN[8] is byte 12 of config zone, should be 0xEE
|
||||
#define ADDRESS_I2CEN 14 // I2C Enable, bit 0 represents I2C enable status
|
||||
#define ADDRESS_I2CADD 16 // Defines I2C address of SHA204
|
||||
#define ADDRESS_OTPMODE 18 // Sets the One-time-programmable mode
|
||||
#define ADDRESS_SELECTOR 19 // Controls writability of Selector
|
||||
|
||||
#define SHA204_SERIAL_SZ 9 // The number of bytes the serial number consists of
|
||||
|
||||
/* Low level HW access macros */
|
||||
/* function calls is not working, as it will have too much overhead */
|
||||
#if !defined(ARDUINO_ARCH_AVR) // For everything else than AVR use pinMode / digitalWrite
|
||||
#define SHA204_SET_OUTPUT() pinMode(device_pin, OUTPUT)
|
||||
#define SHA204_SET_INPUT() pinMode(device_pin, INPUT)
|
||||
#define SHA204_POUT_HIGH() digitalWrite(device_pin, HIGH)
|
||||
#define SHA204_POUT_LOW() digitalWrite(device_pin, LOW)
|
||||
#define SHA204_PIN_READ() digitalRead(device_pin)
|
||||
#else
|
||||
#define SHA204_SET_INPUT() *device_port_DDR &= ~device_pin
|
||||
#define SHA204_SET_OUTPUT() *device_port_DDR |= device_pin
|
||||
#define SHA204_POUT_HIGH() *device_port_OUT |= device_pin
|
||||
#define SHA204_POUT_LOW() *device_port_OUT &= ~device_pin
|
||||
#define SHA204_PIN_READ() (*device_port_IN & device_pin)
|
||||
#endif
|
||||
|
||||
void atsha204_init(uint8_t pin);
|
||||
void atsha204_idle(void);
|
||||
void atsha204_sleep(void);
|
||||
uint8_t atsha204_wakeup(uint8_t *response);
|
||||
uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size,
|
||||
uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);
|
||||
uint8_t atsha204_getSerialNumber(uint8_t *response);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
#include <string.h>
|
||||
#if defined(__AVR__)
|
||||
#include <avr/pgmspace.h>
|
||||
#define PRIPSTR "%S"
|
||||
#elif defined(ESP8266)
|
||||
#include <pgmspace.h>
|
||||
#endif
|
||||
#include "sha256.h"
|
||||
|
||||
|
||||
const uint32_t sha256K[] PROGMEM = {
|
||||
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
|
||||
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
|
||||
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
|
||||
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
|
||||
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
|
||||
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
|
||||
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
|
||||
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
|
||||
};
|
||||
|
||||
#define BUFFER_SIZE 64
|
||||
|
||||
const uint8_t sha256InitState[] PROGMEM = {
|
||||
0x67,0xe6,0x09,0x6a, // H0
|
||||
0x85,0xae,0x67,0xbb, // H1
|
||||
0x72,0xf3,0x6e,0x3c, // H2
|
||||
0x3a,0xf5,0x4f,0xa5, // H3
|
||||
0x7f,0x52,0x0e,0x51, // H4
|
||||
0x8c,0x68,0x05,0x9b, // H5
|
||||
0xab,0xd9,0x83,0x1f, // H6
|
||||
0x19,0xcd,0xe0,0x5b // H7
|
||||
};
|
||||
|
||||
void Sha256Class::init(void) {
|
||||
memcpy_P(state.b,sha256InitState,32);
|
||||
byteCount = 0;
|
||||
bufferOffset = 0;
|
||||
}
|
||||
|
||||
uint32_t Sha256Class::ror32(uint32_t number, uint8_t bits) {
|
||||
return ((number << (32-bits)) | (number >> bits));
|
||||
}
|
||||
|
||||
void Sha256Class::hashBlock() {
|
||||
uint8_t i;
|
||||
uint32_t a,b,c,d,e,f,g,h,t1,t2;
|
||||
|
||||
a=state.w[0];
|
||||
b=state.w[1];
|
||||
c=state.w[2];
|
||||
d=state.w[3];
|
||||
e=state.w[4];
|
||||
f=state.w[5];
|
||||
g=state.w[6];
|
||||
h=state.w[7];
|
||||
|
||||
for (i=0; i<64; i++) {
|
||||
if (i>=16) {
|
||||
t1 = buffer.w[i&15] + buffer.w[(i-7)&15];
|
||||
t2 = buffer.w[(i-2)&15];
|
||||
t1 += ror32(t2,17) ^ ror32(t2,19) ^ (t2>>10);
|
||||
t2 = buffer.w[(i-15)&15];
|
||||
t1 += ror32(t2,7) ^ ror32(t2,18) ^ (t2>>3);
|
||||
buffer.w[i&15] = t1;
|
||||
}
|
||||
t1 = h;
|
||||
t1 += ror32(e,6) ^ ror32(e,11) ^ ror32(e,25); // ∑1(e)
|
||||
t1 += g ^ (e & (g ^ f)); // Ch(e,f,g)
|
||||
t1 += pgm_read_dword(sha256K+i); // Ki
|
||||
t1 += buffer.w[i&15]; // Wi
|
||||
t2 = ror32(a,2) ^ ror32(a,13) ^ ror32(a,22); // ∑0(a)
|
||||
t2 += ((b & c) | (a & (b | c))); // Maj(a,b,c)
|
||||
h=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2;
|
||||
}
|
||||
state.w[0] += a;
|
||||
state.w[1] += b;
|
||||
state.w[2] += c;
|
||||
state.w[3] += d;
|
||||
state.w[4] += e;
|
||||
state.w[5] += f;
|
||||
state.w[6] += g;
|
||||
state.w[7] += h;
|
||||
}
|
||||
|
||||
void Sha256Class::addUncounted(uint8_t data) {
|
||||
buffer.b[bufferOffset ^ 3] = data;
|
||||
bufferOffset++;
|
||||
if (bufferOffset == BUFFER_SIZE) {
|
||||
hashBlock();
|
||||
bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Sha256Class::write(uint8_t data) {
|
||||
++byteCount;
|
||||
addUncounted(data);
|
||||
}
|
||||
|
||||
void Sha256Class::pad() {
|
||||
// Implement SHA-256 padding (fips180-2 §5.1.1)
|
||||
|
||||
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||
addUncounted(0x80);
|
||||
while (bufferOffset != 56) addUncounted(0x00);
|
||||
|
||||
// Append length in the last 8 bytes
|
||||
addUncounted(0); // We're only using 32 bit lengths
|
||||
addUncounted(0); // But SHA-1 supports 64 bit lengths
|
||||
addUncounted(0); // So zero pad the top bits
|
||||
addUncounted(byteCount >> 29); // Shifting to multiply by 8
|
||||
addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
|
||||
addUncounted(byteCount >> 13); // byte.
|
||||
addUncounted(byteCount >> 5);
|
||||
addUncounted(byteCount << 3);
|
||||
}
|
||||
|
||||
|
||||
uint8_t* Sha256Class::result(void) {
|
||||
// Pad to complete the last block
|
||||
pad();
|
||||
|
||||
// Swap byte order back
|
||||
for (int i=0; i<8; i++) {
|
||||
uint32_t a,b;
|
||||
a=state.w[i];
|
||||
b=a<<24;
|
||||
b|=(a<<8) & 0x00ff0000;
|
||||
b|=(a>>8) & 0x0000ff00;
|
||||
b|=a>>24;
|
||||
state.w[i]=b;
|
||||
}
|
||||
|
||||
// Return pointer to hash (20 characters)
|
||||
return state.b;
|
||||
}
|
||||
|
||||
#define HMAC_IPAD 0x36
|
||||
#define HMAC_OPAD 0x5c
|
||||
|
||||
uint8_t keyBuffer[BLOCK_LENGTH]; // K0 in FIPS-198a
|
||||
uint8_t innerHash[HASH_LENGTH];
|
||||
|
||||
void Sha256Class::initHmac(const uint8_t* key, int keyLength) {
|
||||
uint8_t i;
|
||||
memset(keyBuffer,0,BLOCK_LENGTH);
|
||||
if (keyLength > BLOCK_LENGTH) {
|
||||
// Hash long keys
|
||||
init();
|
||||
for (;keyLength--;) write(*key++);
|
||||
memcpy(keyBuffer,result(),HASH_LENGTH);
|
||||
} else {
|
||||
// Block length keys are used as is
|
||||
memcpy(keyBuffer,key,keyLength);
|
||||
}
|
||||
// Start inner hash
|
||||
init();
|
||||
for (i=0; i<BLOCK_LENGTH; i++) {
|
||||
write(keyBuffer[i] ^ HMAC_IPAD);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* Sha256Class::resultHmac(void) {
|
||||
uint8_t i;
|
||||
// Complete inner hash
|
||||
memcpy(innerHash,result(),HASH_LENGTH);
|
||||
// Calculate outer hash
|
||||
init();
|
||||
for (i=0; i<BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD);
|
||||
for (i=0; i<HASH_LENGTH; i++) write(innerHash[i]);
|
||||
return result();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// This code is taken from https://github.com/Cathedrow/Cryptosuite (Peter Knight) and is lightly modified for MySensors use
|
||||
#ifndef Sha256_h
|
||||
#define Sha256_h
|
||||
#if !DOXYGEN
|
||||
#include <inttypes.h>
|
||||
|
||||
#define HASH_LENGTH 32
|
||||
#define BLOCK_LENGTH 64
|
||||
|
||||
union _buffer {
|
||||
uint8_t b[BLOCK_LENGTH];
|
||||
uint32_t w[BLOCK_LENGTH/4];
|
||||
};
|
||||
union _state {
|
||||
uint8_t b[HASH_LENGTH];
|
||||
uint32_t w[HASH_LENGTH/4];
|
||||
};
|
||||
|
||||
class Sha256Class
|
||||
{
|
||||
public:
|
||||
void init(void);
|
||||
void initHmac(const uint8_t* secret, int secretLength);
|
||||
uint8_t* result(void);
|
||||
uint8_t* resultHmac(void);
|
||||
void write(uint8_t);
|
||||
private:
|
||||
void pad();
|
||||
void addUncounted(uint8_t data);
|
||||
void hashBlock();
|
||||
uint32_t ror32(uint32_t number, uint8_t bits);
|
||||
_buffer buffer;
|
||||
uint8_t bufferOffset;
|
||||
_state state;
|
||||
uint32_t byteCount;
|
||||
uint8_t keyBuffer[BLOCK_LENGTH];
|
||||
uint8_t innerHash[HASH_LENGTH];
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Arduino DigitalIO Library
|
||||
* Copyright (C) 2013 by William Greiman
|
||||
*
|
||||
* This file is part of the Arduino DigitalIO Library
|
||||
*
|
||||
* This Library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino DigitalIO Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief Fast Digital I/O functions
|
||||
*
|
||||
*/
|
||||
#ifndef DigitalIO_h
|
||||
#define DigitalIO_h 1
|
||||
//------------------------------------------------------------------------------
|
||||
/** DigitalPin version YYYYMMDD */
|
||||
#define DIGITAL_IO_VERSION 20140217
|
||||
//------------------------------------------------------------------------------
|
||||
#include "DigitalPin.h"
|
||||
#include "I2cConstants.h"
|
||||
#include "PinIO.h"
|
||||
#include "SoftI2cMaster.h"
|
||||
#include "SoftSPI.h"
|
||||
#endif // DigitalIO_h
|
||||
@@ -0,0 +1,586 @@
|
||||
/* Arduino DigitalIO Library
|
||||
* Copyright (C) 2013 by William Greiman
|
||||
*
|
||||
* This file is part of the Arduino DigitalIO Library
|
||||
*
|
||||
* This Library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino DigitalIO Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief Fast Digital Pin functions
|
||||
*
|
||||
* @defgroup digitalPin Fast Pin I/O
|
||||
* @ingroup internals
|
||||
* @details Fast Digital I/O functions and template class.
|
||||
* @{
|
||||
*/
|
||||
#ifndef DigitalPin_h
|
||||
#define DigitalPin_h
|
||||
#include <avr/io.h>
|
||||
#include <util/atomic.h>
|
||||
#include <Arduino.h>
|
||||
/** @brief Helper macro for complex inline attributes */
|
||||
#define ALWAYS_INLINE inline __attribute__((always_inline))
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* @class pin_map_t
|
||||
* @brief struct for mapping digital pins
|
||||
*/
|
||||
struct pin_map_t {
|
||||
volatile uint8_t* ddr; /**< address of DDR for this pin */
|
||||
volatile uint8_t* pin; /**< address of PIN for this pin */
|
||||
volatile uint8_t* port; /**< address of PORT for this pin */
|
||||
uint8_t bit; /**< bit number for this pin */
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
#if defined(__AVR_ATmega168__)\
|
||||
||defined(__AVR_ATmega168P__)\
|
||||
||defined(__AVR_ATmega328P__)
|
||||
// 168 and 328 Arduinos
|
||||
const static pin_map_t pinMap[] = {
|
||||
{&DDRD, &PIND, &PORTD, 0}, // D0 0
|
||||
{&DDRD, &PIND, &PORTD, 1}, // D1 1
|
||||
{&DDRD, &PIND, &PORTD, 2}, // D2 2
|
||||
{&DDRD, &PIND, &PORTD, 3}, // D3 3
|
||||
{&DDRD, &PIND, &PORTD, 4}, // D4 4
|
||||
{&DDRD, &PIND, &PORTD, 5}, // D5 5
|
||||
{&DDRD, &PIND, &PORTD, 6}, // D6 6
|
||||
{&DDRD, &PIND, &PORTD, 7}, // D7 7
|
||||
{&DDRB, &PINB, &PORTB, 0}, // B0 8
|
||||
{&DDRB, &PINB, &PORTB, 1}, // B1 9
|
||||
{&DDRB, &PINB, &PORTB, 2}, // B2 10
|
||||
{&DDRB, &PINB, &PORTB, 3}, // B3 11
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 12
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 13
|
||||
{&DDRC, &PINC, &PORTC, 0}, // C0 14
|
||||
{&DDRC, &PINC, &PORTC, 1}, // C1 15
|
||||
{&DDRC, &PINC, &PORTC, 2}, // C2 16
|
||||
{&DDRC, &PINC, &PORTC, 3}, // C3 17
|
||||
{&DDRC, &PINC, &PORTC, 4}, // C4 18
|
||||
{&DDRC, &PINC, &PORTC, 5} // C5 19
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
#elif defined(__AVR_ATmega1280__)\
|
||||
|| defined(__AVR_ATmega2560__)
|
||||
// Mega
|
||||
static const pin_map_t pinMap[] = {
|
||||
{&DDRE, &PINE, &PORTE, 0}, // E0 0
|
||||
{&DDRE, &PINE, &PORTE, 1}, // E1 1
|
||||
{&DDRE, &PINE, &PORTE, 4}, // E4 2
|
||||
{&DDRE, &PINE, &PORTE, 5}, // E5 3
|
||||
{&DDRG, &PING, &PORTG, 5}, // G5 4
|
||||
{&DDRE, &PINE, &PORTE, 3}, // E3 5
|
||||
{&DDRH, &PINH, &PORTH, 3}, // H3 6
|
||||
{&DDRH, &PINH, &PORTH, 4}, // H4 7
|
||||
{&DDRH, &PINH, &PORTH, 5}, // H5 8
|
||||
{&DDRH, &PINH, &PORTH, 6}, // H6 9
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 10
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 11
|
||||
{&DDRB, &PINB, &PORTB, 6}, // B6 12
|
||||
{&DDRB, &PINB, &PORTB, 7}, // B7 13
|
||||
{&DDRJ, &PINJ, &PORTJ, 1}, // J1 14
|
||||
{&DDRJ, &PINJ, &PORTJ, 0}, // J0 15
|
||||
{&DDRH, &PINH, &PORTH, 1}, // H1 16
|
||||
{&DDRH, &PINH, &PORTH, 0}, // H0 17
|
||||
{&DDRD, &PIND, &PORTD, 3}, // D3 18
|
||||
{&DDRD, &PIND, &PORTD, 2}, // D2 19
|
||||
{&DDRD, &PIND, &PORTD, 1}, // D1 20
|
||||
{&DDRD, &PIND, &PORTD, 0}, // D0 21
|
||||
{&DDRA, &PINA, &PORTA, 0}, // A0 22
|
||||
{&DDRA, &PINA, &PORTA, 1}, // A1 23
|
||||
{&DDRA, &PINA, &PORTA, 2}, // A2 24
|
||||
{&DDRA, &PINA, &PORTA, 3}, // A3 25
|
||||
{&DDRA, &PINA, &PORTA, 4}, // A4 26
|
||||
{&DDRA, &PINA, &PORTA, 5}, // A5 27
|
||||
{&DDRA, &PINA, &PORTA, 6}, // A6 28
|
||||
{&DDRA, &PINA, &PORTA, 7}, // A7 29
|
||||
{&DDRC, &PINC, &PORTC, 7}, // C7 30
|
||||
{&DDRC, &PINC, &PORTC, 6}, // C6 31
|
||||
{&DDRC, &PINC, &PORTC, 5}, // C5 32
|
||||
{&DDRC, &PINC, &PORTC, 4}, // C4 33
|
||||
{&DDRC, &PINC, &PORTC, 3}, // C3 34
|
||||
{&DDRC, &PINC, &PORTC, 2}, // C2 35
|
||||
{&DDRC, &PINC, &PORTC, 1}, // C1 36
|
||||
{&DDRC, &PINC, &PORTC, 0}, // C0 37
|
||||
{&DDRD, &PIND, &PORTD, 7}, // D7 38
|
||||
{&DDRG, &PING, &PORTG, 2}, // G2 39
|
||||
{&DDRG, &PING, &PORTG, 1}, // G1 40
|
||||
{&DDRG, &PING, &PORTG, 0}, // G0 41
|
||||
{&DDRL, &PINL, &PORTL, 7}, // L7 42
|
||||
{&DDRL, &PINL, &PORTL, 6}, // L6 43
|
||||
{&DDRL, &PINL, &PORTL, 5}, // L5 44
|
||||
{&DDRL, &PINL, &PORTL, 4}, // L4 45
|
||||
{&DDRL, &PINL, &PORTL, 3}, // L3 46
|
||||
{&DDRL, &PINL, &PORTL, 2}, // L2 47
|
||||
{&DDRL, &PINL, &PORTL, 1}, // L1 48
|
||||
{&DDRL, &PINL, &PORTL, 0}, // L0 49
|
||||
{&DDRB, &PINB, &PORTB, 3}, // B3 50
|
||||
{&DDRB, &PINB, &PORTB, 2}, // B2 51
|
||||
{&DDRB, &PINB, &PORTB, 1}, // B1 52
|
||||
{&DDRB, &PINB, &PORTB, 0}, // B0 53
|
||||
{&DDRF, &PINF, &PORTF, 0}, // F0 54
|
||||
{&DDRF, &PINF, &PORTF, 1}, // F1 55
|
||||
{&DDRF, &PINF, &PORTF, 2}, // F2 56
|
||||
{&DDRF, &PINF, &PORTF, 3}, // F3 57
|
||||
{&DDRF, &PINF, &PORTF, 4}, // F4 58
|
||||
{&DDRF, &PINF, &PORTF, 5}, // F5 59
|
||||
{&DDRF, &PINF, &PORTF, 6}, // F6 60
|
||||
{&DDRF, &PINF, &PORTF, 7}, // F7 61
|
||||
{&DDRK, &PINK, &PORTK, 0}, // K0 62
|
||||
{&DDRK, &PINK, &PORTK, 1}, // K1 63
|
||||
{&DDRK, &PINK, &PORTK, 2}, // K2 64
|
||||
{&DDRK, &PINK, &PORTK, 3}, // K3 65
|
||||
{&DDRK, &PINK, &PORTK, 4}, // K4 66
|
||||
{&DDRK, &PINK, &PORTK, 5}, // K5 67
|
||||
{&DDRK, &PINK, &PORTK, 6}, // K6 68
|
||||
{&DDRK, &PINK, &PORTK, 7} // K7 69
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
#elif defined(__AVR_ATmega1284P__)\
|
||||
|| defined(__AVR_ATmega1284__)\
|
||||
|| defined(__AVR_ATmega644P__)\
|
||||
|| defined(__AVR_ATmega644__)\
|
||||
|| defined(__AVR_ATmega64__)\
|
||||
|| defined(__AVR_ATmega32__)\
|
||||
|| defined(__AVR_ATmega324__)\
|
||||
|| defined(__AVR_ATmega16__)
|
||||
|
||||
#ifdef defined(VARIANT_MIGHTY)
|
||||
// Mighty Layout
|
||||
static const pin_map_t pinMap[] = {
|
||||
{&DDRB, &PINB, &PORTB, 0}, // B0 0
|
||||
{&DDRB, &PINB, &PORTB, 1}, // B1 1
|
||||
{&DDRB, &PINB, &PORTB, 2}, // B2 2
|
||||
{&DDRB, &PINB, &PORTB, 3}, // B3 3
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 4
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 5
|
||||
{&DDRB, &PINB, &PORTB, 6}, // B6 6
|
||||
{&DDRB, &PINB, &PORTB, 7}, // B7 7
|
||||
{&DDRD, &PIND, &PORTD, 0}, // D0 8
|
||||
{&DDRD, &PIND, &PORTD, 1}, // D1 9
|
||||
{&DDRD, &PIND, &PORTD, 2}, // D2 10
|
||||
{&DDRD, &PIND, &PORTD, 3}, // D3 11
|
||||
{&DDRD, &PIND, &PORTD, 4}, // D4 12
|
||||
{&DDRD, &PIND, &PORTD, 5}, // D5 13
|
||||
{&DDRD, &PIND, &PORTD, 6}, // D6 14
|
||||
{&DDRD, &PIND, &PORTD, 7}, // D7 15
|
||||
{&DDRC, &PINC, &PORTC, 0}, // C0 16
|
||||
{&DDRC, &PINC, &PORTC, 1}, // C1 17
|
||||
{&DDRC, &PINC, &PORTC, 2}, // C2 18
|
||||
{&DDRC, &PINC, &PORTC, 3}, // C3 19
|
||||
{&DDRC, &PINC, &PORTC, 4}, // C4 20
|
||||
{&DDRC, &PINC, &PORTC, 5}, // C5 21
|
||||
{&DDRC, &PINC, &PORTC, 6}, // C6 22
|
||||
{&DDRC, &PINC, &PORTC, 7}, // C7 23
|
||||
{&DDRA, &PINA, &PORTA, 0}, // A0 24
|
||||
{&DDRA, &PINA, &PORTA, 1}, // A1 25
|
||||
{&DDRA, &PINA, &PORTA, 2}, // A2 26
|
||||
{&DDRA, &PINA, &PORTA, 3}, // A3 27
|
||||
{&DDRA, &PINA, &PORTA, 4}, // A4 28
|
||||
{&DDRA, &PINA, &PORTA, 5}, // A5 29
|
||||
{&DDRA, &PINA, &PORTA, 6}, // A6 30
|
||||
{&DDRA, &PINA, &PORTA, 7} // A7 31
|
||||
};
|
||||
#elif defined(VARIANT_BOBUINO)
|
||||
// Bobuino Layout
|
||||
static const pin_map_t pinMap[] = {
|
||||
{&DDRD, &PIND, &PORTD, 0}, // D0 0
|
||||
{&DDRD, &PIND, &PORTD, 1}, // D1 1
|
||||
{&DDRD, &PIND, &PORTD, 2}, // D2 2
|
||||
{&DDRD, &PIND, &PORTD, 3}, // D3 3
|
||||
{&DDRB, &PINB, &PORTB, 0}, // B0 4
|
||||
{&DDRB, &PINB, &PORTB, 1}, // B1 5
|
||||
{&DDRB, &PINB, &PORTB, 2}, // B2 6
|
||||
{&DDRB, &PINB, &PORTB, 3}, // B3 7
|
||||
{&DDRD, &PIND, &PORTD, 5}, // D5 8
|
||||
{&DDRD, &PIND, &PORTD, 6}, // D6 9
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 10
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 11
|
||||
{&DDRB, &PINB, &PORTB, 6}, // B6 12
|
||||
{&DDRB, &PINB, &PORTB, 7}, // B7 13
|
||||
{&DDRA, &PINA, &PORTA, 7}, // A7 14
|
||||
{&DDRA, &PINA, &PORTA, 6}, // A6 15
|
||||
{&DDRA, &PINA, &PORTA, 5}, // A5 16
|
||||
{&DDRA, &PINA, &PORTA, 4}, // A4 17
|
||||
{&DDRA, &PINA, &PORTA, 3}, // A3 18
|
||||
{&DDRA, &PINA, &PORTA, 2}, // A2 19
|
||||
{&DDRA, &PINA, &PORTA, 1}, // A1 20
|
||||
{&DDRA, &PINA, &PORTA, 0}, // A0 21
|
||||
{&DDRC, &PINC, &PORTC, 0}, // C0 22
|
||||
{&DDRC, &PINC, &PORTC, 1}, // C1 23
|
||||
{&DDRC, &PINC, &PORTC, 2}, // C2 24
|
||||
{&DDRC, &PINC, &PORTC, 3}, // C3 25
|
||||
{&DDRC, &PINC, &PORTC, 4}, // C4 26
|
||||
{&DDRC, &PINC, &PORTC, 5}, // C5 27
|
||||
{&DDRC, &PINC, &PORTC, 6}, // C6 28
|
||||
{&DDRC, &PINC, &PORTC, 7}, // C7 29
|
||||
{&DDRD, &PIND, &PORTD, 4}, // D4 30
|
||||
{&DDRD, &PIND, &PORTD, 7} // D7 31
|
||||
};
|
||||
#elif defined(VARIANT_STANDARD)
|
||||
// Standard Layout
|
||||
static const pin_map_t pinMap[] = {
|
||||
{&DDRB, &PINB, &PORTB, 0}, // B0 0
|
||||
{&DDRB, &PINB, &PORTB, 1}, // B1 1
|
||||
{&DDRB, &PINB, &PORTB, 2}, // B2 2
|
||||
{&DDRB, &PINB, &PORTB, 3}, // B3 3
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 4
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 5
|
||||
{&DDRB, &PINB, &PORTB, 6}, // B6 6
|
||||
{&DDRB, &PINB, &PORTB, 7}, // B7 7
|
||||
{&DDRD, &PIND, &PORTD, 0}, // D0 8
|
||||
{&DDRD, &PIND, &PORTD, 1}, // D1 9
|
||||
{&DDRD, &PIND, &PORTD, 2}, // D2 10
|
||||
{&DDRD, &PIND, &PORTD, 3}, // D3 11
|
||||
{&DDRD, &PIND, &PORTD, 4}, // D4 12
|
||||
{&DDRD, &PIND, &PORTD, 5}, // D5 13
|
||||
{&DDRD, &PIND, &PORTD, 6}, // D6 14
|
||||
{&DDRD, &PIND, &PORTD, 7}, // D7 15
|
||||
{&DDRC, &PINC, &PORTC, 0}, // C0 16
|
||||
{&DDRC, &PINC, &PORTC, 1}, // C1 17
|
||||
{&DDRC, &PINC, &PORTC, 2}, // C2 18
|
||||
{&DDRC, &PINC, &PORTC, 3}, // C3 19
|
||||
{&DDRC, &PINC, &PORTC, 4}, // C4 20
|
||||
{&DDRC, &PINC, &PORTC, 5}, // C5 21
|
||||
{&DDRC, &PINC, &PORTC, 6}, // C6 22
|
||||
{&DDRC, &PINC, &PORTC, 7}, // C7 23
|
||||
{&DDRA, &PINA, &PORTA, 7}, // A7 24
|
||||
{&DDRA, &PINA, &PORTA, 6}, // A6 25
|
||||
{&DDRA, &PINA, &PORTA, 5}, // A5 26
|
||||
{&DDRA, &PINA, &PORTA, 4}, // A4 27
|
||||
{&DDRA, &PINA, &PORTA, 3}, // A3 28
|
||||
{&DDRA, &PINA, &PORTA, 2}, // A2 29
|
||||
{&DDRA, &PINA, &PORTA, 1}, // A1 30
|
||||
{&DDRA, &PINA, &PORTA, 0} // A0 31
|
||||
};
|
||||
#else // VARIANT_MIGHTY
|
||||
#error Undefined variant 1284, 644, 324, 64, 32
|
||||
#endif // VARIANT_MIGHTY
|
||||
//------------------------------------------------------------------------------
|
||||
#elif defined(__AVR_ATmega32U4__)
|
||||
#ifdef CORE_TEENSY
|
||||
// Teensy 2.0
|
||||
static const pin_map_t pinMap[] = {
|
||||
{&DDRB, &PINB, &PORTB, 0}, // B0 0
|
||||
{&DDRB, &PINB, &PORTB, 1}, // B1 1
|
||||
{&DDRB, &PINB, &PORTB, 2}, // B2 2
|
||||
{&DDRB, &PINB, &PORTB, 3}, // B3 3
|
||||
{&DDRB, &PINB, &PORTB, 7}, // B7 4
|
||||
{&DDRD, &PIND, &PORTD, 0}, // D0 5
|
||||
{&DDRD, &PIND, &PORTD, 1}, // D1 6
|
||||
{&DDRD, &PIND, &PORTD, 2}, // D2 7
|
||||
{&DDRD, &PIND, &PORTD, 3}, // D3 8
|
||||
{&DDRC, &PINC, &PORTC, 6}, // C6 9
|
||||
{&DDRC, &PINC, &PORTC, 7}, // C7 10
|
||||
{&DDRD, &PIND, &PORTD, 6}, // D6 11
|
||||
{&DDRD, &PIND, &PORTD, 7}, // D7 12
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 13
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 14
|
||||
{&DDRB, &PINB, &PORTB, 6}, // B6 15
|
||||
{&DDRF, &PINF, &PORTF, 7}, // F7 16
|
||||
{&DDRF, &PINF, &PORTF, 6}, // F6 17
|
||||
{&DDRF, &PINF, &PORTF, 5}, // F5 18
|
||||
{&DDRF, &PINF, &PORTF, 4}, // F4 19
|
||||
{&DDRF, &PINF, &PORTF, 1}, // F1 20
|
||||
{&DDRF, &PINF, &PORTF, 0}, // F0 21
|
||||
{&DDRD, &PIND, &PORTD, 4}, // D4 22
|
||||
{&DDRD, &PIND, &PORTD, 5}, // D5 23
|
||||
{&DDRE, &PINE, &PORTE, 6} // E6 24
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
#else // CORE_TEENSY
|
||||
// Leonardo
|
||||
static const pin_map_t pinMap[] = {
|
||||
{&DDRD, &PIND, &PORTD, 2}, // D2 0
|
||||
{&DDRD, &PIND, &PORTD, 3}, // D3 1
|
||||
{&DDRD, &PIND, &PORTD, 1}, // D1 2
|
||||
{&DDRD, &PIND, &PORTD, 0}, // D0 3
|
||||
{&DDRD, &PIND, &PORTD, 4}, // D4 4
|
||||
{&DDRC, &PINC, &PORTC, 6}, // C6 5
|
||||
{&DDRD, &PIND, &PORTD, 7}, // D7 6
|
||||
{&DDRE, &PINE, &PORTE, 6}, // E6 7
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 8
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 9
|
||||
{&DDRB, &PINB, &PORTB, 6}, // B6 10
|
||||
{&DDRB, &PINB, &PORTB, 7}, // B7 11
|
||||
{&DDRD, &PIND, &PORTD, 6}, // D6 12
|
||||
{&DDRC, &PINC, &PORTC, 7}, // C7 13
|
||||
{&DDRB, &PINB, &PORTB, 3}, // B3 14
|
||||
{&DDRB, &PINB, &PORTB, 1}, // B1 15
|
||||
{&DDRB, &PINB, &PORTB, 2}, // B2 16
|
||||
{&DDRB, &PINB, &PORTB, 0}, // B0 17
|
||||
{&DDRF, &PINF, &PORTF, 7}, // F7 18
|
||||
{&DDRF, &PINF, &PORTF, 6}, // F6 19
|
||||
{&DDRF, &PINF, &PORTF, 5}, // F5 20
|
||||
{&DDRF, &PINF, &PORTF, 4}, // F4 21
|
||||
{&DDRF, &PINF, &PORTF, 1}, // F1 22
|
||||
{&DDRF, &PINF, &PORTF, 0}, // F0 23
|
||||
{&DDRD, &PIND, &PORTD, 4}, // D4 24
|
||||
{&DDRD, &PIND, &PORTD, 7}, // D7 25
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 26
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 27
|
||||
{&DDRB, &PINB, &PORTB, 6}, // B6 28
|
||||
{&DDRD, &PIND, &PORTD, 6} // D6 29
|
||||
};
|
||||
#endif // CORE_TEENSY
|
||||
//------------------------------------------------------------------------------
|
||||
#elif defined(__AVR_AT90USB646__)\
|
||||
|| defined(__AVR_AT90USB1286__)
|
||||
// Teensy++ 1.0 & 2.0
|
||||
static const pin_map_t pinMap[] = {
|
||||
{&DDRD, &PIND, &PORTD, 0}, // D0 0
|
||||
{&DDRD, &PIND, &PORTD, 1}, // D1 1
|
||||
{&DDRD, &PIND, &PORTD, 2}, // D2 2
|
||||
{&DDRD, &PIND, &PORTD, 3}, // D3 3
|
||||
{&DDRD, &PIND, &PORTD, 4}, // D4 4
|
||||
{&DDRD, &PIND, &PORTD, 5}, // D5 5
|
||||
{&DDRD, &PIND, &PORTD, 6}, // D6 6
|
||||
{&DDRD, &PIND, &PORTD, 7}, // D7 7
|
||||
{&DDRE, &PINE, &PORTE, 0}, // E0 8
|
||||
{&DDRE, &PINE, &PORTE, 1}, // E1 9
|
||||
{&DDRC, &PINC, &PORTC, 0}, // C0 10
|
||||
{&DDRC, &PINC, &PORTC, 1}, // C1 11
|
||||
{&DDRC, &PINC, &PORTC, 2}, // C2 12
|
||||
{&DDRC, &PINC, &PORTC, 3}, // C3 13
|
||||
{&DDRC, &PINC, &PORTC, 4}, // C4 14
|
||||
{&DDRC, &PINC, &PORTC, 5}, // C5 15
|
||||
{&DDRC, &PINC, &PORTC, 6}, // C6 16
|
||||
{&DDRC, &PINC, &PORTC, 7}, // C7 17
|
||||
{&DDRE, &PINE, &PORTE, 6}, // E6 18
|
||||
{&DDRE, &PINE, &PORTE, 7}, // E7 19
|
||||
{&DDRB, &PINB, &PORTB, 0}, // B0 20
|
||||
{&DDRB, &PINB, &PORTB, 1}, // B1 21
|
||||
{&DDRB, &PINB, &PORTB, 2}, // B2 22
|
||||
{&DDRB, &PINB, &PORTB, 3}, // B3 23
|
||||
{&DDRB, &PINB, &PORTB, 4}, // B4 24
|
||||
{&DDRB, &PINB, &PORTB, 5}, // B5 25
|
||||
{&DDRB, &PINB, &PORTB, 6}, // B6 26
|
||||
{&DDRB, &PINB, &PORTB, 7}, // B7 27
|
||||
{&DDRA, &PINA, &PORTA, 0}, // A0 28
|
||||
{&DDRA, &PINA, &PORTA, 1}, // A1 29
|
||||
{&DDRA, &PINA, &PORTA, 2}, // A2 30
|
||||
{&DDRA, &PINA, &PORTA, 3}, // A3 31
|
||||
{&DDRA, &PINA, &PORTA, 4}, // A4 32
|
||||
{&DDRA, &PINA, &PORTA, 5}, // A5 33
|
||||
{&DDRA, &PINA, &PORTA, 6}, // A6 34
|
||||
{&DDRA, &PINA, &PORTA, 7}, // A7 35
|
||||
{&DDRE, &PINE, &PORTE, 4}, // E4 36
|
||||
{&DDRE, &PINE, &PORTE, 5}, // E5 37
|
||||
{&DDRF, &PINF, &PORTF, 0}, // F0 38
|
||||
{&DDRF, &PINF, &PORTF, 1}, // F1 39
|
||||
{&DDRF, &PINF, &PORTF, 2}, // F2 40
|
||||
{&DDRF, &PINF, &PORTF, 3}, // F3 41
|
||||
{&DDRF, &PINF, &PORTF, 4}, // F4 42
|
||||
{&DDRF, &PINF, &PORTF, 5}, // F5 43
|
||||
{&DDRF, &PINF, &PORTF, 6}, // F6 44
|
||||
{&DDRF, &PINF, &PORTF, 7} // F7 45
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
#else // CPU type
|
||||
#error unknown CPU type
|
||||
#endif // CPU type
|
||||
//------------------------------------------------------------------------------
|
||||
/** count of pins */
|
||||
static const uint8_t digitalPinCount = sizeof(pinMap)/sizeof(pin_map_t);
|
||||
//==============================================================================
|
||||
/** generate bad pin number error */
|
||||
void badPinNumber(void)
|
||||
__attribute__((error("Pin number is too large or not a constant")));
|
||||
//------------------------------------------------------------------------------
|
||||
/** Check for valid pin number
|
||||
* @param[in] pin Number of pin to be checked.
|
||||
*/
|
||||
static ALWAYS_INLINE void badPinCheck(uint8_t pin) {
|
||||
if (!__builtin_constant_p(pin) || pin >= digitalPinCount) {
|
||||
badPinNumber();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** fast write helper
|
||||
* @param[in] address I/O register address
|
||||
* @param[in] bit bit number to write
|
||||
* @param[in] level value for bit
|
||||
*/
|
||||
static ALWAYS_INLINE void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) {
|
||||
uint8_t oldSREG;
|
||||
if (address > (uint8_t*)0X5F) {
|
||||
oldSREG = SREG;
|
||||
cli();
|
||||
}
|
||||
if (level) {
|
||||
*address |= 1 << bit;
|
||||
} else {
|
||||
*address &= ~(1 << bit);
|
||||
}
|
||||
if (address > (uint8_t*)0X5F) {
|
||||
SREG = oldSREG;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** read pin value
|
||||
* @param[in] pin Arduino pin number
|
||||
* @return value read
|
||||
*/
|
||||
static ALWAYS_INLINE bool fastDigitalRead(uint8_t pin) {
|
||||
badPinCheck(pin);
|
||||
return (*pinMap[pin].pin >> pinMap[pin].bit) & 1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** toggle a pin
|
||||
* @param[in] pin Arduino pin number
|
||||
*
|
||||
* If the pin is in output mode toggle the pin level.
|
||||
* If the pin is in input mode toggle the state of the 20K pullup.
|
||||
*/
|
||||
static ALWAYS_INLINE void fastDigitalToggle(uint8_t pin) {
|
||||
badPinCheck(pin);
|
||||
if (pinMap[pin].pin > (uint8_t*)0X5F) {
|
||||
// must write bit to high address port
|
||||
*pinMap[pin].pin = 1 << pinMap[pin].bit;
|
||||
} else {
|
||||
// will compile to sbi and PIN register will not be read.
|
||||
*pinMap[pin].pin |= 1 << pinMap[pin].bit;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** Set pin value
|
||||
* @param[in] pin Arduino pin number
|
||||
* @param[in] level value to write
|
||||
*/
|
||||
static ALWAYS_INLINE void fastDigitalWrite(uint8_t pin, bool level) {
|
||||
badPinCheck(pin);
|
||||
fastBitWriteSafe(pinMap[pin].port, pinMap[pin].bit, level);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** set pin mode
|
||||
* @param[in] pin Arduino pin number
|
||||
* @param[in] mode if true set output mode else input mode
|
||||
*
|
||||
* fastPinMode does not enable or disable the 20K pullup for input mode.
|
||||
*/
|
||||
static ALWAYS_INLINE void fastPinMode(uint8_t pin, bool mode) {
|
||||
badPinCheck(pin);
|
||||
fastBitWriteSafe(pinMap[pin].ddr, pinMap[pin].bit, mode);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** set pin configuration
|
||||
* @param[in] pin Arduino pin number
|
||||
* @param[in] mode If true set output mode else input mode
|
||||
* @param[in] level If mode is output, set level high/low.
|
||||
* If mode is input, enable or disable the pin's 20K pullup.
|
||||
*/
|
||||
static ALWAYS_INLINE void fastPinConfig(uint8_t pin, bool mode, bool level) {
|
||||
fastPinMode(pin, mode);
|
||||
fastDigitalWrite(pin, level);
|
||||
}
|
||||
//==============================================================================
|
||||
/**
|
||||
* @class DigitalPin
|
||||
* @brief Fast AVR digital port I/O
|
||||
*/
|
||||
template<uint8_t PinNumber>
|
||||
class DigitalPin {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Constructor */
|
||||
DigitalPin() {}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Constructor
|
||||
* @param[in] pinMode if true set output mode else input mode.
|
||||
*/
|
||||
explicit DigitalPin(bool pinMode) {
|
||||
mode(pinMode);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Constructor
|
||||
* @param[in] mode If true set output mode else input mode
|
||||
* @param[in] level If mode is output, set level high/low.
|
||||
* If mode is input, enable or disable the pin's 20K pullup.
|
||||
*/
|
||||
DigitalPin(bool mode, bool level) {
|
||||
config(mode, level);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Asignment operator
|
||||
* @param[in] value If true set the pin's level high else set the
|
||||
* pin's level low.
|
||||
*
|
||||
* @return This DigitalPin instance.
|
||||
*/
|
||||
inline DigitalPin & operator = (bool value) __attribute__((always_inline)) {
|
||||
write(value);
|
||||
return *this;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Parenthesis operator
|
||||
* @return Pin's level
|
||||
*/
|
||||
inline operator bool () const __attribute__((always_inline)) {
|
||||
return read();
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** set pin configuration
|
||||
* @param[in] mode If true set output mode else input mode
|
||||
* @param[in] level If mode is output, set level high/low.
|
||||
* If mode is input, enable or disable the pin's 20K pullup.
|
||||
*/
|
||||
ALWAYS_INLINE void config(bool mode, bool level) {
|
||||
fastPinConfig(PinNumber, mode, level);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/**
|
||||
* Set pin level high if output mode or enable 20K pullup if input mode.
|
||||
*/
|
||||
ALWAYS_INLINE void high() {write(true);}
|
||||
//----------------------------------------------------------------------------
|
||||
/**
|
||||
* Set pin level low if output mode or disable 20K pullup if input mode.
|
||||
*/
|
||||
ALWAYS_INLINE void low() {write(false);}
|
||||
//----------------------------------------------------------------------------
|
||||
/**
|
||||
* Set pin mode
|
||||
* @param[in] pinMode if true set output mode else input mode.
|
||||
*
|
||||
* mode() does not enable or disable the 20K pullup for input mode.
|
||||
*/
|
||||
ALWAYS_INLINE void mode(bool pinMode) {
|
||||
fastPinMode(PinNumber, pinMode);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** @return Pin's level */
|
||||
ALWAYS_INLINE bool read() const {
|
||||
return fastDigitalRead(PinNumber);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** toggle a pin
|
||||
*
|
||||
* If the pin is in output mode toggle the pin's level.
|
||||
* If the pin is in input mode toggle the state of the 20K pullup.
|
||||
*/
|
||||
ALWAYS_INLINE void toggle() {
|
||||
fastDigitalToggle(PinNumber);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Write the pin's level.
|
||||
* @param[in] value If true set the pin's level high else set the
|
||||
* pin's level low.
|
||||
*/
|
||||
ALWAYS_INLINE void write(bool value) {
|
||||
fastDigitalWrite(PinNumber, value);
|
||||
}
|
||||
};
|
||||
#endif // DigitalPin_h
|
||||
/** @} */
|
||||
@@ -0,0 +1,63 @@
|
||||
/* Arduino I2C Library
|
||||
* Copyright (C) 2013 by William Greiman
|
||||
*
|
||||
* This file is part of the Arduino I2C Library
|
||||
*
|
||||
* This Library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino I2C Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @file I2cConstants.h
|
||||
* @brief Two Wire Interface constants.
|
||||
*
|
||||
* @defgroup twoWire I2C constants
|
||||
* @ingroup internals
|
||||
* @details Two Wire Interface library.
|
||||
* @{
|
||||
*/
|
||||
#ifndef I2cConstants_h
|
||||
#define I2cConstants_h
|
||||
#include <inttypes.h>
|
||||
|
||||
/** Option argument for transfer() of transferContinue() to continue a
|
||||
an I2C operation. */
|
||||
const uint8_t I2C_CONTINUE = 0;
|
||||
|
||||
/** Option argument for transfer() of transferContinue() to end a
|
||||
transfer with a STOP condition */
|
||||
const uint8_t I2C_STOP = 1;
|
||||
|
||||
/** Option argument for transfer() of transferContinue() to end a
|
||||
transfer with a repeated START condition */
|
||||
const uint8_t I2C_REP_START = 2;
|
||||
|
||||
/** Set I2C bus speed to 100 kHz. Used by TwiMaster class. */
|
||||
const uint8_t I2C_100KHZ = 0;
|
||||
|
||||
/** Set I2C bus speed to 400 kHz. Used by TwiMaster class. */
|
||||
const uint8_t I2C_400KHZ = 1;
|
||||
|
||||
/** Bit to OR with address for a read operation. */
|
||||
const uint8_t I2C_READ = 1;
|
||||
|
||||
/** Bit to OR with address for write operation. */
|
||||
const uint8_t I2C_WRITE = 0;
|
||||
|
||||
/** Disable internal pull-ups on SDA and SCL. Used by TwiMaster class. */
|
||||
const uint8_t I2C_NO_PULLUPS = 0;
|
||||
|
||||
/** Enable internal pull-ups on SDA and SCL. Used by TwiMaster class. */
|
||||
const uint8_t I2C_INTERNAL_PULLUPS = 1;
|
||||
#endif // I2cConstants_h
|
||||
/** @} */
|
||||
@@ -0,0 +1,68 @@
|
||||
/* Arduino DigitalIO Library
|
||||
* Copyright (C) 2013 by William Greiman
|
||||
*
|
||||
* This file is part of the Arduino DigitalIO Library
|
||||
*
|
||||
* This Library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino DigitalIO Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief Digital AVR port I/O with runtime pin number.
|
||||
*
|
||||
* @defgroup runtimeDigital Runtime Pin I/O
|
||||
* @details Two Wire Interface library.
|
||||
* @{
|
||||
*/
|
||||
#include "PinIO.h"
|
||||
#include <util/atomic.h>
|
||||
#include <Arduino.h>
|
||||
//==============================================================================
|
||||
/** Constructor
|
||||
* @param[in] pin Pin assigned to this object.
|
||||
*/
|
||||
PinIO::PinIO(uint8_t pin) {
|
||||
begin(pin);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** Initialize pin bit mask and port address.
|
||||
* @param[in] pin Arduino board pin number.
|
||||
* @return true for success or false if invalid pin number.
|
||||
*/
|
||||
bool PinIO::begin(uint8_t pin) {
|
||||
if (pin >= NUM_DIGITAL_PINS) return false;
|
||||
uint8_t port = digitalPinToPort(pin);
|
||||
pinReg_ = portInputRegister(port);
|
||||
bit_ = digitalPinToBitMask(pin);
|
||||
mask_ = ~bit_;
|
||||
portReg_ = pinReg_ + 2;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** Configure the pin
|
||||
*
|
||||
* @param[in] mode Configure as output mode if true else input mode.
|
||||
* @param[in] data For output mode set pin high if true else low.
|
||||
* For input mode enable 20K pullup if true else Hi-Z.
|
||||
*
|
||||
* This function may be used with interrupts enabled or disabled.
|
||||
* The previous interrupt state will be restored.
|
||||
*/
|
||||
void PinIO::config(bool mode, bool data) {
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||
modeI(mode);
|
||||
writeI(data);
|
||||
}
|
||||
}
|
||||
/** @} */
|
||||
@@ -0,0 +1,154 @@
|
||||
/* Arduino DigitalIO Library
|
||||
* Copyright (C) 2013 by William Greiman
|
||||
*
|
||||
* This file is part of the Arduino DigitalIO Library
|
||||
*
|
||||
* This Library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino DigitalIO Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief Digital AVR port I/O with runtime pin number.
|
||||
*
|
||||
* @defgroup runtimeDigital Runtime Pin I/O
|
||||
* @ingroup internals
|
||||
* @details Two Wire Interface library.
|
||||
* @{
|
||||
*/
|
||||
#ifndef PinIO_h
|
||||
#define PinIO_h
|
||||
#include <util/atomic.h>
|
||||
#include <avr/io.h>
|
||||
/** @brief Helper macro for complex inline attributes */
|
||||
#define ALWAYS_INLINE inline __attribute__((always_inline))
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* @class PinIO
|
||||
* @brief AVR port I/O with runtime pin numbers.
|
||||
*/
|
||||
class PinIO {
|
||||
public:
|
||||
/** Create a PinIO object with no assigned pin. */
|
||||
PinIO() : bit_(0), mask_(0XFF) {}
|
||||
/** Constructor
|
||||
* @param[in] pin Pin assigned to this object.
|
||||
*/
|
||||
explicit PinIO(uint8_t pin);
|
||||
/** Initialize pin bit mask and port address.
|
||||
* @param[in] pin Arduino board pin number.
|
||||
* @return true for success or false if invalid pin number.
|
||||
*/
|
||||
bool begin(uint8_t pin);
|
||||
/** Configure the pin
|
||||
*
|
||||
* @param[in] mode Configure as output mode if true else input mode.
|
||||
* @param[in] data For output mode set pin high if true else low.
|
||||
* For input mode enable 20K pullup if true else Hi-Z.
|
||||
*
|
||||
* This function may be used with interrupts enabled or disabled.
|
||||
* The previous interrupt state will be restored.
|
||||
*/
|
||||
void config(bool mode, bool data);
|
||||
//----------------------------------------------------------------------------
|
||||
/** @return Pin's level */
|
||||
ALWAYS_INLINE bool read() {return *pinReg_ & bit_;}
|
||||
//----------------------------------------------------------------------------
|
||||
/** toggle a pin
|
||||
*
|
||||
* If the pin is in output mode toggle the pin's level.
|
||||
* If the pin is in input mode toggle the state of the 20K pullup.
|
||||
*/
|
||||
ALWAYS_INLINE void toggle() {*pinReg_ = bit_;}
|
||||
//============================================================================
|
||||
/**
|
||||
* Set pin high if output mode or enable 20K pullup if input mode.
|
||||
*
|
||||
* This function must be called with interrupts disabled.
|
||||
* This function will not change the interrupt state.
|
||||
*/
|
||||
ALWAYS_INLINE void highI() {writeI(1);}
|
||||
/**
|
||||
* Set pin low if output mode or disable 20K pullup if input mode.
|
||||
*
|
||||
* This function must be called with interrupts disabled.
|
||||
* This function will not change the interrupt state.
|
||||
*/
|
||||
ALWAYS_INLINE void lowI() {writeI(0);}
|
||||
/**
|
||||
* Set pin mode
|
||||
* @param[in] mode if true set output mode else input mode.
|
||||
*
|
||||
* mode() does not enable or disable the 20K pullup for input mode.
|
||||
*
|
||||
* This function must be called with interrupts disabled.
|
||||
* This function will not change the interrupt state.
|
||||
*/
|
||||
ALWAYS_INLINE void modeI(bool mode) {
|
||||
volatile uint8_t* ddrReg = pinReg_ + 1;
|
||||
*ddrReg = mode ? *ddrReg | bit_ : *ddrReg & mask_;
|
||||
}
|
||||
/** Write pin.
|
||||
*
|
||||
* @param[in] level If output mode set pin high if true else low.
|
||||
* If input mode enable 20K pullup if true else disable pullup.
|
||||
*
|
||||
* This function must be called with interrupts disabled.
|
||||
* This function will not change the interrupt state.
|
||||
*/
|
||||
ALWAYS_INLINE void writeI(bool level) {
|
||||
*portReg_ = level ? *portReg_ | bit_ : *portReg_ & mask_;
|
||||
}
|
||||
//============================================================================
|
||||
/**
|
||||
* Set pin level high if output mode or enable 20K pullup if input mode.
|
||||
*
|
||||
* This function will enable interrupts. This function should not be
|
||||
* called in an ISR or where interrupts are disabled.
|
||||
*/
|
||||
ALWAYS_INLINE void high() {ATOMIC_BLOCK(ATOMIC_FORCEON) {highI();}}
|
||||
/**
|
||||
* Set pin level low if output mode or disable 20K pullup if input mode.
|
||||
*
|
||||
* This function will enable interrupts. This function should not be
|
||||
* called in an ISR or where interrupts are disabled.
|
||||
*/
|
||||
ALWAYS_INLINE void low() {ATOMIC_BLOCK(ATOMIC_FORCEON) {lowI();}}
|
||||
/**
|
||||
* Set pin mode
|
||||
* @param[in] pinMode if true set output mode else input mode.
|
||||
*
|
||||
* mode() does not enable or disable the 20K pullup for input mode.
|
||||
*
|
||||
* This function will enable interrupts. This function should not be
|
||||
* called in an ISR or where interrupts are disabled.
|
||||
*/
|
||||
ALWAYS_INLINE void mode(bool pinMode) {ATOMIC_BLOCK(ATOMIC_FORCEON) {modeI(pinMode);}}
|
||||
/** Write pin.
|
||||
*
|
||||
* @param[in] level If output mode set pin high if true else low.
|
||||
* If input mode enable 20K pullup if true else disable pullup.
|
||||
*
|
||||
* This function will enable interrupts. This function should not be
|
||||
* called in an ISR or where interrupts are disabled.
|
||||
*/
|
||||
ALWAYS_INLINE void write(bool level) {ATOMIC_BLOCK(ATOMIC_FORCEON) {writeI(level);}}
|
||||
//----------------------------------------------------------------------------
|
||||
private:
|
||||
uint8_t bit_;
|
||||
uint8_t mask_;
|
||||
volatile uint8_t* pinReg_;
|
||||
volatile uint8_t* portReg_;
|
||||
};
|
||||
#endif // PinIO_h
|
||||
/** @} */
|
||||
@@ -0,0 +1,239 @@
|
||||
/* Arduino DigitalIO Library
|
||||
* Copyright (C) 2013 by William Greiman
|
||||
*
|
||||
* This file is part of the Arduino DigitalIO Library
|
||||
*
|
||||
* This Library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino DigitalIO Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief Software I2C library
|
||||
*
|
||||
* @defgroup softI2C Software I2C
|
||||
* @details Software Two Wire Interface library.
|
||||
* @{
|
||||
*/
|
||||
#include "SoftI2cMaster.h"
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* Start an I2C transfer with possible continuation.
|
||||
*
|
||||
* @param[in] addrRW I2C slave address plus R/W bit.
|
||||
* The I2C slave address is in the high seven bits
|
||||
* and is ORed with on of the following:
|
||||
* - I2C_READ for a read transfer.
|
||||
* - I2C_WRITE for a write transfer.
|
||||
* .
|
||||
* @param[in,out] buf Source or destination for transfer.
|
||||
* @param[in] nbytes Number of bytes to transfer (may be zero).
|
||||
* @param[in] option Option for ending the transfer, one of:
|
||||
* - I2C_STOP end the transfer with an I2C stop
|
||||
* condition.
|
||||
* - I2C_REP_START end the transfer with an I2C
|
||||
* repeated start condition.
|
||||
* - I2C_CONTINUE allow additional transferContinue()
|
||||
* calls.
|
||||
* .
|
||||
* @return true for success else false.
|
||||
*/
|
||||
bool I2cMasterBase::transfer(uint8_t addrRW,
|
||||
void *buf, size_t nbytes, uint8_t option) {
|
||||
if (_state != STATE_REP_START) {
|
||||
start();
|
||||
}
|
||||
if (!write(addrRW)) {
|
||||
_state = addrRW & I2C_READ ? STATE_RX_ADDR_NACK : STATE_TX_ADDR_NACK;
|
||||
return false;
|
||||
}
|
||||
_state = addrRW & I2C_READ ? STATE_RX_DATA : STATE_TX_DATA;
|
||||
return transferContinue(buf, nbytes, option);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* Continue an I2C transfer.
|
||||
*
|
||||
* @param[in,out] buf Source or destination for transfer.
|
||||
* @param[in] nbytes Number of bytes to transfer (may be zero).
|
||||
* @param[in] option Option for ending the transfer, one of:
|
||||
* - I2C_STOP end the transfer with an I2C stop
|
||||
* condition.
|
||||
* - I2C_REP_START end the transfer with an I2C
|
||||
* repeated start condition.
|
||||
* - I2C_CONTINUE allow additional transferContinue()
|
||||
* calls.
|
||||
* .
|
||||
* @return true for success else false.
|
||||
*/
|
||||
bool I2cMasterBase::transferContinue(void *buf, size_t nbytes, uint8_t option) {
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(buf);
|
||||
if (_state == STATE_RX_DATA) {
|
||||
for (size_t i = 0; i < nbytes; i++) {
|
||||
p[i] = read(i == (nbytes - 1) && option != I2C_CONTINUE);
|
||||
}
|
||||
} else if (_state == STATE_TX_DATA) {
|
||||
for (size_t i = 0; i < nbytes; i++) {
|
||||
if (!write(p[i])) {
|
||||
_state = STATE_TX_DATA_NACK;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (option == I2C_STOP) {
|
||||
stop();
|
||||
_state = STATE_STOP;
|
||||
} else if (option == I2C_REP_START) {
|
||||
start();
|
||||
_state = STATE_STOP;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//==============================================================================
|
||||
// WARNING don't change SoftI2cMaster unless you verify the change with a scope
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor, initialize SCL/SDA pins and set the bus high.
|
||||
*
|
||||
* @param[in] sdaPin The software SDA pin number.
|
||||
*
|
||||
* @param[in] sclPin The software SCL pin number.
|
||||
*/
|
||||
SoftI2cMaster::SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin) {
|
||||
begin(sclPin, sdaPin);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* Initialize SCL/SDA pins and set the bus high.
|
||||
*
|
||||
* @param[in] sdaPin The software SDA pin number.
|
||||
*
|
||||
* @param[in] sclPin The software SCL pin number.
|
||||
*/
|
||||
void SoftI2cMaster::begin(uint8_t sclPin, uint8_t sdaPin) {
|
||||
uint8_t port;
|
||||
|
||||
// Get bit mask and address of scl registers.
|
||||
_sclBit = digitalPinToBitMask(sclPin);
|
||||
port = digitalPinToPort(sclPin);
|
||||
_sclDDR = portModeRegister(port);
|
||||
volatile uint8_t* sclOutReg = portOutputRegister(port);
|
||||
|
||||
// Get bit mask and address of sda registers.
|
||||
_sdaBit = digitalPinToBitMask(sdaPin);
|
||||
port = digitalPinToPort(sdaPin);
|
||||
_sdaDDR = portModeRegister(port);
|
||||
_sdaInReg = portInputRegister(port);
|
||||
volatile uint8_t* sdaOutReg = portOutputRegister(port);
|
||||
|
||||
// Clear PORT bit for scl and sda.
|
||||
uint8_t s = SREG;
|
||||
noInterrupts();
|
||||
*sclOutReg &= ~_sclBit;
|
||||
*sdaOutReg &= ~_sdaBit;
|
||||
SREG = s;
|
||||
|
||||
// Set scl and sda high.
|
||||
writeScl(HIGH);
|
||||
writeSda(HIGH);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/* Read a byte and send ACK if more reads follow else NACK to terminate read.
|
||||
*
|
||||
* @param[in] last Set true to terminate the read else false.
|
||||
*
|
||||
* @return The byte read from the I2C bus.
|
||||
*/
|
||||
uint8_t SoftI2cMaster::read(uint8_t last) {
|
||||
uint8_t b = 0;
|
||||
|
||||
// Set sda to high Z mode for read.
|
||||
writeSda(HIGH);
|
||||
// Read a byte.
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
// Don't change this loop unless you verify the change with a scope.
|
||||
b <<= 1;
|
||||
sclDelay(16);
|
||||
writeScl(HIGH);
|
||||
sclDelay(12);
|
||||
if (readSda()) b |= 1;
|
||||
writeScl(LOW);
|
||||
}
|
||||
// send ACK or NACK
|
||||
writeSda(last);
|
||||
sclDelay(12);
|
||||
writeScl(HIGH);
|
||||
sclDelay(18);
|
||||
writeScl(LOW);
|
||||
writeSda(LOW);
|
||||
return b;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/* Issue a start condition. */
|
||||
void SoftI2cMaster::start() {
|
||||
if (!readSda()) {
|
||||
writeSda(HIGH);
|
||||
writeScl(HIGH);
|
||||
sclDelay(20);
|
||||
}
|
||||
writeSda(LOW);
|
||||
sclDelay(20);
|
||||
writeScl(LOW);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/* Issue a stop condition. */
|
||||
void SoftI2cMaster::stop(void) {
|
||||
writeSda(LOW);
|
||||
sclDelay(20);
|
||||
writeScl(HIGH);
|
||||
sclDelay(20);
|
||||
writeSda(HIGH);
|
||||
sclDelay(20);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
* Write a byte.
|
||||
*
|
||||
* @param[in] data The byte to send.
|
||||
*
|
||||
* @return The value true, 1, if the slave returned an ACK or false for NACK.
|
||||
*/
|
||||
bool SoftI2cMaster::write(uint8_t data) {
|
||||
// write byte
|
||||
for (uint8_t m = 0X80; m != 0; m >>= 1) {
|
||||
// don't change this loop unless you verify the change with a scope
|
||||
writeSda(m & data);
|
||||
sclDelay(8);
|
||||
writeScl(HIGH);
|
||||
sclDelay(18);
|
||||
writeScl(LOW);
|
||||
}
|
||||
sclDelay(8);
|
||||
// Go to sda high Z mode for input.
|
||||
writeSda(HIGH);
|
||||
writeScl(HIGH);
|
||||
sclDelay(16);
|
||||
|
||||
// Get ACK or NACK.
|
||||
uint8_t rtn = readSda();
|
||||
|
||||
// pull scl low.
|
||||
writeScl(LOW);
|
||||
|
||||
// Pull sda low.
|
||||
writeSda(LOW);
|
||||
return rtn == 0;
|
||||
}
|
||||
/** @} */
|
||||
@@ -0,0 +1,335 @@
|
||||
/* Arduino DigitalIO Library
|
||||
* Copyright (C) 2013 by William Greiman
|
||||
*
|
||||
* This file is part of the Arduino DigitalIO Library
|
||||
*
|
||||
* This Library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino DigitalIO Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SOFT_I2C_MASTER_H
|
||||
#define SOFT_I2C_MASTER_H
|
||||
/**
|
||||
* @file
|
||||
* @brief Software I2C library
|
||||
*
|
||||
* @defgroup softI2C Software I2C
|
||||
* @ingroup internals
|
||||
* @details Software Two Wire Interface library.
|
||||
* @{
|
||||
*/
|
||||
#if ARDUINO < 100
|
||||
#error Requires Arduino 1.0 or greater.
|
||||
#else // ARDUINO
|
||||
#include <Arduino.h>
|
||||
#endif // ARDUINO
|
||||
#include <util/delay_basic.h>
|
||||
#include "DigitalPin.h"
|
||||
#include "I2cConstants.h"
|
||||
//------------------------------------------------------------------------------
|
||||
// State codes.
|
||||
|
||||
/** Stop condition transmitted. */
|
||||
const uint8_t STATE_STOP = 0;
|
||||
|
||||
/** Repeated start condition transmitted. */
|
||||
const uint8_t STATE_REP_START = 1;
|
||||
|
||||
/** Read data transfer active. */
|
||||
const uint8_t STATE_RX_DATA = 2;
|
||||
|
||||
/** Write data transfer active. */
|
||||
const uint8_t STATE_TX_DATA = 3;
|
||||
|
||||
/** Slave address plus read bit transmitted, NACK received. */
|
||||
const uint8_t STATE_RX_ADDR_NACK = 4;
|
||||
|
||||
/** Slave address plus write bit transmitted, NACK received. */
|
||||
const uint8_t STATE_TX_ADDR_NACK = 5;
|
||||
/** Data byte transmitted, NACK received. */
|
||||
const uint8_t STATE_TX_DATA_NACK = 6;
|
||||
//==============================================================================
|
||||
/**
|
||||
* @class I2cMasterBase
|
||||
* @brief Base class for FastI2cMaster, SoftI2cMaster
|
||||
*/
|
||||
class I2cMasterBase {
|
||||
public:
|
||||
I2cMasterBase() : _state(STATE_STOP) {}
|
||||
/** Read a byte
|
||||
*
|
||||
* @note This function should only be used by experts. Data should be
|
||||
* accessed by calling transfer() and transferContinue()
|
||||
*
|
||||
* @param[in] last send a NACK to terminate read if last is true else
|
||||
* send an ACK to continue the read.
|
||||
*
|
||||
* @return byte read from I2C bus
|
||||
*/
|
||||
virtual uint8_t read(uint8_t last) = 0;
|
||||
|
||||
/** Issue a start condition
|
||||
*
|
||||
* @note This function should only be used by experts. Data should be
|
||||
* accessed by calling transfer() and transferContinue()
|
||||
*/
|
||||
virtual void start() = 0;
|
||||
/** Issue a stop condition.
|
||||
*
|
||||
* @note This function should only be used by experts. Data should be
|
||||
* accessed by calling transfer() and transferContinue()
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/**
|
||||
* Start an I2C transfer with possible continuation.
|
||||
*
|
||||
* @param[in] addressRW I2C slave address plus R/W bit.
|
||||
* The I2C slave address is in the high seven bits
|
||||
* and is ORed with on of the following:
|
||||
* - I2C_READ for a read transfer.
|
||||
* - I2C_WRITE for a write transfer.
|
||||
* .
|
||||
* @param[in,out] buf Source or destination for transfer.
|
||||
* @param[in] nbyte Number of bytes to transfer (may be zero).
|
||||
* @param[in] option Option for ending the transfer, one of:
|
||||
* - I2C_STOP end the transfer with an I2C stop
|
||||
* condition.
|
||||
* - I2C_REP_START end the transfer with an I2C
|
||||
* repeated start condition.
|
||||
* - I2C_CONTINUE allow additional transferContinue()
|
||||
* calls.
|
||||
* .
|
||||
* @return true for success else false.
|
||||
*/
|
||||
bool transfer(uint8_t addressRW, void *buf,
|
||||
size_t nbyte, uint8_t option = I2C_STOP);
|
||||
|
||||
/**
|
||||
* Continue an I2C transfer.
|
||||
*
|
||||
* @param[in,out] buf Source or destination for transfer.
|
||||
* @param[in] nbyte Number of bytes to transfer (may be zero).
|
||||
* @param[in] option Option for ending the transfer, one of:
|
||||
* - I2C_STOP end the transfer with an I2C stop
|
||||
* condition.
|
||||
* - I2C_REP_START end the transfer with an I2C
|
||||
* repeated start condition.
|
||||
* - I2C_CONTINUE allow additional transferContinue()
|
||||
* calls.
|
||||
* .
|
||||
* @return true for success else false.
|
||||
*/
|
||||
bool transferContinue(void *buf, size_t nbyte, uint8_t option = I2C_STOP);
|
||||
/** Write a byte
|
||||
*
|
||||
* @note This function should only be used by experts. Data should be
|
||||
* accessed by calling transfer() and transferContinue()
|
||||
*
|
||||
* @param[in] data byte to write
|
||||
* @return true for ACK or false for NACK */
|
||||
virtual bool write(uint8_t data) = 0;
|
||||
|
||||
private:
|
||||
uint8_t _state;
|
||||
};
|
||||
//==============================================================================
|
||||
/**
|
||||
* @class SoftI2cMaster
|
||||
* @brief Software I2C master class
|
||||
*/
|
||||
class SoftI2cMaster : public I2cMasterBase {
|
||||
public:
|
||||
SoftI2cMaster() {}
|
||||
/**
|
||||
* Constructor, initialize SCL/SDA pins and set the bus high.
|
||||
*
|
||||
* @param[in] sdaPin The software SDA pin number.
|
||||
*
|
||||
* @param[in] sclPin The software SCL pin number.
|
||||
*/
|
||||
SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin);
|
||||
/**
|
||||
* Initialize SCL/SDA pins and set the bus high.
|
||||
*
|
||||
* @param[in] sdaPin The software SDA pin number.
|
||||
*
|
||||
* @param[in] sclPin The software SCL pin number.
|
||||
*/
|
||||
void begin(uint8_t sclPin, uint8_t sdaPin);
|
||||
uint8_t read(uint8_t last);
|
||||
void start();
|
||||
void stop(void);
|
||||
bool write(uint8_t b);
|
||||
|
||||
private:
|
||||
uint8_t _sclBit;
|
||||
uint8_t _sdaBit;
|
||||
volatile uint8_t* _sclDDR;
|
||||
volatile uint8_t* _sdaDDR;
|
||||
volatile uint8_t* _sdaInReg;
|
||||
//----------------------------------------------------------------------------
|
||||
bool readSda() {return *_sdaInReg & _sdaBit;}
|
||||
//----------------------------------------------------------------------------
|
||||
void sclDelay(uint8_t n) {_delay_loop_1(n);}
|
||||
//----------------------------------------------------------------------------
|
||||
void writeScl(bool value) {
|
||||
uint8_t s = SREG;
|
||||
noInterrupts();
|
||||
if (value == LOW) {
|
||||
// Pull scl low.
|
||||
*_sclDDR |= _sclBit;
|
||||
} else {
|
||||
// Put scl in high Z input mode.
|
||||
*_sclDDR &= ~_sclBit;
|
||||
}
|
||||
SREG = s;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
void writeSda(bool value) {
|
||||
uint8_t s = SREG;
|
||||
noInterrupts();
|
||||
if (value == LOW) {
|
||||
// Pull sda low.
|
||||
*_sdaDDR |= _sdaBit;
|
||||
} else {
|
||||
// Put sda in high Z input mode.
|
||||
*_sdaDDR &= ~_sdaBit;
|
||||
}
|
||||
SREG = s;
|
||||
}
|
||||
};
|
||||
//==============================================================================
|
||||
// Template based fast software I2C
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* @class FastI2cMaster
|
||||
* @brief Fast software I2C master class.
|
||||
*/
|
||||
template<uint8_t sclPin, uint8_t sdaPin>
|
||||
class FastI2cMaster : public I2cMasterBase {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
FastI2cMaster() {
|
||||
begin();
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Initialize I2C bus pins. */
|
||||
void begin() {
|
||||
fastDigitalWrite(sclPin, LOW);
|
||||
fastDigitalWrite(sdaPin, LOW);
|
||||
|
||||
sclWrite(HIGH);
|
||||
sdaWrite(HIGH);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
uint8_t read(uint8_t last) {
|
||||
uint8_t data = 0;
|
||||
sdaWrite(HIGH);
|
||||
|
||||
readBit(7, &data);
|
||||
readBit(6, &data);
|
||||
readBit(5, &data);
|
||||
readBit(4, &data);
|
||||
readBit(3, &data);
|
||||
readBit(2, &data);
|
||||
readBit(1, &data);
|
||||
readBit(0, &data);
|
||||
|
||||
// send ACK or NACK
|
||||
sdaWrite(last);
|
||||
sclDelay(4);
|
||||
sclWrite(HIGH);
|
||||
sclDelay(6);
|
||||
sclWrite(LOW);
|
||||
sdaWrite(LOW);
|
||||
return data;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
void start() {
|
||||
if (!fastDigitalRead(sdaPin)) {
|
||||
// It's a repeat start.
|
||||
sdaWrite(HIGH);
|
||||
sclDelay(8);
|
||||
sclWrite(HIGH);
|
||||
sclDelay(8);
|
||||
}
|
||||
sdaWrite(LOW);
|
||||
sclDelay(8);
|
||||
sclWrite(LOW);
|
||||
sclDelay(8);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
void stop(void) {
|
||||
sdaWrite(LOW);
|
||||
sclDelay(8);
|
||||
sclWrite(HIGH);
|
||||
sclDelay(8);
|
||||
sdaWrite(HIGH);
|
||||
sclDelay(8);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
bool write(uint8_t data) {
|
||||
// write byte
|
||||
writeBit(7, data);
|
||||
writeBit(6, data);
|
||||
writeBit(5, data);
|
||||
writeBit(4, data);
|
||||
writeBit(3, data);
|
||||
writeBit(2, data);
|
||||
writeBit(1, data);
|
||||
writeBit(0, data);
|
||||
|
||||
// get ACK or NACK
|
||||
sdaWrite(HIGH);
|
||||
|
||||
sclWrite(HIGH);
|
||||
sclDelay(5);
|
||||
bool rtn = fastDigitalRead(sdaPin);
|
||||
sclWrite(LOW);
|
||||
sdaWrite(LOW);
|
||||
return rtn == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
void sclWrite(bool value) {fastPinMode(sclPin, !value);}
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
void sdaWrite(bool value) {fastPinMode(sdaPin, !value);}
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
void readBit(uint8_t bit, uint8_t* data) {
|
||||
sclWrite(HIGH);
|
||||
sclDelay(5);
|
||||
if (fastDigitalRead(sdaPin)) *data |= 1 << bit;
|
||||
sclWrite(LOW);
|
||||
if (bit) sclDelay(6);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
void sclDelay(uint8_t n) {_delay_loop_1(n);}
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
void writeBit(uint8_t bit, uint8_t data) {
|
||||
uint8_t mask = 1 << bit;
|
||||
sdaWrite(data & mask);
|
||||
sclWrite(HIGH);
|
||||
sclDelay(5);
|
||||
sclWrite(LOW);
|
||||
sclDelay(5);
|
||||
}
|
||||
};
|
||||
#endif // SOFT_I2C_MASTER_H
|
||||
/** @} */
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/* Arduino DigitalIO Library
|
||||
* Copyright (C) 2013 by William Greiman
|
||||
*
|
||||
* This file is part of the Arduino DigitalIO Library
|
||||
*
|
||||
* This Library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino DigitalIO Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
* @brief Software SPI.
|
||||
*
|
||||
* @defgroup softSPI Software SPI
|
||||
* @ingroup internals
|
||||
* @details Software SPI Template Class.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef SoftSPI_h
|
||||
#define SoftSPI_h
|
||||
#include "DigitalPin.h"
|
||||
//------------------------------------------------------------------------------
|
||||
/** Nop for timing. */
|
||||
#define nop asm volatile ("nop\n\t")
|
||||
//------------------------------------------------------------------------------
|
||||
/** Pin Mode for MISO is input.*/
|
||||
const bool MISO_MODE = false;
|
||||
/** Pullups disabled for MISO are disabled. */
|
||||
const bool MISO_LEVEL = false;
|
||||
/** Pin Mode for MOSI is output.*/
|
||||
const bool MOSI_MODE = true;
|
||||
/** Pin Mode for SCK is output. */
|
||||
const bool SCK_MODE = true;
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* @class SoftSPI
|
||||
* @brief Fast software SPI.
|
||||
*/
|
||||
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin, uint8_t Mode = 0>
|
||||
class SoftSPI {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Initialize SoftSPI pins. */
|
||||
void begin() {
|
||||
fastPinConfig(MisoPin, MISO_MODE, MISO_LEVEL);
|
||||
fastPinConfig(MosiPin, MOSI_MODE, !MODE_CPHA(Mode));
|
||||
fastPinConfig(SckPin, SCK_MODE, MODE_CPOL(Mode));
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Soft SPI receive byte.
|
||||
* @return Data byte received.
|
||||
*/
|
||||
ALWAYS_INLINE uint8_t receive() {
|
||||
uint8_t data = 0;
|
||||
receiveBit(7, &data);
|
||||
receiveBit(6, &data);
|
||||
receiveBit(5, &data);
|
||||
receiveBit(4, &data);
|
||||
receiveBit(3, &data);
|
||||
receiveBit(2, &data);
|
||||
receiveBit(1, &data);
|
||||
receiveBit(0, &data);
|
||||
return data;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Soft SPI send byte.
|
||||
* @param[in] data Data byte to send.
|
||||
*/
|
||||
ALWAYS_INLINE void send(uint8_t data) {
|
||||
sendBit(7, data);
|
||||
sendBit(6, data);
|
||||
sendBit(5, data);
|
||||
sendBit(4, data);
|
||||
sendBit(3, data);
|
||||
sendBit(2, data);
|
||||
sendBit(1, data);
|
||||
sendBit(0, data);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Soft SPI transfer byte.
|
||||
* @param[in] txData Data byte to send.
|
||||
* @return Data byte received.
|
||||
*/
|
||||
ALWAYS_INLINE uint8_t transfer(uint8_t txData) {
|
||||
uint8_t rxData = 0;
|
||||
transferBit(7, &rxData, txData);
|
||||
transferBit(6, &rxData, txData);
|
||||
transferBit(5, &rxData, txData);
|
||||
transferBit(4, &rxData, txData);
|
||||
transferBit(3, &rxData, txData);
|
||||
transferBit(2, &rxData, txData);
|
||||
transferBit(1, &rxData, txData);
|
||||
transferBit(0, &rxData, txData);
|
||||
return rxData;
|
||||
}
|
||||
|
||||
private:
|
||||
//----------------------------------------------------------------------------
|
||||
ALWAYS_INLINE bool MODE_CPHA(uint8_t mode) {return (mode & 1) != 0;}
|
||||
ALWAYS_INLINE bool MODE_CPOL(uint8_t mode) {return (mode & 2) != 0;}
|
||||
ALWAYS_INLINE void receiveBit(uint8_t bit, uint8_t* data) {
|
||||
if (MODE_CPHA(Mode)) {
|
||||
fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
|
||||
}
|
||||
nop;
|
||||
nop;
|
||||
fastDigitalWrite(SckPin,
|
||||
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
|
||||
if (fastDigitalRead(MisoPin)) *data |= 1 << bit;
|
||||
if (!MODE_CPHA(Mode)) {
|
||||
fastDigitalWrite(SckPin, MODE_CPOL(Mode));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
ALWAYS_INLINE void sendBit(uint8_t bit, uint8_t data) {
|
||||
if (MODE_CPHA(Mode)) {
|
||||
fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
|
||||
}
|
||||
fastDigitalWrite(MosiPin, data & (1 << bit));
|
||||
fastDigitalWrite(SckPin,
|
||||
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
|
||||
nop;
|
||||
nop;
|
||||
if (!MODE_CPHA(Mode)) {
|
||||
fastDigitalWrite(SckPin, MODE_CPOL(Mode));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
ALWAYS_INLINE void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData) {
|
||||
if (MODE_CPHA(Mode)) {
|
||||
fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
|
||||
}
|
||||
fastDigitalWrite(MosiPin, txData & (1 << bit));
|
||||
fastDigitalWrite(SckPin,
|
||||
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
|
||||
if (fastDigitalRead(MisoPin)) *rxData |= 1 << bit;
|
||||
if (!MODE_CPHA(Mode)) {
|
||||
fastDigitalWrite(SckPin, MODE_CPOL(Mode));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
};
|
||||
#endif // SoftSPI_h
|
||||
/** @} */
|
||||
@@ -0,0 +1,126 @@
|
||||
#ifndef ADS7818_h
|
||||
#define ADS7871_h
|
||||
//------------------------------------------------------------------------------
|
||||
/** nop to tune soft SPI timing */
|
||||
#define nop asm volatile ("nop\n\t")
|
||||
//------------------------------------------------------------------------------
|
||||
/** doxygen can't handle __attribute__ very well */
|
||||
#define STATIC_ALWAYS_INLINE static inline __attribute__((always_inline))
|
||||
//------------------------------------------------------------------------------
|
||||
template<uint8_t ClkPin, uint8_t ConvPin, uint8_t DataPin>
|
||||
/** ADS7818 class */
|
||||
class ADS7818 {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Set pin modes and initial levels. */
|
||||
void begin() {
|
||||
fastPinMode(ClkPin, 1);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
fastPinMode(ConvPin, 1);
|
||||
fastDigitalWrite(ConvPin, 1);
|
||||
fastPinMode(DataPin, 0);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Read ADS7818 12-bit ADC
|
||||
* \return 16-bit value read.
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
uint16_t read() {
|
||||
uint16_t v = 0;
|
||||
fastDigitalWrite(ConvPin, 0);
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
fastDigitalWrite(ConvPin, 1);
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
|
||||
readBitFast16(v, 11);
|
||||
readBitFast16(v, 10);
|
||||
readBitFast16(v, 9);
|
||||
readBitFast16(v, 8);
|
||||
readBitFast16(v, 7);
|
||||
readBitFast16(v, 6);
|
||||
readBitFast16(v, 5);
|
||||
readBitFast16(v, 4);
|
||||
readBitFast16(v, 3);
|
||||
readBitFast16(v, 2);
|
||||
readBitFast16(v, 1);
|
||||
readBitFast16(v, 0);
|
||||
return v;
|
||||
}
|
||||
private:
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
void readBitFast16(uint16_t &v, uint8_t b) {
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
if (fastDigitalRead(DataPin)) v |= (1 << b);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
}
|
||||
};
|
||||
#if 0
|
||||
/** initialize I/O ports */
|
||||
STATIC_ALWAYS_INLINE void adcBegin() {
|
||||
fastPinMode(ADC_CLK_PIN, 1);
|
||||
fastDigitalWrite(ADC_CLK_PIN, 0);
|
||||
fastPinMode(ADC_CONV_PIN, 1);
|
||||
fastDigitalWrite(ADC_CONV_PIN, 1);
|
||||
fastPinMode(ADC_DATA_PIN, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** clock for don't care bits
|
||||
*\param[in] delayBefore if true, delay before raising clock*/
|
||||
STATIC_ALWAYS_INLINE void fastDummy(bool first) {
|
||||
if (!first) nop;
|
||||
fastDigitalWrite(ADC_CLK_PIN, 1);
|
||||
nop;
|
||||
fastDigitalWrite(ADC_CLK_PIN, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** read next bit fast as possible
|
||||
* \param[in] v word to receive bit
|
||||
* \param[in] b bit number to be set. v |= (1 << b) if next bit is high.
|
||||
*/
|
||||
STATIC_ALWAYS_INLINE void readBitFast16(uint16_t &v, uint8_t b) {
|
||||
fastDigitalWrite(ADC_CLK_PIN, 1);
|
||||
if (fastDigitalRead(ADC_DATA_PIN)) v |= (1 << b);
|
||||
fastDigitalWrite(ADC_CLK_PIN, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** Read AD7680 16-bit ADC in less than 8 microseconds
|
||||
* cs is chip select pin
|
||||
*/
|
||||
STATIC_ALWAYS_INLINE uint16_t adcRead(bool centered = false) {
|
||||
uint16_t v = 0;
|
||||
fastDigitalWrite(ADC_CONV_PIN, 0);
|
||||
fastDigitalWrite(ADC_CLK_PIN, 1);
|
||||
fastDigitalWrite(ADC_CLK_PIN, 0);
|
||||
fastDigitalWrite(ADC_CONV_PIN, 1);
|
||||
fastDigitalWrite(ADC_CLK_PIN, 1);
|
||||
fastDigitalWrite(ADC_CLK_PIN, 0);
|
||||
// uint16_t v = 0;
|
||||
|
||||
// fastDummy(1);
|
||||
// fastDummy(0);
|
||||
// fastDummy(0);
|
||||
// fastDummy(0);
|
||||
|
||||
// readBitFast16(v, 15);
|
||||
// readBitFast16(v, 14);
|
||||
// readBitFast16(v, 13);
|
||||
// readBitFast16(v, 12);
|
||||
readBitFast16(v, 11);
|
||||
readBitFast16(v, 10);
|
||||
readBitFast16(v, 9);
|
||||
readBitFast16(v, 8);
|
||||
readBitFast16(v, 7);
|
||||
readBitFast16(v, 6);
|
||||
readBitFast16(v, 5);
|
||||
readBitFast16(v, 4);
|
||||
readBitFast16(v, 3);
|
||||
readBitFast16(v, 2);
|
||||
readBitFast16(v, 1);
|
||||
readBitFast16(v, 0);
|
||||
return centered ? v ^ 0X8000 : v;
|
||||
}
|
||||
#endif // 0
|
||||
#endif // ADS7818_h
|
||||
@@ -0,0 +1,262 @@
|
||||
#ifndef MCP320X_h
|
||||
#define MCP320X_h
|
||||
#include <DigitalIO.h>
|
||||
//==============================================================================
|
||||
template<uint8_t CsPin, uint8_t ClkPin, uint8_t DoutPin, uint8_t UsecDelay = 0>
|
||||
/** MCP3201 class */
|
||||
class MCP3201 {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Set pin modes and initial levels. */
|
||||
void begin() {
|
||||
fastPinMode(ClkPin, 1);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
fastPinMode(CsPin, 1);
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
fastPinMode(DoutPin, 0);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Read MCP3201 12-bit ADC
|
||||
* \return 16-bit value read.
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
uint16_t read() {
|
||||
fastDigitalWrite(CsPin, 0);
|
||||
// Start sample.
|
||||
mcpAdcDummy(3);
|
||||
// Extra sample time.
|
||||
if (UsecDelay){
|
||||
delayMicroseconds(UsecDelay);
|
||||
}
|
||||
// End sample.
|
||||
mcpAdcDummy(3);
|
||||
// Null bit.
|
||||
mcpAdcDummy(3);
|
||||
uint16_t v = 0;
|
||||
readBit(v, 11);
|
||||
readBit(v, 10);
|
||||
readBit(v, 9);
|
||||
readBit(v, 8);
|
||||
readBit(v, 7);
|
||||
readBit(v, 6);
|
||||
readBit(v, 5);
|
||||
readBit(v, 4);
|
||||
readBit(v, 3);
|
||||
readBit(v, 2);
|
||||
readBit(v, 1);
|
||||
readBit(v, 0);
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
return v;
|
||||
}
|
||||
private:
|
||||
//----------------------------------------------------------------------------
|
||||
/** delay n nops
|
||||
* \param[in] n nops to delay, must be a constant so compiler optimizes if().
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
void delayCycles(uint8_t n) {
|
||||
if (n & 1) asm volatile("nop\n\t");
|
||||
if (n & 2) asm volatile("nop\n\t" "nop\n\t");
|
||||
if (n & 4) asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** clock for don't care bits
|
||||
*\param[in] delayBefore if true, delay before raising clock*/
|
||||
inline __attribute__((always_inline))
|
||||
void mcpAdcDummy(uint8_t delayBefore) {
|
||||
delayCycles(delayBefore);
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
delayCycles(3);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 2) {
|
||||
delayCycles(3);
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
delayCycles(delayRead);
|
||||
if (fastDigitalRead(DoutPin)) v |= (1 << b);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
}
|
||||
};
|
||||
//==============================================================================
|
||||
template<uint8_t CsPin, uint8_t ClkPin,
|
||||
uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0>
|
||||
/** MCP3202 class */
|
||||
class MCP3202 {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Set pin modes and initial levels. */
|
||||
void begin() {
|
||||
fastPinMode(ClkPin, 1);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
fastPinMode(CsPin, 1);
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
fastPinMode(DoutPin, 0);
|
||||
fastPinMode(DinPin, 1);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Read MCP3202 12-bit ADC
|
||||
* \return 16-bit value read.
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
uint16_t read(uint8_t config) {
|
||||
uint16_t v = 0;
|
||||
fastDigitalWrite(CsPin, 0);
|
||||
// Start bit.
|
||||
writeBit(true);
|
||||
// Mode bit.
|
||||
writeBit(config & 2);
|
||||
// Channel Selection and start sample.
|
||||
writeBit(config & 1);
|
||||
// Extra sample time.
|
||||
if (UsecDelay){
|
||||
delayMicroseconds(UsecDelay);
|
||||
}
|
||||
// MSB first format and end sample.
|
||||
writeBit(true);
|
||||
// Null bit.
|
||||
writeBit(true);
|
||||
// Data bits.
|
||||
readBit(v, 11);
|
||||
readBit(v, 10);
|
||||
readBit(v, 9);
|
||||
readBit(v, 8);
|
||||
readBit(v, 7);
|
||||
readBit(v, 6);
|
||||
readBit(v, 5);
|
||||
readBit(v, 4);
|
||||
readBit(v, 3);
|
||||
readBit(v, 2);
|
||||
readBit(v, 1);
|
||||
readBit(v, 0);
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
return v;
|
||||
}
|
||||
private:
|
||||
//----------------------------------------------------------------------------
|
||||
/** delay n nops
|
||||
* \param[in] n nops to delay, must be a constant so compiler optimizes if().
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
void delayCycles(uint8_t n) {
|
||||
if (n & 1) asm volatile("nop\n\t");
|
||||
if (n & 2) asm volatile("nop\n\t" "nop\n\t");
|
||||
if (n & 4) asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** clock for don't care bits
|
||||
*\param[in] delayBefore if true, delay before raising clock*/
|
||||
inline __attribute__((always_inline))
|
||||
void writeBit(bool value) {
|
||||
fastDigitalWrite(DinPin, value);
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
delayCycles(2);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 1) {
|
||||
delayCycles(2);
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
delayCycles(delayRead);
|
||||
if (fastDigitalRead(DoutPin)) v |= (1 << b);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
}
|
||||
};
|
||||
//==============================================================================
|
||||
template<uint8_t CsPin, uint8_t ClkPin,
|
||||
uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0>
|
||||
/** MCP3204 class */
|
||||
class MCP3204 {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Set pin modes and initial levels. */
|
||||
void begin() {
|
||||
fastPinMode(ClkPin, 1);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
fastPinMode(CsPin, 1);
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
fastPinMode(DoutPin, 0);
|
||||
fastPinMode(DinPin, 1);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Read MCP3204 12-bit ADC
|
||||
* \return 16-bit value read.
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
uint16_t read(uint8_t config) {
|
||||
uint16_t v = 0;
|
||||
fastDigitalWrite(CsPin, 0);
|
||||
// Start bit.
|
||||
writeBit(true);
|
||||
// Mode bit.
|
||||
writeBit(config & 8);
|
||||
// D2 channel bit.
|
||||
writeBit(config & 4);
|
||||
// D1 channel bit.
|
||||
writeBit(config & 2);
|
||||
// D0 channel bit and start sample.
|
||||
writeBit(config & 1);
|
||||
// Extra sample time.
|
||||
if (UsecDelay){
|
||||
delayMicroseconds(UsecDelay);
|
||||
}
|
||||
// End sample cycle.
|
||||
writeBit(true);
|
||||
// Null bit.
|
||||
writeBit(true);
|
||||
// Data bits.
|
||||
readBit(v, 11);
|
||||
readBit(v, 10);
|
||||
readBit(v, 9);
|
||||
readBit(v, 8);
|
||||
readBit(v, 7);
|
||||
readBit(v, 6);
|
||||
readBit(v, 5);
|
||||
readBit(v, 4);
|
||||
readBit(v, 3);
|
||||
readBit(v, 2);
|
||||
readBit(v, 1);
|
||||
readBit(v, 0);
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
return v;
|
||||
}
|
||||
private:
|
||||
//----------------------------------------------------------------------------
|
||||
/** delay n nops
|
||||
* \param[in] n nops to delay, must be a constant so compiler optimizes if().
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
void delayCycles(uint8_t n) {
|
||||
if (n & 1) asm volatile("nop\n\t");
|
||||
if (n & 2) asm volatile("nop\n\t" "nop\n\t");
|
||||
if (n & 4) asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** clock for don't care bits
|
||||
*\param[in] delayBefore if true, delay before raising clock*/
|
||||
inline __attribute__((always_inline))
|
||||
void writeBit(bool value) {
|
||||
fastDigitalWrite(DinPin, value);
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
delayCycles(2);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
void readBit(uint16_t &v, uint8_t b, uint8_t delayRead = 1) {
|
||||
delayCycles(2);
|
||||
fastDigitalWrite(ClkPin, 1);
|
||||
delayCycles(delayRead);
|
||||
if (fastDigitalRead(DoutPin)) v |= (1 << b);
|
||||
fastDigitalWrite(ClkPin, 0);
|
||||
}
|
||||
};
|
||||
//==============================================================================
|
||||
// MCP3204 and MCP3208 use the same code.
|
||||
template<uint8_t CsPin, uint8_t ClkPin,
|
||||
uint8_t DoutPin, uint8_t DinPin, uint8_t UsecDelay = 0>
|
||||
class MCP3208 : public MCP3204<CsPin, ClkPin, DoutPin, DinPin, UsecDelay> {
|
||||
};
|
||||
#endif // MCP320X_h
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef MCP355X_h
|
||||
#define MCP355X_h
|
||||
#include <DigitalIO.h>
|
||||
// Overflow values.
|
||||
const int32_t MCP355X_OVH = 2097152L;
|
||||
const int32_t MCP355X_OVL = -2097153L;
|
||||
const int32_t MCP355X_ERR = 0X80000000;
|
||||
const uint8_t MCP355X_TO_MS = 100;
|
||||
//==============================================================================
|
||||
template<uint8_t CsPin, uint8_t SckPin, uint8_t SdoPin>
|
||||
/** MCP355X class */
|
||||
class MCP355X {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Set pin modes and initial levels. */
|
||||
void begin(bool singleMode = true) {
|
||||
m_singleMode = singleMode;
|
||||
fastPinMode(SdoPin, 0);
|
||||
fastPinMode(SckPin, 1);
|
||||
fastDigitalWrite(SckPin, 1);
|
||||
fastPinMode(CsPin, 1);
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
if (!m_singleMode) {
|
||||
// Wait for conversion to complete.
|
||||
delay(100);
|
||||
// Start continuous conversion mode.
|
||||
fastDigitalWrite(CsPin, 0);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
/** Read MCP355X 22-bit ADC
|
||||
* \return 22-bit value.
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
int32_t read() {
|
||||
uint8_t t = 0;
|
||||
uint8_t v3 = 0;
|
||||
if (m_singleMode) {
|
||||
// Start conversion.
|
||||
fastDigitalWrite(CsPin, 0);
|
||||
// Delay at least 10 usec to avoid RDY glitch on exit from Shutdown.
|
||||
delay(1);
|
||||
// Toggle CsPin to indicate single conversion mode.
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
// Wait for conversion to complete.
|
||||
while (1) {
|
||||
delay(1);
|
||||
fastDigitalWrite(CsPin, 0);
|
||||
// Delay while RDY settles.
|
||||
delayCycles(4);
|
||||
if (!fastDigitalRead(SdoPin)) break;
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
if (t++ > MCP355X_TO_MS) return MCP355X_ERR;
|
||||
}
|
||||
} else {
|
||||
while (1) {
|
||||
if (!fastDigitalRead(SdoPin)) break;
|
||||
delay(1);
|
||||
if (t++ > MCP355X_TO_MS) return MCP355X_ERR;
|
||||
}
|
||||
}
|
||||
uint8_t v2 = readByte();
|
||||
uint8_t v1 = readByte();
|
||||
uint8_t v0 = readByte();
|
||||
|
||||
// The 25th falling edge of SCK changes SDO/RDY from Data mode to RDY mode.
|
||||
uint8_t dummy = 0;
|
||||
readBit(dummy, 0);
|
||||
|
||||
if (m_singleMode) {
|
||||
fastDigitalWrite(CsPin, 1);
|
||||
}
|
||||
if ((v2 & 0XE0) == 0X20) {
|
||||
// Negative in range so extend sign bit.
|
||||
v2 |= 0XC0;
|
||||
v3 = 0XFF;
|
||||
} else if (v2 & 0X40) {
|
||||
// Overflow high. Cause value to be >= MCP355X_OVH.
|
||||
if (v2 & 0X20) v2 &= 0X3F;
|
||||
} else if (v2 & 0X80) {
|
||||
// Overflow low. Cause value to be <= MCP355X_OVH.
|
||||
if ((v2 & 0X20) == 0) v2 |= 0X40;
|
||||
v3 = 0XFF;
|
||||
}
|
||||
uint16_t v_high = (v3 << 8) | v2;
|
||||
uint16_t v_low = (v1 << 8) | v0;
|
||||
return ((uint32_t)v_high << 16) | v_low;
|
||||
}
|
||||
private:
|
||||
//----------------------------------------------------------------------------
|
||||
/** delay n nops
|
||||
* \param[in] n nops to delay, must be a constant so compiler optimizes if().
|
||||
*/
|
||||
inline __attribute__((always_inline))
|
||||
void delayCycles(uint8_t n) {
|
||||
if (n & 1) asm volatile("nop\n\t");
|
||||
if (n & 2) asm volatile("nop\n\t" "nop\n\t");
|
||||
if (n & 4) asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
// Default delay yields about 2 MHz clock.
|
||||
inline __attribute__((always_inline))
|
||||
void readBit(uint8_t &v, uint8_t b, uint8_t delayRead = 0) {
|
||||
fastDigitalWrite(SckPin, 0);
|
||||
delayCycles(2 + delayRead);
|
||||
fastDigitalWrite(SckPin, 1);
|
||||
if (fastDigitalRead(SdoPin)) v |= (1 << b);
|
||||
delayCycles(delayRead);
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
inline __attribute__((always_inline))
|
||||
uint8_t readByte() {
|
||||
uint8_t v = 0;
|
||||
readBit(v, 7);
|
||||
readBit(v, 6);
|
||||
readBit(v, 5);
|
||||
readBit(v, 4);
|
||||
readBit(v, 3);
|
||||
readBit(v, 2);
|
||||
readBit(v, 1);
|
||||
readBit(v, 0);
|
||||
return v;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
bool m_singleMode;
|
||||
};
|
||||
#endif // MCP355X_h
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <DigitalIO.h>
|
||||
// Create object for pin 13 in output mode and demo toggle().
|
||||
DigitalPin<13> pin13(OUTPUT);
|
||||
|
||||
void setup() {}
|
||||
|
||||
void loop() {
|
||||
// toggle is a two byte instruction that executes
|
||||
// in two cycles or 125 ns on a 16 MHz CPU
|
||||
pin13.toggle();
|
||||
delay(250);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Demo of config() and fast toggle() function.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
// Class with compile time pin number.
|
||||
DigitalPin<13> pin13;
|
||||
|
||||
void setup() {
|
||||
// Set mode to OUTPUT and level LOW.
|
||||
pin13.config(OUTPUT, LOW);
|
||||
}
|
||||
void loop() {
|
||||
// toggle is a two byte instruction that executes
|
||||
// in two cycles or 125 ns on a 16 MHz CPU
|
||||
pin13.toggle();
|
||||
delay(100);
|
||||
pin13.toggle();
|
||||
delay(400);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Read pin 12 and write value to pin 13.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
DigitalPin<12> pin12(INPUT);
|
||||
DigitalPin<13> pin13(OUTPUT);
|
||||
|
||||
void setup() {}
|
||||
|
||||
void loop() {
|
||||
pin13 = pin12;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Scope test for fast shiftOut function.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
// Create clockPin in output mode with inital level LOW.
|
||||
DigitalPin<12> clockPin(OUTPUT, LOW);
|
||||
|
||||
// Create dataPin in output mode with inital level HIGH.
|
||||
DigitalPin<13> dataPin(OUTPUT, HIGH);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Time to send one bit is ten cycles or 625 ns for 16 MHz CPU.
|
||||
inline __attribute__((always_inline))
|
||||
void sendBit(uint8_t bit, uint8_t data) {
|
||||
dataPin = data & (1 << bit);
|
||||
clockPin = 1;
|
||||
// may want a nop here - clock pulse is 125 ns wide
|
||||
clockPin = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Time to send one byte is 5 usec.
|
||||
void shiftOut(uint8_t bits) {
|
||||
sendBit(7, bits);
|
||||
sendBit(6, bits);
|
||||
sendBit(5, bits);
|
||||
sendBit(4, bits);
|
||||
sendBit(3, bits);
|
||||
sendBit(2, bits);
|
||||
sendBit(1, bits);
|
||||
sendBit(0, bits);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
// Not used.
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
shiftOut(0X55);
|
||||
delay(2);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Use begin to assign pin numbers.
|
||||
// Read pin 12 and write the value to pin 13.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
// Declare the PinIO instances.
|
||||
PinIO readPin;
|
||||
PinIO writePin;
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
// Assign pin 12 to readPin.
|
||||
readPin.begin(12);
|
||||
|
||||
// input mode with pull-ups disabled
|
||||
readPin.config(INPUT, LOW);
|
||||
|
||||
// Assign pin 13 to writePin and set mode to output.
|
||||
writePin.begin(13);
|
||||
writePin.mode(OUTPUT);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
// Copy the value read from readPin to writePin.
|
||||
writePin.write(readPin.read());
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Test the config() and toggle() functions.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
// Set runtime pin number.
|
||||
PinIO pin13(13);
|
||||
|
||||
void setup() {
|
||||
// set mode to OUTPUT and level LOW
|
||||
pin13.config(OUTPUT, LOW);
|
||||
}
|
||||
void loop() {
|
||||
pin13.toggle();
|
||||
delay(100);
|
||||
pin13.toggle();
|
||||
delay(400);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Read pin 12 and write the value to pin 13.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
PinIO readPin(12);
|
||||
PinIO writePin(13);
|
||||
|
||||
void setup() {
|
||||
// Set input mode and disable pull-up.
|
||||
readPin.config(INPUT, LOW);
|
||||
|
||||
// set output mode
|
||||
writePin.mode(OUTPUT);
|
||||
}
|
||||
void loop() {
|
||||
writePin.write(readPin.read());
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/* Scan I2C bus for devices.
|
||||
*
|
||||
* For I2C write addresses: the scanner sends a start,
|
||||
* the write address, looks for an ACK, then sends a stop.
|
||||
*
|
||||
* For I2C read addresses: the scanner sends a start, the
|
||||
* read address, looks for an ACK. If there is an ACK, it
|
||||
* reads a byte and NACKs it. Finally it sends a stop.
|
||||
*
|
||||
* When the I2C chip responds to the read address, it
|
||||
* outputs data and will miss a stop condition sent
|
||||
* immediately after the read address (bus contention).
|
||||
*
|
||||
* If the I2C chip misses the stop condition, the
|
||||
* address scanner will see ghost addresses until the
|
||||
* read ends randomly. By reading a byte after any read
|
||||
* address that ACKs, there is a chance to NACK the read
|
||||
* and properly end the I2C transaction.
|
||||
*/
|
||||
#include <DigitalIO.h>
|
||||
|
||||
// Set pin numbers for your configuration.
|
||||
const uint8_t SDA_PIN = A4;
|
||||
const uint8_t SCL_PIN = A5;
|
||||
|
||||
SoftI2cMaster i2c(SCL_PIN, SDA_PIN);
|
||||
//FastI2cMaster<SCL_PIN, SDA_PIN> i2c;
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
|
||||
Serial.begin(9600);
|
||||
while (!Serial);
|
||||
|
||||
if (!digitalRead(SDA_PIN) && !digitalRead(SCL_PIN)) {
|
||||
Serial.println("External pull-up resistors appear to be missing.");
|
||||
Serial.println("Many false responses may be detected.");
|
||||
Serial.println("Type any character to continue.");
|
||||
|
||||
while (!Serial.available());
|
||||
Serial.println();
|
||||
|
||||
}
|
||||
uint8_t add = 0;
|
||||
bool found = false;
|
||||
do {
|
||||
bool wr = i2c.transfer(add | I2C_WRITE, 0, 0);
|
||||
bool rd = i2c.transfer(add | I2C_READ, 0, 0, I2C_CONTINUE);
|
||||
if (rd) {
|
||||
uint8_t dummy;
|
||||
// Must read byte, send NACK, and issue STOP.
|
||||
i2c.transferContinue(&dummy, 1);
|
||||
}
|
||||
if (rd || wr) {
|
||||
found = true;
|
||||
Serial.print("Device at address: 0X");
|
||||
Serial.print(add, HEX);
|
||||
Serial.print(" responds to ");
|
||||
if (rd) Serial.print("Read");
|
||||
if (rd && wr) Serial.print(" and ");
|
||||
if (wr) Serial.print("Write");
|
||||
Serial.println('.');
|
||||
}
|
||||
add += 2;
|
||||
}
|
||||
while (add);
|
||||
|
||||
if (!found) {
|
||||
Serial.println("No devices found.");
|
||||
}
|
||||
Serial.println("Done");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
// Not used.
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
// Utility sketch to explore DS1307 and demonstrate software I2C Master.
|
||||
//
|
||||
|
||||
#include <DigitalIO.h>
|
||||
|
||||
// Set pin numbers for your configuration.
|
||||
const uint8_t SDA_PIN = A4;
|
||||
const uint8_t SCL_PIN = A5;
|
||||
|
||||
//SoftI2cMaster rtc(SCL_PIN, SDA_PIN);
|
||||
FastI2cMaster<SCL_PIN, SDA_PIN> rtc;
|
||||
|
||||
// i2c 8-bit address for DS1307. low bit is read/write
|
||||
#define DS1307ADDR 0XD0
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
* Read 'count' bytes from the DS1307 starting at 'address'.
|
||||
*/
|
||||
uint8_t readDS1307(uint8_t address, uint8_t *buf, uint8_t count) {
|
||||
|
||||
// Send address of data.
|
||||
if (!rtc.transfer(DS1307ADDR | I2C_WRITE, &address, 1)) return false;
|
||||
|
||||
// Read data.
|
||||
return rtc.transfer(DS1307ADDR | I2C_READ, buf, count);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
* write 'count' bytes to DS1307 starting at 'address'.
|
||||
*/
|
||||
uint8_t writeDS1307(uint8_t address, uint8_t *buf, uint8_t count) {
|
||||
|
||||
// Write address and continue transfer for write of data.
|
||||
if (!rtc.transfer(DS1307ADDR | I2C_WRITE, &address, 1, I2C_CONTINUE)) {
|
||||
return false;
|
||||
}
|
||||
// Write data.
|
||||
return rtc.transferContinue(buf, count);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
/** Store and print a string in flash memory.*/
|
||||
#define PgmPrint(x) Serial.print(F(x))
|
||||
/** Store and print a string in flash memory followed by a CR/LF.*/
|
||||
#define PgmPrintln(x) Serial.println(F(x))
|
||||
//-------------------------------------------------------------------------------
|
||||
// Day of week U.S. convention.
|
||||
char *Ddd[] = {"Bad", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||
//------------------------------------------------------------------------------
|
||||
void hexPrint(uint8_t v) {
|
||||
Serial.print(v >> 4, HEX);
|
||||
Serial.print(v & 0XF, HEX);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void hexPrintln(uint8_t v) {
|
||||
hexPrint(v);
|
||||
Serial.println();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// read hex input
|
||||
uint8_t hexRead(uint16_t* v) {
|
||||
bool valid = false;
|
||||
uint16_t n = 0;
|
||||
while (!Serial.available());
|
||||
while (Serial.available()) {
|
||||
uint8_t c = Serial.read();
|
||||
// Exit if end-of-line.
|
||||
if (c == '\n' || c == '\r') {
|
||||
delay(10);
|
||||
// If CR/LF.
|
||||
Serial.read();
|
||||
break;
|
||||
}
|
||||
n <<= 4;
|
||||
if ('a' <= c && c <= 'f') {
|
||||
n += c - ('a' - 10);
|
||||
}
|
||||
else if ('A' <= c && c <= 'F') {
|
||||
n += c - ('A' - 10);
|
||||
}
|
||||
else if ('0' <= c && c <= '9') {
|
||||
n += c - '0';
|
||||
}
|
||||
else {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
delay(10);
|
||||
valid = true;
|
||||
}
|
||||
if (!valid) {
|
||||
PgmPrintln("Invalid entry");
|
||||
return false;
|
||||
}
|
||||
*v = n;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
uint8_t bcdRead(uint8_t min, uint8_t max, uint8_t* n) {
|
||||
uint16_t v;
|
||||
if (!hexRead(&v)) return false;
|
||||
uint8_t d = 10 * (v >> 4) + (v & 0XF);
|
||||
if ((v >> 4) > 9 || (v & 0XF) > 9 || d < min || d > max) {
|
||||
PgmPrintln("Invalid");
|
||||
return false;
|
||||
}
|
||||
*n = v;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void displayTime(void) {
|
||||
uint8_t r[8];
|
||||
if (!readDS1307(0, r, 8)) {
|
||||
PgmPrintln("Read Failed for display time");
|
||||
return;
|
||||
}
|
||||
PgmPrint("The current time is 20");
|
||||
// year
|
||||
hexPrint(r[6]);
|
||||
Serial.write('-');
|
||||
|
||||
// month
|
||||
hexPrint(r[5]);
|
||||
Serial.write('-');
|
||||
|
||||
// day
|
||||
hexPrint(r[4]);
|
||||
Serial.write(' ');
|
||||
|
||||
Serial.print(Ddd[r[3] < 8 ? r[3] : 0]);
|
||||
Serial.write(' ');
|
||||
|
||||
// hour
|
||||
hexPrint(r[2]);
|
||||
Serial.write(':');
|
||||
|
||||
// minute
|
||||
hexPrint(r[1]);
|
||||
Serial.write(':');
|
||||
|
||||
// second
|
||||
hexPrintln(r[0]);
|
||||
|
||||
// Control register.
|
||||
PgmPrint("Control Register: ");
|
||||
hexPrintln(r[7]);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// dump registers and 56 bytes of RAM
|
||||
void dumpAll(void) {
|
||||
uint8_t buf[8];
|
||||
for (uint8_t a = 0; a < 64; a += 8) {
|
||||
hexPrint(a);
|
||||
Serial.write(' ');
|
||||
if (!readDS1307(a, buf, 8)) {
|
||||
PgmPrint("read failed for dumpAll");
|
||||
return;
|
||||
}
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
Serial.write(' ');
|
||||
hexPrint(buf[i]);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void fillNvRam(void) {
|
||||
uint8_t buf[8];
|
||||
PgmPrint("Enter HEX value for all NV RAM locations (00-FF): ");
|
||||
uint16_t v;
|
||||
if (!hexRead(&v)) return;
|
||||
hexPrint(v);
|
||||
for (uint8_t a = 8; a < 64; a ++) {
|
||||
if (!writeDS1307(a, (uint8_t *)&v, 1)) {
|
||||
PgmPrintln("write failed for fillNvRam");
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// set control register
|
||||
/*
|
||||
The DS1307 control register is used to control the operation of the SQW/OUT pin.
|
||||
+-----------------------------------------------+
|
||||
|BIT 7|BIT 6|BIT 5|BIT 4|BIT 3|BIT 2|BIT 1|BIT 0|
|
||||
+-----------------------------------------------+
|
||||
|OUT | 0 | 0 |SQWE | 0 | 0 | RS1 | RS0 |
|
||||
+-----------------------------------------------+
|
||||
|
||||
OUT (Output control): This bit controls the output level of the SQW/OUT pin
|
||||
when the square wave output is disabled. If SQWE = 0, the logic level on the
|
||||
SQW/OUT pin is 1 if OUT = 1 and is 0 if OUT = 0.
|
||||
|
||||
SQWE (Square Wave Enable): This bit, when set to a logic 1, will enable the
|
||||
oscillator output. The frequency of the square wave output depends upon the
|
||||
value of the RS0 and RS1 bits. With the square wave output set to 1Hz, the
|
||||
clock registers update on the falling edge of the square wave.
|
||||
|
||||
Square wave Output Frequency for SQWE = 1.
|
||||
RS1 RS0 FREQUENCY
|
||||
0 0 1Hz
|
||||
0 1 4.096kHz
|
||||
1 0 8.192kHz
|
||||
1 1 32.768kHz
|
||||
*/
|
||||
void setControl(void) {
|
||||
PgmPrintln("SQW/OUT pin: ");
|
||||
PgmPrintln("(00) Low");
|
||||
PgmPrintln("(10) 1Hz");
|
||||
PgmPrintln("(11) 4.096kHz");
|
||||
PgmPrintln("(12) 8.192kHz");
|
||||
PgmPrintln("(13) 32.768kHz");
|
||||
PgmPrintln("(80) High");
|
||||
PgmPrint("Enter control: ");
|
||||
uint16_t r;
|
||||
if (!hexRead(&r)) return;
|
||||
hexPrintln(r);
|
||||
if (!writeDS1307(7, (uint8_t *)&r, 1)) {
|
||||
PgmPrint("Write Failed for setControl");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setDate(void) {
|
||||
uint8_t r[4];
|
||||
PgmPrint("Enter year (00-99): ");
|
||||
if (!bcdRead(0, 99, &r[3])) return;
|
||||
hexPrintln(r[3]);
|
||||
PgmPrint("Enter month (01-12): ");
|
||||
if (!bcdRead(1, 12, &r[2])) return;
|
||||
hexPrintln(r[2]);
|
||||
PgmPrint("Enter day (01-31): ");
|
||||
if (!bcdRead(1, 31, &r[1])) return;
|
||||
hexPrintln(r[1]);
|
||||
PgmPrint("Enter day of week (01-07, Sun-Sat): ");
|
||||
if (!bcdRead(1, 7, &r[0])) return;
|
||||
hexPrintln(r[0]);
|
||||
|
||||
if (!writeDS1307(3, r, 4)) {
|
||||
PgmPrintln("Write failed for setDate");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setNvRam() {
|
||||
uint8_t u;
|
||||
uint16_t a, v;
|
||||
while (true) {
|
||||
PgmPrint("Enter zero to quit or Hex address (8-3F):");
|
||||
if (!hexRead(&a) || a == 0) return;
|
||||
if (a < 8 || a > 0X3F) {
|
||||
PgmPrintln("Invalid address");
|
||||
return;
|
||||
}
|
||||
hexPrintln(a);
|
||||
if (!readDS1307(a, &u, 1)) {
|
||||
PgmPrintln("Read failed");
|
||||
return;
|
||||
}
|
||||
PgmPrint("Current value: ");
|
||||
hexPrintln(u);
|
||||
PgmPrint("Enter new HEX value (00-FF): ");
|
||||
if (!hexRead(&v)) return;
|
||||
hexPrintln(v);
|
||||
if (!writeDS1307(a, (uint8_t *)&v, 1)) {
|
||||
PgmPrint("Write Failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setTime(void) {
|
||||
uint8_t r[3];
|
||||
PgmPrint("Enter hours (00-23): ");
|
||||
if (!bcdRead(0, 23, &r[2])) return;
|
||||
hexPrintln(r[2]);
|
||||
PgmPrint("Enter minutes (00-59): ");
|
||||
if (!bcdRead(0, 59, &r[1])) return;
|
||||
hexPrintln(r[1]);
|
||||
PgmPrint("Enter seconds (00-59): ");
|
||||
if (!bcdRead(0, 59, &r[0])) return;
|
||||
hexPrintln(r[0]);
|
||||
|
||||
if (!writeDS1307(0, r, 3)) {
|
||||
PgmPrintln("write failed in setTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {
|
||||
Serial.println();
|
||||
displayTime();
|
||||
while (Serial.read() >= 0) {}
|
||||
PgmPrintln("\nOptions are:");
|
||||
PgmPrintln("(0) Display date and time");
|
||||
PgmPrintln("(1) Set time");
|
||||
PgmPrintln("(2) Set date");
|
||||
PgmPrintln("(3) Set Control");
|
||||
PgmPrintln("(4) Dump all");
|
||||
PgmPrintln("(5) Fill NV RAM");
|
||||
PgmPrintln("(6) Set NV RAM value");
|
||||
PgmPrint("Enter option: ");
|
||||
|
||||
uint16_t n;
|
||||
if (!hexRead(&n)) return;
|
||||
Serial.println(n, DEC);
|
||||
if (n == 0) return;
|
||||
Serial.println();
|
||||
if (n == 1) {
|
||||
setTime();
|
||||
} else if (n == 2) {
|
||||
setDate();
|
||||
} else if (n == 3) {
|
||||
setControl();
|
||||
} else if (n == 4) {
|
||||
dumpAll();
|
||||
} else if (n == 5) {
|
||||
fillNvRam();
|
||||
} else if (n == 6) {
|
||||
setNvRam();
|
||||
} else {
|
||||
PgmPrintln("Invalid option");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Scope test for write timing with Arduino digitaWrite().
|
||||
const uint8_t PIN13 = 13;
|
||||
|
||||
void setup() {
|
||||
// Set mode to OUTPUT.
|
||||
pinMode(PIN13, OUTPUT);
|
||||
}
|
||||
void loop() {
|
||||
digitalWrite(PIN13, HIGH);
|
||||
digitalWrite(PIN13, LOW);
|
||||
digitalWrite(PIN13, HIGH);
|
||||
digitalWrite(PIN13, LOW);
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Scope test for write timing of DigitalPin functions.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
// Class with compile time pin number.
|
||||
DigitalPin<13> pin13;
|
||||
|
||||
void setup() {
|
||||
// set mode to OUTPUT
|
||||
pin13.mode(OUTPUT);
|
||||
}
|
||||
void loop() {
|
||||
pin13.high();
|
||||
pin13.low();
|
||||
pin13.write(1);
|
||||
pin13.write(0);
|
||||
pin13.toggle();
|
||||
pin13.toggle();
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Test fastDigital function timing with a scope.
|
||||
#include <DigitalIO.h>
|
||||
const uint8_t PIN = 13;
|
||||
void setup() {
|
||||
fastPinMode(PIN, OUTPUT);
|
||||
}
|
||||
void loop() {
|
||||
fastDigitalWrite(PIN, HIGH);
|
||||
fastDigitalWrite(PIN, LOW);
|
||||
fastDigitalToggle(PIN);
|
||||
fastDigitalToggle(PIN);
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Scope test for PinIO class write timing.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
// Class with runtime pin numbers.
|
||||
PinIO pin(13);
|
||||
|
||||
void setup() {
|
||||
// Set mode to OUTPUT.
|
||||
pin.mode(OUTPUT);
|
||||
}
|
||||
void loop() {
|
||||
pin.high();
|
||||
pin.low();
|
||||
pin.write(1);
|
||||
pin.write(0);
|
||||
pin.toggle();
|
||||
pin.toggle();
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Scope test for development - assumes 328 processor.
|
||||
#include <DigitalIO.h>
|
||||
|
||||
const uint8_t SOFT_SPI_MISO_PIN = 7;
|
||||
const uint8_t SOFT_SPI_MOSI_PIN = 8;
|
||||
const uint8_t SOFT_SPI_SCK_PIN = 9;
|
||||
const uint8_t SPI_MODE = 0;
|
||||
|
||||
SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, SPI_MODE> spi;
|
||||
|
||||
int test;
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
spi.begin();
|
||||
while(1) {
|
||||
Serial.println("Enter:");
|
||||
Serial.println("R - Receive");
|
||||
Serial.println("S - Send");
|
||||
Serial.println("T - Transfer");
|
||||
while ((test = Serial.read()) <= 0) {}
|
||||
test = toupper(test);
|
||||
if (strchr("RST", test)) break;
|
||||
Serial.println("Invalid entry");
|
||||
};
|
||||
Serial.print("Starting test ");
|
||||
Serial.println((char)test);
|
||||
}
|
||||
void loop() {
|
||||
if (test == 'S') spi.send(0X55);
|
||||
if (test == 'R') {
|
||||
Serial.println(spi.receive(), HEX);
|
||||
}
|
||||
if (test == 'T') {
|
||||
Serial.println(spi.transfer(0XAA), HEX);
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/* An Alternative Software Serial Library
|
||||
* http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
* Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Version 1.2: Support Teensy 3.x
|
||||
//
|
||||
// Version 1.1: Improve performance in receiver code
|
||||
//
|
||||
// Version 1.0: Initial Release
|
||||
|
||||
|
||||
#include "AltSoftSerial.h"
|
||||
#include "config/AltSoftSerial_Boards.h"
|
||||
#include "config/AltSoftSerial_Timers.h"
|
||||
|
||||
/****************************************/
|
||||
/** Initialization **/
|
||||
/****************************************/
|
||||
|
||||
static uint16_t ticks_per_bit=0;
|
||||
bool AltSoftSerial::timing_error=false;
|
||||
|
||||
static uint8_t rx_state;
|
||||
static uint8_t rx_byte;
|
||||
static uint8_t rx_bit = 0;
|
||||
static uint16_t rx_target;
|
||||
static uint16_t rx_stop_ticks=0;
|
||||
static volatile uint8_t rx_buffer_head;
|
||||
static volatile uint8_t rx_buffer_tail;
|
||||
#define RX_BUFFER_SIZE 80
|
||||
static volatile uint8_t rx_buffer[RX_BUFFER_SIZE];
|
||||
|
||||
static volatile uint8_t tx_state=0;
|
||||
static uint8_t tx_byte;
|
||||
static uint8_t tx_bit;
|
||||
static volatile uint8_t tx_buffer_head;
|
||||
static volatile uint8_t tx_buffer_tail;
|
||||
#define TX_BUFFER_SIZE 68
|
||||
static volatile uint8_t tx_buffer[RX_BUFFER_SIZE];
|
||||
|
||||
|
||||
#ifndef INPUT_PULLUP
|
||||
#define INPUT_PULLUP INPUT
|
||||
#endif
|
||||
|
||||
void AltSoftSerial::init(uint32_t cycles_per_bit)
|
||||
{
|
||||
if (cycles_per_bit < 7085) {
|
||||
CONFIG_TIMER_NOPRESCALE();
|
||||
} else {
|
||||
cycles_per_bit /= 8;
|
||||
if (cycles_per_bit < 7085) {
|
||||
CONFIG_TIMER_PRESCALE_8();
|
||||
} else {
|
||||
return; // minimum 283 baud at 16 MHz clock
|
||||
}
|
||||
}
|
||||
ticks_per_bit = cycles_per_bit;
|
||||
rx_stop_ticks = cycles_per_bit * 37 / 4;
|
||||
pinMode(INPUT_CAPTURE_PIN, INPUT_PULLUP);
|
||||
digitalWrite(OUTPUT_COMPARE_A_PIN, HIGH);
|
||||
pinMode(OUTPUT_COMPARE_A_PIN, OUTPUT);
|
||||
rx_state = 0;
|
||||
rx_buffer_head = 0;
|
||||
rx_buffer_tail = 0;
|
||||
tx_state = 0;
|
||||
tx_buffer_head = 0;
|
||||
tx_buffer_tail = 0;
|
||||
ENABLE_INT_INPUT_CAPTURE();
|
||||
}
|
||||
|
||||
void AltSoftSerial::end(void)
|
||||
{
|
||||
DISABLE_INT_COMPARE_B();
|
||||
DISABLE_INT_INPUT_CAPTURE();
|
||||
flushInput();
|
||||
flushOutput();
|
||||
DISABLE_INT_COMPARE_A();
|
||||
// TODO: restore timer to original settings?
|
||||
}
|
||||
|
||||
|
||||
/****************************************/
|
||||
/** Transmission **/
|
||||
/****************************************/
|
||||
|
||||
void AltSoftSerial::writeByte(uint8_t b)
|
||||
{
|
||||
uint8_t intr_state, head;
|
||||
|
||||
head = tx_buffer_head + 1;
|
||||
if (head >= TX_BUFFER_SIZE) head = 0;
|
||||
while (tx_buffer_tail == head) ; // wait until space in buffer
|
||||
intr_state = SREG;
|
||||
cli();
|
||||
if (tx_state) {
|
||||
tx_buffer[head] = b;
|
||||
tx_buffer_head = head;
|
||||
} else {
|
||||
tx_state = 1;
|
||||
tx_byte = b;
|
||||
tx_bit = 0;
|
||||
ENABLE_INT_COMPARE_A();
|
||||
CONFIG_MATCH_CLEAR();
|
||||
SET_COMPARE_A(GET_TIMER_COUNT() + 16);
|
||||
}
|
||||
SREG = intr_state;
|
||||
}
|
||||
|
||||
|
||||
ISR(COMPARE_A_INTERRUPT)
|
||||
{
|
||||
uint8_t state, byte, bit, head, tail;
|
||||
uint16_t target;
|
||||
|
||||
state = tx_state;
|
||||
byte = tx_byte;
|
||||
target = GET_COMPARE_A();
|
||||
while (state < 9) {
|
||||
target += ticks_per_bit;
|
||||
bit = byte & 1;
|
||||
byte >>= 1;
|
||||
state++;
|
||||
if (bit != tx_bit) {
|
||||
if (bit) {
|
||||
CONFIG_MATCH_SET();
|
||||
} else {
|
||||
CONFIG_MATCH_CLEAR();
|
||||
}
|
||||
SET_COMPARE_A(target);
|
||||
tx_bit = bit;
|
||||
tx_byte = byte;
|
||||
tx_state = state;
|
||||
// TODO: how to detect timing_error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (state == 9) {
|
||||
tx_state = 10;
|
||||
CONFIG_MATCH_SET();
|
||||
SET_COMPARE_A(target + ticks_per_bit);
|
||||
return;
|
||||
}
|
||||
head = tx_buffer_head;
|
||||
tail = tx_buffer_tail;
|
||||
if (head == tail) {
|
||||
tx_state = 0;
|
||||
CONFIG_MATCH_NORMAL();
|
||||
DISABLE_INT_COMPARE_A();
|
||||
} else {
|
||||
tx_state = 1;
|
||||
if (++tail >= TX_BUFFER_SIZE) tail = 0;
|
||||
tx_buffer_tail = tail;
|
||||
tx_byte = tx_buffer[tail];
|
||||
tx_bit = 0;
|
||||
CONFIG_MATCH_CLEAR();
|
||||
SET_COMPARE_A(target + ticks_per_bit);
|
||||
// TODO: how to detect timing_error?
|
||||
}
|
||||
}
|
||||
|
||||
void AltSoftSerial::flushOutput(void)
|
||||
{
|
||||
while (tx_state) /* wait */ ;
|
||||
}
|
||||
|
||||
|
||||
/****************************************/
|
||||
/** Reception **/
|
||||
/****************************************/
|
||||
|
||||
|
||||
ISR(CAPTURE_INTERRUPT)
|
||||
{
|
||||
uint8_t state, bit, head;
|
||||
uint16_t capture, target;
|
||||
int16_t offset;
|
||||
|
||||
capture = GET_INPUT_CAPTURE();
|
||||
bit = rx_bit;
|
||||
if (bit) {
|
||||
CONFIG_CAPTURE_FALLING_EDGE();
|
||||
rx_bit = 0;
|
||||
} else {
|
||||
CONFIG_CAPTURE_RISING_EDGE();
|
||||
rx_bit = 0x80;
|
||||
}
|
||||
state = rx_state;
|
||||
if (state == 0) {
|
||||
if (!bit) {
|
||||
SET_COMPARE_B(capture + rx_stop_ticks);
|
||||
ENABLE_INT_COMPARE_B();
|
||||
rx_target = capture + ticks_per_bit + ticks_per_bit/2;
|
||||
rx_state = 1;
|
||||
}
|
||||
} else {
|
||||
target = rx_target;
|
||||
while (1) {
|
||||
offset = capture - target;
|
||||
if (offset < 0) break;
|
||||
rx_byte = (rx_byte >> 1) | rx_bit;
|
||||
target += ticks_per_bit;
|
||||
state++;
|
||||
if (state >= 9) {
|
||||
DISABLE_INT_COMPARE_B();
|
||||
head = rx_buffer_head + 1;
|
||||
if (head >= RX_BUFFER_SIZE) head = 0;
|
||||
if (head != rx_buffer_tail) {
|
||||
rx_buffer[head] = rx_byte;
|
||||
rx_buffer_head = head;
|
||||
}
|
||||
CONFIG_CAPTURE_FALLING_EDGE();
|
||||
rx_bit = 0;
|
||||
rx_state = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
rx_target = target;
|
||||
rx_state = state;
|
||||
}
|
||||
//if (GET_TIMER_COUNT() - capture > ticks_per_bit) AltSoftSerial::timing_error = true;
|
||||
}
|
||||
|
||||
ISR(COMPARE_B_INTERRUPT)
|
||||
{
|
||||
uint8_t head, state, bit;
|
||||
|
||||
DISABLE_INT_COMPARE_B();
|
||||
CONFIG_CAPTURE_FALLING_EDGE();
|
||||
state = rx_state;
|
||||
bit = rx_bit ^ 0x80;
|
||||
while (state < 9) {
|
||||
rx_byte = (rx_byte >> 1) | bit;
|
||||
state++;
|
||||
}
|
||||
head = rx_buffer_head + 1;
|
||||
if (head >= RX_BUFFER_SIZE) head = 0;
|
||||
if (head != rx_buffer_tail) {
|
||||
rx_buffer[head] = rx_byte;
|
||||
rx_buffer_head = head;
|
||||
}
|
||||
rx_state = 0;
|
||||
CONFIG_CAPTURE_FALLING_EDGE();
|
||||
rx_bit = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AltSoftSerial::read(void)
|
||||
{
|
||||
uint8_t head, tail, out;
|
||||
|
||||
head = rx_buffer_head;
|
||||
tail = rx_buffer_tail;
|
||||
if (head == tail) return -1;
|
||||
if (++tail >= RX_BUFFER_SIZE) tail = 0;
|
||||
out = rx_buffer[tail];
|
||||
rx_buffer_tail = tail;
|
||||
return out;
|
||||
}
|
||||
|
||||
int AltSoftSerial::peek(void)
|
||||
{
|
||||
uint8_t head, tail;
|
||||
|
||||
head = rx_buffer_head;
|
||||
tail = rx_buffer_tail;
|
||||
if (head == tail) return -1;
|
||||
return rx_buffer[tail];
|
||||
}
|
||||
|
||||
int AltSoftSerial::available(void)
|
||||
{
|
||||
uint8_t head, tail;
|
||||
|
||||
head = rx_buffer_head;
|
||||
tail = rx_buffer_tail;
|
||||
if (head >= tail) return head - tail;
|
||||
return RX_BUFFER_SIZE + head - tail;
|
||||
}
|
||||
|
||||
void AltSoftSerial::flushInput(void)
|
||||
{
|
||||
rx_buffer_head = rx_buffer_tail;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALTSS_USE_FTM0
|
||||
void ftm0_isr(void)
|
||||
{
|
||||
uint32_t flags = FTM0_STATUS;
|
||||
FTM0_STATUS = 0;
|
||||
if (flags & (1<<5)) altss_capture_interrupt();
|
||||
if (flags & (1<<6)) altss_compare_a_interrupt();
|
||||
if (flags & (1<<0)) altss_compare_b_interrupt();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/* An Alternative Software Serial Library
|
||||
* http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
* Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AltSoftSerial_h
|
||||
#define AltSoftSerial_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#include "pins_arduino.h"
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) && defined(CORE_TEENSY)
|
||||
#define ALTSS_BASE_FREQ F_BUS
|
||||
#else
|
||||
#define ALTSS_BASE_FREQ F_CPU
|
||||
#endif
|
||||
|
||||
/** AltSoftSerial class */
|
||||
class AltSoftSerial : public Stream
|
||||
{
|
||||
public:
|
||||
AltSoftSerial() { } //!< Constructor
|
||||
~AltSoftSerial() { end(); } //!< Destructor
|
||||
static void begin(uint32_t baud) { init((ALTSS_BASE_FREQ + baud / 2) / baud); } //!< begin
|
||||
static void end(); //!< end
|
||||
int peek(); //!< peek
|
||||
int read(); //!< read
|
||||
int available(); //!< available
|
||||
#if ARDUINO >= 100
|
||||
size_t write(uint8_t byte) { writeByte(byte); return 1; } //!< write
|
||||
void flush() { flushOutput(); } //!< flush
|
||||
#else
|
||||
void write(uint8_t byte) { writeByte(byte); } //!< write
|
||||
void flush() { flushInput(); } //!< flush
|
||||
#endif
|
||||
using Print::write;
|
||||
static void flushInput(); //!< flushInput
|
||||
static void flushOutput(); //!< flushOutput
|
||||
// for drop-in compatibility with NewSoftSerial, rxPin & txPin ignored
|
||||
//AltSoftSerial(uint8_t rxPin, uint8_t txPin, bool inverse = false) { }
|
||||
bool listen() { return false; } //!< listen
|
||||
bool isListening() { return true; } //!< isListening
|
||||
bool overflow() { bool r = timing_error; timing_error = false; return r; } //!< overflow
|
||||
static int library_version() { return 1; } //!< library_version
|
||||
static void enable_timer0(bool) { } //!< enable_timer0
|
||||
static bool timing_error; //!< timing_error
|
||||
private:
|
||||
static void init(uint32_t cycles_per_bit);
|
||||
static void writeByte(uint8_t byte);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#AltSoftSerial Library#
|
||||
|
||||
Improved software emulated serial, using hardware timers for precise signal
|
||||
timing and availability of CPU time for other libraries to respond to interrupts
|
||||
during data AltSoftSerial data transmission and reception.
|
||||
|
||||
http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
|
||||

|
||||
@@ -0,0 +1,138 @@
|
||||
/* An Alternative Software Serial Library
|
||||
* http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
* Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
// Teensy 2.0
|
||||
//
|
||||
#if defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
|
||||
|
||||
//#define ALTSS_USE_TIMER1
|
||||
//#define INPUT_CAPTURE_PIN 22 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 14 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 15 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 4 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER3
|
||||
#define INPUT_CAPTURE_PIN 10 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
|
||||
|
||||
|
||||
// Teensy++ 2.0
|
||||
//
|
||||
#elif defined(__AVR_AT90USB1286__) && defined(CORE_TEENSY)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 4 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 25 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 26 // unusable PWM
|
||||
#define OUTPUT_COMPARE_C_PIN 27 // unusable PWM
|
||||
|
||||
//#define ALTSS_USE_TIMER3
|
||||
//#define INPUT_CAPTURE_PIN 17 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 16 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 15 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 14 // unusable PWM
|
||||
|
||||
|
||||
// Teensy 3.0 & 3.1
|
||||
//
|
||||
#elif defined(__MK20DX128__) || defined(__MK20DX256__)
|
||||
#define ALTSS_USE_FTM0
|
||||
#define INPUT_CAPTURE_PIN 20 // receive (FTM0_CH5)
|
||||
#define OUTPUT_COMPARE_A_PIN 21 // transmit (FTM0_CH6)
|
||||
#define OUTPUT_COMPARE_B_PIN 22 // unusable PWM (FTM0_CH0)
|
||||
#define OUTPUT_COMPARE_C_PIN 23 // PWM usable fixed freq
|
||||
#define OUTPUT_COMPARE_D_PIN 5 // PWM usable fixed freq
|
||||
#define OUTPUT_COMPARE_E_PIN 6 // PWM usable fixed freq
|
||||
#define OUTPUT_COMPARE_F_PIN 9 // PWM usable fixed freq
|
||||
#define OUTPUT_COMPARE_G_PIN 10 // PWM usable fixed freq
|
||||
|
||||
|
||||
// Wiring-S
|
||||
//
|
||||
#elif defined(__AVR_ATmega644P__) && defined(WIRING)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 6 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 5 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 4 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// Arduino Uno, Duemilanove, LilyPad, etc
|
||||
//
|
||||
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 8 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 10 // unusable PWM
|
||||
|
||||
|
||||
// Arduino Leonardo & Yun (from Cristian Maglie)
|
||||
//
|
||||
#elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || defined(__AVR_ATmega32U4__)
|
||||
|
||||
//#define ALTSS_USE_TIMER1
|
||||
//#define INPUT_CAPTURE_PIN 4 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 10 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 11 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER3
|
||||
#define INPUT_CAPTURE_PIN 13 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 5 // transmit
|
||||
|
||||
|
||||
// Arduino Mega
|
||||
//
|
||||
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
|
||||
//#define ALTSS_USE_TIMER4
|
||||
//#define INPUT_CAPTURE_PIN 49 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 6 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 7 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 8 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER5
|
||||
#define INPUT_CAPTURE_PIN 48 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 46 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 45 // unusable PWM
|
||||
#define OUTPUT_COMPARE_C_PIN 44 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// Sanguino
|
||||
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 14 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 13 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 12 // unusable PWM
|
||||
|
||||
|
||||
// Unknown board
|
||||
#else
|
||||
#error "Please define your board timer and pins"
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/* An Alternative Software Serial Library
|
||||
* http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
* Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if defined(ALTSS_USE_TIMER1)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK1 = 0, TCCR1A = 0, TCCR1B = (1<<ICNC1) | (1<<CS10))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK1 = 0, TCCR1A = 0, TCCR1B = (1<<ICNC1) | (1<<CS11))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR1A = TCCR1A & ~((1<<COM1A1) | (1<<COM1A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR1A = (TCCR1A & ~(1<<COM1A1)) | (1<<COM1A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR1A = (TCCR1A | (1<<COM1A1)) & ~(1<<COM1A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR1A = TCCR1A | ((1<<COM1A1) | (1<<COM1A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR1B &= ~(1<<ICES1))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR1B |= (1<<ICES1))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR1 = (1<<ICF1), TIMSK1 = (1<<ICIE1))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR1 = (1<<OCF1A), TIMSK1 |= (1<<OCIE1A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR1 = (1<<OCF1B), TIMSK1 |= (1<<OCIE1B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK1 &= ~(1<<ICIE1))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK1 &= ~(1<<OCIE1A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK1 &= ~(1<<OCIE1B))
|
||||
#define GET_TIMER_COUNT() (TCNT1)
|
||||
#define GET_INPUT_CAPTURE() (ICR1)
|
||||
#define GET_COMPARE_A() (OCR1A)
|
||||
#define GET_COMPARE_B() (OCR1B)
|
||||
#define SET_COMPARE_A(val) (OCR1A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR1B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER1_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER1_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER1_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER3)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK3 = 0, TCCR3A = 0, TCCR3B = (1<<ICNC3) | (1<<CS30))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK3 = 0, TCCR3A = 0, TCCR3B = (1<<ICNC3) | (1<<CS31))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR3A = TCCR3A & ~((1<<COM3A1) | (1<<COM3A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR3A = (TCCR3A & ~(1<<COM3A1)) | (1<<COM3A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR3A = (TCCR3A | (1<<COM3A1)) & ~(1<<COM3A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR3A = TCCR3A | ((1<<COM3A1) | (1<<COM3A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR3B &= ~(1<<ICES3))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR3B |= (1<<ICES3))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR3 = (1<<ICF3), TIMSK3 = (1<<ICIE3))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR3 = (1<<OCF3A), TIMSK3 |= (1<<OCIE3A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR3 = (1<<OCF3B), TIMSK3 |= (1<<OCIE3B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK3 &= ~(1<<ICIE3))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK3 &= ~(1<<OCIE3A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK3 &= ~(1<<OCIE3B))
|
||||
#define GET_TIMER_COUNT() (TCNT3)
|
||||
#define GET_INPUT_CAPTURE() (ICR3)
|
||||
#define GET_COMPARE_A() (OCR3A)
|
||||
#define GET_COMPARE_B() (OCR3B)
|
||||
#define SET_COMPARE_A(val) (OCR3A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR3B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER3_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER3_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER3_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER4)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK4 = 0, TCCR4A = 0, TCCR4B = (1<<ICNC4) | (1<<CS40))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK4 = 0, TCCR4A = 0, TCCR4B = (1<<ICNC4) | (1<<CS41))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR4A = TCCR4A & ~((1<<COM4A1) | (1<<COM4A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR4A = (TCCR4A & ~(1<<COM4A1)) | (1<<COM4A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR4A = (TCCR4A | (1<<COM4A1)) & ~(1<<COM4A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR4A = TCCR4A | ((1<<COM4A1) | (1<<COM4A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR4B &= ~(1<<ICES4))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR4B |= (1<<ICES4))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR4 = (1<<ICF4), TIMSK4 = (1<<ICIE4))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR4 = (1<<OCF4A), TIMSK4 |= (1<<OCIE4A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR4 = (1<<OCF4B), TIMSK4 |= (1<<OCIE4B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK4 &= ~(1<<ICIE4))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK4 &= ~(1<<OCIE4A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK4 &= ~(1<<OCIE4B))
|
||||
#define GET_TIMER_COUNT() (TCNT4)
|
||||
#define GET_INPUT_CAPTURE() (ICR4)
|
||||
#define GET_COMPARE_A() (OCR4A)
|
||||
#define GET_COMPARE_B() (OCR4B)
|
||||
#define SET_COMPARE_A(val) (OCR4A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR4B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER4_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER4_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER4_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER5)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK5 = 0, TCCR5A = 0, TCCR5B = (1<<ICNC5) | (1<<CS50))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK5 = 0, TCCR5A = 0, TCCR5B = (1<<ICNC5) | (1<<CS51))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR5A = TCCR5A & ~((1<<COM5A1) | (1<<COM5A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR5A = (TCCR5A & ~(1<<COM5A1)) | (1<<COM5A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR5A = (TCCR5A | (1<<COM5A1)) & ~(1<<COM5A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR5A = TCCR5A | ((1<<COM5A1) | (1<<COM5A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR5B &= ~(1<<ICES5))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR5B |= (1<<ICES5))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR5 = (1<<ICF5), TIMSK5 = (1<<ICIE5))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR5 = (1<<OCF5A), TIMSK5 |= (1<<OCIE5A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR5 = (1<<OCF5B), TIMSK5 |= (1<<OCIE5B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK5 &= ~(1<<ICIE5))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK5 &= ~(1<<OCIE5A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK5 &= ~(1<<OCIE5B))
|
||||
#define GET_TIMER_COUNT() (TCNT5)
|
||||
#define GET_INPUT_CAPTURE() (ICR5)
|
||||
#define GET_COMPARE_A() (OCR5A)
|
||||
#define GET_COMPARE_B() (OCR5B)
|
||||
#define SET_COMPARE_A(val) (OCR5A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR5B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER5_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER5_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER5_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_FTM0)
|
||||
// CH5 = input capture (input, pin 20)
|
||||
// CH6 = compare a (output, pin 21)
|
||||
// CH0 = compare b (input timeout)
|
||||
#define CONFIG_TIMER_NOPRESCALE() FTM0_SC = 0; FTM0_CNT = 0; FTM0_MOD = 0xFFFF; \
|
||||
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); \
|
||||
digitalWriteFast(21, HIGH); \
|
||||
NVIC_SET_PRIORITY(IRQ_FTM0, 48); \
|
||||
NVIC_ENABLE_IRQ(IRQ_FTM0);
|
||||
#define CONFIG_TIMER_PRESCALE_8() FTM0_SC = 0; FTM0_CNT = 0; FTM0_MOD = 0xFFFF; \
|
||||
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(3); \
|
||||
digitalWriteFast(21, HIGH); \
|
||||
NVIC_SET_PRIORITY(IRQ_FTM0, 48); \
|
||||
NVIC_ENABLE_IRQ(IRQ_FTM0);
|
||||
#define CONFIG_MATCH_NORMAL() (FTM0_C6SC = 0)
|
||||
#define CONFIG_MATCH_TOGGLE() (FTM0_C6SC = (FTM0_C6SC & 0xC3) | 0x14)
|
||||
#define CONFIG_MATCH_CLEAR() (FTM0_C6SC = (FTM0_C6SC & 0xC3) | 0x18)
|
||||
#define CONFIG_MATCH_SET() (FTM0_C6SC = (FTM0_C6SC & 0xC3) | 0x1C)
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (FTM0_C5SC = (FTM0_C5SC & 0xC3) | 0x08)
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (FTM0_C5SC = (FTM0_C5SC & 0xC3) | 0x04)
|
||||
#define ENABLE_INT_INPUT_CAPTURE() FTM0_C5SC = 0x48; \
|
||||
CORE_PIN20_CONFIG = PORT_PCR_MUX(4)|PORT_PCR_PE|PORT_PCR_PS
|
||||
#define ENABLE_INT_COMPARE_A() FTM0_C6SC |= 0x40; \
|
||||
CORE_PIN21_CONFIG = PORT_PCR_MUX(4)|PORT_PCR_DSE|PORT_PCR_SRE
|
||||
#define ENABLE_INT_COMPARE_B() (FTM0_C0SC |= 0x40)
|
||||
#define DISABLE_INT_INPUT_CAPTURE() FTM0_C5SC &= ~0x40; \
|
||||
CORE_PIN20_CONFIG = PORT_PCR_MUX(1)|PORT_PCR_PE|PORT_PCR_PS
|
||||
#define DISABLE_INT_COMPARE_A() FTM0_C6SC &= ~0x40; \
|
||||
CORE_PIN21_CONFIG = PORT_PCR_MUX(1)|PORT_PCR_DSE|PORT_PCR_SRE; \
|
||||
digitalWriteFast(21, HIGH)
|
||||
#define DISABLE_INT_COMPARE_B() (FTM0_C0SC &= ~0x40)
|
||||
#define GET_TIMER_COUNT() (FTM0_CNT)
|
||||
#define GET_INPUT_CAPTURE() (FTM0_C5V)
|
||||
#define GET_COMPARE_A() (FTM0_C6V)
|
||||
#define GET_COMPARE_B() (FTM0_C0V)
|
||||
#define SET_COMPARE_A(val) (FTM0_C6V = val)
|
||||
#define SET_COMPARE_B(val) (FTM0_C0V = val)
|
||||
#define CAPTURE_INTERRUPT altss_capture_interrupt
|
||||
#define COMPARE_A_INTERRUPT altss_compare_a_interrupt
|
||||
#define COMPARE_B_INTERRUPT altss_compare_b_interrupt
|
||||
#ifdef ISR
|
||||
#undef ISR
|
||||
#endif
|
||||
#define ISR(f) static void f (void)
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
|
||||
// Teensy 2.0
|
||||
//
|
||||
#if defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
|
||||
|
||||
//#define ALTSS_USE_TIMER1
|
||||
//#define INPUT_CAPTURE_PIN 22 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 14 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 15 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 4 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER3
|
||||
#define INPUT_CAPTURE_PIN 10 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
|
||||
|
||||
|
||||
// Teensy++ 2.0
|
||||
//
|
||||
#elif defined(__AVR_AT90USB1286__) && defined(CORE_TEENSY)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 4 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 25 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 26 // unusable PWM
|
||||
#define OUTPUT_COMPARE_C_PIN 27 // unusable PWM
|
||||
|
||||
//#define ALTSS_USE_TIMER3
|
||||
//#define INPUT_CAPTURE_PIN 17 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 16 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 15 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 14 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// Wiring-S
|
||||
//
|
||||
#elif defined(__AVR_ATmega644P__) && defined(WIRING)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 6 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 5 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 4 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// Arduino Uno, Duemilanove, LilyPad, etc
|
||||
//
|
||||
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 8 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 10 // unusable PWM
|
||||
|
||||
|
||||
// Arduino Leonardo & Yun (from Cristian Maglie)
|
||||
//
|
||||
#elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO)
|
||||
|
||||
//#define ALTSS_USE_TIMER1
|
||||
//#define INPUT_CAPTURE_PIN 4 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 10 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 11 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER3
|
||||
#define INPUT_CAPTURE_PIN 13 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 5 // transmit
|
||||
|
||||
|
||||
// Arduino Mega
|
||||
//
|
||||
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
|
||||
//#define ALTSS_USE_TIMER4
|
||||
//#define INPUT_CAPTURE_PIN 49 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 6 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 7 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 8 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER5
|
||||
#define INPUT_CAPTURE_PIN 48 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 46 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 45 // unusable PWM
|
||||
#define OUTPUT_COMPARE_C_PIN 44 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// Sanguino
|
||||
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 14 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 13 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 12 // unusable PWM
|
||||
|
||||
|
||||
// Unknown board
|
||||
#else
|
||||
#error "Please define your board timer and pins"
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
|
||||
#if defined(ALTSS_USE_TIMER1)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK1 = 0, TCCR1A = 0, TCCR1B = (1<<ICNC1) | (1<<CS10))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK1 = 0, TCCR1A = 0, TCCR1B = (1<<ICNC1) | (1<<CS11))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR1A = TCCR1A & ~((1<<COM1A1) | (1<<COM1A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR1A = (TCCR1A & ~(1<<COM1A1)) | (1<<COM1A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR1A = (TCCR1A | (1<<COM1A1)) & ~(1<<COM1A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR1A = TCCR1A | ((1<<COM1A1) | (1<<COM1A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR1B &= ~(1<<ICES1))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR1B |= (1<<ICES1))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR1 = (1<<ICF1), TIMSK1 = (1<<ICIE1))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR1 = (1<<OCF1A), TIMSK1 |= (1<<OCIE1A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR1 = (1<<OCF1B), TIMSK1 |= (1<<OCIE1B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK1 &= ~(1<<ICIE1))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK1 &= ~(1<<OCIE1A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK1 &= ~(1<<OCIE1B))
|
||||
#define GET_TIMER_COUNT() (TCNT1)
|
||||
#define GET_INPUT_CAPTURE() (ICR1)
|
||||
#define GET_COMPARE_A() (OCR1A)
|
||||
#define GET_COMPARE_B() (OCR1B)
|
||||
#define SET_COMPARE_A(val) (OCR1A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR1B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER1_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER1_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER1_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER3)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK3 = 0, TCCR3A = 0, TCCR3B = (1<<ICNC3) | (1<<CS30))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK3 = 0, TCCR3A = 0, TCCR3B = (1<<ICNC3) | (1<<CS31))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR3A = TCCR3A & ~((1<<COM3A1) | (1<<COM3A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR3A = (TCCR3A & ~(1<<COM3A1)) | (1<<COM3A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR3A = (TCCR3A | (1<<COM3A1)) & ~(1<<COM3A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR3A = TCCR3A | ((1<<COM3A1) | (1<<COM3A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR3B &= ~(1<<ICES3))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR3B |= (1<<ICES3))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR3 = (1<<ICF3), TIMSK3 = (1<<ICIE3))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR3 = (1<<OCF3A), TIMSK3 |= (1<<OCIE3A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR3 = (1<<OCF3B), TIMSK3 |= (1<<OCIE3B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK3 &= ~(1<<ICIE3))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK3 &= ~(1<<OCIE3A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK3 &= ~(1<<OCIE3B))
|
||||
#define GET_TIMER_COUNT() (TCNT3)
|
||||
#define GET_INPUT_CAPTURE() (ICR3)
|
||||
#define GET_COMPARE_A() (OCR3A)
|
||||
#define GET_COMPARE_B() (OCR3B)
|
||||
#define SET_COMPARE_A(val) (OCR3A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR3B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER3_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER3_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER3_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER4)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK4 = 0, TCCR4A = 0, TCCR4B = (1<<ICNC4) | (1<<CS40))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK4 = 0, TCCR4A = 0, TCCR4B = (1<<ICNC4) | (1<<CS41))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR4A = TCCR4A & ~((1<<COM4A1) | (1<<COM4A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR4A = (TCCR4A & ~(1<<COM4A1)) | (1<<COM4A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR4A = (TCCR4A | (1<<COM4A1)) & ~(1<<COM4A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR4A = TCCR4A | ((1<<COM4A1) | (1<<COM4A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR4B &= ~(1<<ICES4))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR4B |= (1<<ICES4))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR4 = (1<<ICF4), TIMSK4 = (1<<ICIE4))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR4 = (1<<OCF4A), TIMSK4 |= (1<<OCIE4A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR4 = (1<<OCF4B), TIMSK4 |= (1<<OCIE4B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK4 &= ~(1<<ICIE4))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK4 &= ~(1<<OCIE4A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK4 &= ~(1<<OCIE4B))
|
||||
#define GET_TIMER_COUNT() (TCNT4)
|
||||
#define GET_INPUT_CAPTURE() (ICR4)
|
||||
#define GET_COMPARE_A() (OCR4A)
|
||||
#define GET_COMPARE_B() (OCR4B)
|
||||
#define SET_COMPARE_A(val) (OCR4A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR4B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER4_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER4_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER4_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER5)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK5 = 0, TCCR5A = 0, TCCR5B = (1<<ICNC5) | (1<<CS50))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK5 = 0, TCCR5A = 0, TCCR5B = (1<<ICNC5) | (1<<CS51))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR5A = TCCR5A & ~((1<<COM5A1) | (1<<COM5A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR5A = (TCCR5A & ~(1<<COM5A1)) | (1<<COM5A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR5A = (TCCR5A | (1<<COM5A1)) & ~(1<<COM5A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR5A = TCCR5A | ((1<<COM5A1) | (1<<COM5A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR5B &= ~(1<<ICES5))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR5B |= (1<<ICES5))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR5 = (1<<ICF5), TIMSK5 = (1<<ICIE5))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR5 = (1<<OCF5A), TIMSK5 |= (1<<OCIE5A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR5 = (1<<OCF5B), TIMSK5 |= (1<<OCIE5B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK5 &= ~(1<<ICIE5))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK5 &= ~(1<<OCIE5A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK5 &= ~(1<<OCIE5B))
|
||||
#define GET_TIMER_COUNT() (TCNT5)
|
||||
#define GET_INPUT_CAPTURE() (ICR5)
|
||||
#define GET_COMPARE_A() (OCR5A)
|
||||
#define GET_COMPARE_B() (OCR5B)
|
||||
#define SET_COMPARE_A(val) (OCR5A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR5B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER5_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER5_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER5_COMPB_vect
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <AltSoftSerial.h>
|
||||
|
||||
// AltSoftSerial always uses these pins:
|
||||
//
|
||||
// Board Transmit Receive PWM Unusable
|
||||
// ----- -------- ------- ------------
|
||||
// Teensy 3.0 & 3.1 21 20 22
|
||||
// Teensy 2.0 9 10 (none)
|
||||
// Teensy++ 2.0 25 4 26, 27
|
||||
// Arduino Uno 9 8 10
|
||||
// Arduino Leonardo 5 13 (none)
|
||||
// Arduino Mega 46 48 44, 45
|
||||
// Wiring-S 5 6 4
|
||||
// Sanguino 13 14 12
|
||||
|
||||
AltSoftSerial altSerial;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // wait for Arduino Serial Monitor to open
|
||||
Serial.println("AltSoftSerial Test Begin");
|
||||
altSerial.begin(9600);
|
||||
altSerial.println("Hello World");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
char c;
|
||||
|
||||
if (Serial.available()) {
|
||||
c = Serial.read();
|
||||
altSerial.print(c);
|
||||
}
|
||||
if (altSerial.available()) {
|
||||
c = altSerial.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
AltSoftSerial KEYWORD1
|
||||
active KEYWORD2
|
||||
overflow KEYWORD2
|
||||
library_version KEYWORD2
|
||||
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
PubSubClient.cpp - A simple client for MQTT.
|
||||
Nick O'Leary
|
||||
http://knolleary.net
|
||||
*/
|
||||
|
||||
#include "PubSubClient.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
PubSubClient::PubSubClient() {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
this->_client = NULL;
|
||||
this->stream = NULL;
|
||||
setCallback(NULL);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr, port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr, port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip, port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip, port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id) {
|
||||
return connect(id,NULL,NULL,0,0,0,0);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
|
||||
return connect(id,user,pass,0,0,0,0);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
|
||||
return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage);
|
||||
}
|
||||
|
||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
|
||||
if (!connected()) {
|
||||
int result = 0;
|
||||
|
||||
if (domain != NULL) {
|
||||
result = _client->connect(this->domain, this->port);
|
||||
} else {
|
||||
result = _client->connect(this->ip, this->port);
|
||||
}
|
||||
if (result == 1) {
|
||||
nextMsgId = 1;
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = 5;
|
||||
unsigned int j;
|
||||
|
||||
#if MQTT_VERSION == MQTT_VERSION_3_1
|
||||
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
|
||||
#define MQTT_HEADER_VERSION_LENGTH 9
|
||||
#elif MQTT_VERSION == MQTT_VERSION_3_1_1
|
||||
uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
|
||||
#define MQTT_HEADER_VERSION_LENGTH 7
|
||||
#endif
|
||||
for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
|
||||
buffer[length++] = d[j];
|
||||
}
|
||||
|
||||
uint8_t v;
|
||||
if (willTopic) {
|
||||
v = 0x06|(willQos<<3)|(willRetain<<5);
|
||||
} else {
|
||||
v = 0x02;
|
||||
}
|
||||
|
||||
if(user != NULL) {
|
||||
v = v|0x80;
|
||||
|
||||
if(pass != NULL) {
|
||||
v = v|(0x80>>1);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[length++] = v;
|
||||
|
||||
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
|
||||
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
|
||||
length = writeString(id,buffer,length);
|
||||
if (willTopic) {
|
||||
length = writeString(willTopic,buffer,length);
|
||||
length = writeString(willMessage,buffer,length);
|
||||
}
|
||||
|
||||
if(user != NULL) {
|
||||
length = writeString(user,buffer,length);
|
||||
if(pass != NULL) {
|
||||
length = writeString(pass,buffer,length);
|
||||
}
|
||||
}
|
||||
|
||||
write(MQTTCONNECT,buffer,length-5);
|
||||
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
|
||||
while (!_client->available()) {
|
||||
unsigned long t = millis();
|
||||
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
|
||||
_state = MQTT_CONNECTION_TIMEOUT;
|
||||
_client->stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint8_t llen;
|
||||
uint16_t len = readPacket(&llen);
|
||||
|
||||
if (len == 4) {
|
||||
if (buffer[3] == 0) {
|
||||
lastInActivity = millis();
|
||||
pingOutstanding = false;
|
||||
_state = MQTT_CONNECTED;
|
||||
return true;
|
||||
} else {
|
||||
_state = buffer[3];
|
||||
}
|
||||
}
|
||||
_client->stop();
|
||||
} else {
|
||||
_state = MQTT_CONNECT_FAILED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// reads a byte into result
|
||||
boolean PubSubClient::readByte(uint8_t * result) {
|
||||
uint32_t previousMillis = millis();
|
||||
while(!_client->available()) {
|
||||
uint32_t currentMillis = millis();
|
||||
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*result = _client->read();
|
||||
return true;
|
||||
}
|
||||
|
||||
// reads a byte into result[*index] and increments index
|
||||
boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
|
||||
uint16_t current_index = *index;
|
||||
uint8_t * write_address = &(result[current_index]);
|
||||
if(readByte(write_address)){
|
||||
*index = current_index + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
|
||||
uint16_t len = 0;
|
||||
if(!readByte(buffer, &len)) return 0;
|
||||
bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
|
||||
uint32_t multiplier = 1;
|
||||
uint16_t length = 0;
|
||||
uint8_t digit = 0;
|
||||
uint16_t skip = 0;
|
||||
uint8_t start = 0;
|
||||
|
||||
do {
|
||||
if(!readByte(&digit)) return 0;
|
||||
buffer[len++] = digit;
|
||||
length += (digit & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((digit & 128) != 0);
|
||||
*lengthLength = len-1;
|
||||
|
||||
if (isPublish) {
|
||||
// Read in topic length to calculate bytes to skip over for Stream writing
|
||||
if(!readByte(buffer, &len)) return 0;
|
||||
if(!readByte(buffer, &len)) return 0;
|
||||
skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
|
||||
start = 2;
|
||||
if (buffer[0]&MQTTQOS1) {
|
||||
// skip message id
|
||||
skip += 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = start;i<length;i++) {
|
||||
if(!readByte(&digit)) return 0;
|
||||
if (this->stream) {
|
||||
if (isPublish && len-*lengthLength-2>skip) {
|
||||
this->stream->write(digit);
|
||||
}
|
||||
}
|
||||
if (len < MQTT_MAX_PACKET_SIZE) {
|
||||
buffer[len] = digit;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
|
||||
len = 0; // This will cause the packet to be ignored.
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
boolean PubSubClient::loop() {
|
||||
if (connected()) {
|
||||
unsigned long t = millis();
|
||||
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
|
||||
if (pingOutstanding) {
|
||||
this->_state = MQTT_CONNECTION_TIMEOUT;
|
||||
_client->stop();
|
||||
return false;
|
||||
} else {
|
||||
buffer[0] = MQTTPINGREQ;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
lastOutActivity = t;
|
||||
lastInActivity = t;
|
||||
pingOutstanding = true;
|
||||
}
|
||||
}
|
||||
if (_client->available()) {
|
||||
uint8_t llen;
|
||||
uint16_t len = readPacket(&llen);
|
||||
uint16_t msgId = 0;
|
||||
uint8_t *payload;
|
||||
if (len > 0) {
|
||||
lastInActivity = t;
|
||||
uint8_t type = buffer[0]&0xF0;
|
||||
if (type == MQTTPUBLISH) {
|
||||
if (callback) {
|
||||
uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2];
|
||||
char topic[tl+1];
|
||||
for (uint16_t i=0;i<tl;i++) {
|
||||
topic[i] = buffer[llen+3+i];
|
||||
}
|
||||
topic[tl] = 0;
|
||||
// msgId only present for QOS>0
|
||||
if ((buffer[0]&0x06) == MQTTQOS1) {
|
||||
msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
|
||||
payload = buffer+llen+3+tl+2;
|
||||
callback(topic,payload,len-llen-3-tl-2);
|
||||
|
||||
buffer[0] = MQTTPUBACK;
|
||||
buffer[1] = 2;
|
||||
buffer[2] = (msgId >> 8);
|
||||
buffer[3] = (msgId & 0xFF);
|
||||
_client->write(buffer,4);
|
||||
lastOutActivity = t;
|
||||
|
||||
} else {
|
||||
payload = buffer+llen+3+tl;
|
||||
callback(topic,payload,len-llen-3-tl);
|
||||
}
|
||||
}
|
||||
} else if (type == MQTTPINGREQ) {
|
||||
buffer[0] = MQTTPINGRESP;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
} else if (type == MQTTPINGRESP) {
|
||||
pingOutstanding = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const char* payload) {
|
||||
return publish(topic,(const uint8_t*)payload,strlen(payload),false);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
|
||||
return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
|
||||
return publish(topic, payload, plength, false);
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
|
||||
if (connected()) {
|
||||
if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = 5;
|
||||
length = writeString(topic,buffer,length);
|
||||
uint16_t i;
|
||||
for (i=0;i<plength;i++) {
|
||||
buffer[length++] = payload[i];
|
||||
}
|
||||
uint8_t header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
return write(header,buffer,length-5);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
|
||||
uint8_t llen = 0;
|
||||
uint8_t digit;
|
||||
unsigned int rc = 0;
|
||||
uint16_t tlen;
|
||||
unsigned int pos = 0;
|
||||
unsigned int i;
|
||||
uint8_t header;
|
||||
unsigned int len;
|
||||
|
||||
if (!connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tlen = strlen(topic);
|
||||
|
||||
header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
buffer[pos++] = header;
|
||||
len = plength + 2 + tlen;
|
||||
do {
|
||||
digit = len % 128;
|
||||
len = len / 128;
|
||||
if (len > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
buffer[pos++] = digit;
|
||||
llen++;
|
||||
} while(len>0);
|
||||
|
||||
pos = writeString(topic,buffer,pos);
|
||||
|
||||
rc += _client->write(buffer,pos);
|
||||
|
||||
for (i=0;i<plength;i++) {
|
||||
rc += _client->write((char)pgm_read_byte_near(payload + i));
|
||||
}
|
||||
|
||||
lastOutActivity = millis();
|
||||
|
||||
return rc == tlen + 4 + plength;
|
||||
}
|
||||
|
||||
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
|
||||
uint8_t lenBuf[4];
|
||||
uint8_t llen = 0;
|
||||
uint8_t digit;
|
||||
uint8_t pos = 0;
|
||||
uint16_t rc;
|
||||
uint16_t len = length;
|
||||
do {
|
||||
digit = len % 128;
|
||||
len = len / 128;
|
||||
if (len > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
lenBuf[pos++] = digit;
|
||||
llen++;
|
||||
} while(len>0);
|
||||
|
||||
buf[4-llen] = header;
|
||||
for (int i=0;i<llen;i++) {
|
||||
buf[5-llen+i] = lenBuf[i];
|
||||
}
|
||||
|
||||
#ifdef MQTT_MAX_TRANSFER_SIZE
|
||||
uint8_t* writeBuf = buf+(4-llen);
|
||||
uint16_t bytesRemaining = length+1+llen; //Match the length type
|
||||
uint8_t bytesToWrite;
|
||||
boolean result = true;
|
||||
while((bytesRemaining > 0) && result) {
|
||||
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
|
||||
rc = _client->write(writeBuf,bytesToWrite);
|
||||
result = (rc == bytesToWrite);
|
||||
bytesRemaining -= rc;
|
||||
writeBuf += rc;
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
rc = _client->write(buf+(4-llen),length+1+llen);
|
||||
lastOutActivity = millis();
|
||||
return (rc == 1+llen+length);
|
||||
#endif
|
||||
}
|
||||
|
||||
boolean PubSubClient::subscribe(const char* topic) {
|
||||
return subscribe(topic, 0);
|
||||
}
|
||||
|
||||
boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
|
||||
// original: if (qos < 0 || qos > 1) { (qos is uint8_t, hence qos < 0 impossible, tekka)
|
||||
if (qos > 1) {
|
||||
return false;
|
||||
}
|
||||
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
if (connected()) {
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = 5;
|
||||
nextMsgId++;
|
||||
if (nextMsgId == 0) {
|
||||
nextMsgId = 1;
|
||||
}
|
||||
buffer[length++] = (nextMsgId >> 8);
|
||||
buffer[length++] = (nextMsgId & 0xFF);
|
||||
length = writeString((char*)topic, buffer,length);
|
||||
buffer[length++] = qos;
|
||||
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean PubSubClient::unsubscribe(const char* topic) {
|
||||
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
if (connected()) {
|
||||
uint16_t length = 5;
|
||||
nextMsgId++;
|
||||
if (nextMsgId == 0) {
|
||||
nextMsgId = 1;
|
||||
}
|
||||
buffer[length++] = (nextMsgId >> 8);
|
||||
buffer[length++] = (nextMsgId & 0xFF);
|
||||
length = writeString(topic, buffer,length);
|
||||
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PubSubClient::disconnect() {
|
||||
buffer[0] = MQTTDISCONNECT;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
_state = MQTT_DISCONNECTED;
|
||||
_client->stop();
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
}
|
||||
|
||||
uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
|
||||
const char* idp = string;
|
||||
uint16_t i = 0;
|
||||
pos += 2;
|
||||
while (*idp) {
|
||||
buf[pos++] = *idp++;
|
||||
i++;
|
||||
}
|
||||
buf[pos-i-2] = (i >> 8);
|
||||
buf[pos-i-1] = (i & 0xFF);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
boolean PubSubClient::connected() {
|
||||
boolean rc;
|
||||
if (_client == NULL ) {
|
||||
rc = false;
|
||||
} else {
|
||||
rc = (int)_client->connected();
|
||||
if (!rc) {
|
||||
if (this->_state == MQTT_CONNECTED) {
|
||||
this->_state = MQTT_CONNECTION_LOST;
|
||||
_client->flush();
|
||||
_client->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
|
||||
IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
|
||||
return setServer(addr,port);
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
|
||||
this->ip = ip;
|
||||
this->port = port;
|
||||
this->domain = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
|
||||
this->domain = domain;
|
||||
this->port = port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
|
||||
this->callback = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setClient(Client& client){
|
||||
this->_client = &client;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setStream(Stream& stream){
|
||||
this->stream = &stream;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int PubSubClient::state() {
|
||||
return this->_state;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
PubSubClient.h - A simple client for MQTT.
|
||||
Nick O'Leary
|
||||
http://knolleary.net
|
||||
*/
|
||||
|
||||
#ifndef PubSubClient_h
|
||||
#define PubSubClient_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "IPAddress.h"
|
||||
#include "Client.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#define MQTT_VERSION_3_1 3
|
||||
#define MQTT_VERSION_3_1_1 4
|
||||
|
||||
// MQTT_VERSION : Pick the version
|
||||
//#define MQTT_VERSION MQTT_VERSION_3_1
|
||||
#ifndef MQTT_VERSION
|
||||
#define MQTT_VERSION MQTT_VERSION_3_1_1
|
||||
#endif
|
||||
|
||||
// MQTT_MAX_PACKET_SIZE : Maximum packet size
|
||||
#ifndef MQTT_MAX_PACKET_SIZE
|
||||
#define MQTT_MAX_PACKET_SIZE 128
|
||||
#endif
|
||||
|
||||
// MQTT_KEEPALIVE : keepAlive interval in Seconds
|
||||
#ifndef MQTT_KEEPALIVE
|
||||
#define MQTT_KEEPALIVE 15
|
||||
#endif
|
||||
|
||||
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
|
||||
#ifndef MQTT_SOCKET_TIMEOUT
|
||||
#define MQTT_SOCKET_TIMEOUT 15
|
||||
#endif
|
||||
|
||||
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
|
||||
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
|
||||
// pass the entire MQTT packet in each write call.
|
||||
//#define MQTT_MAX_TRANSFER_SIZE 80
|
||||
|
||||
// Possible values for client.state()
|
||||
#define MQTT_CONNECTION_TIMEOUT -4
|
||||
#define MQTT_CONNECTION_LOST -3
|
||||
#define MQTT_CONNECT_FAILED -2
|
||||
#define MQTT_DISCONNECTED -1
|
||||
#define MQTT_CONNECTED 0
|
||||
#define MQTT_CONNECT_BAD_PROTOCOL 1
|
||||
#define MQTT_CONNECT_BAD_CLIENT_ID 2
|
||||
#define MQTT_CONNECT_UNAVAILABLE 3
|
||||
#define MQTT_CONNECT_BAD_CREDENTIALS 4
|
||||
#define MQTT_CONNECT_UNAUTHORIZED 5
|
||||
|
||||
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
|
||||
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
|
||||
#define MQTTPUBLISH 3 << 4 // Publish message
|
||||
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
|
||||
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
|
||||
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
|
||||
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
|
||||
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
|
||||
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
|
||||
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
|
||||
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
|
||||
#define MQTTPINGREQ 12 << 4 // PING Request
|
||||
#define MQTTPINGRESP 13 << 4 // PING Response
|
||||
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
|
||||
#define MQTTReserved 15 << 4 // Reserved
|
||||
|
||||
#define MQTTQOS0 (0 << 1)
|
||||
#define MQTTQOS1 (1 << 1)
|
||||
#define MQTTQOS2 (2 << 1)
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <functional>
|
||||
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
||||
#else
|
||||
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
||||
#endif
|
||||
|
||||
/** PubSubClient class */
|
||||
class PubSubClient {
|
||||
private:
|
||||
Client* _client;
|
||||
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
|
||||
uint16_t nextMsgId;
|
||||
unsigned long lastOutActivity;
|
||||
unsigned long lastInActivity;
|
||||
bool pingOutstanding;
|
||||
MQTT_CALLBACK_SIGNATURE;
|
||||
uint16_t readPacket(uint8_t*);
|
||||
boolean readByte(uint8_t * result);
|
||||
boolean readByte(uint8_t * result, uint16_t * index);
|
||||
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
|
||||
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
|
||||
IPAddress ip;
|
||||
const char* domain;
|
||||
uint16_t port;
|
||||
Stream* stream;
|
||||
int _state;
|
||||
public:
|
||||
PubSubClient(); //!< PubSubClient
|
||||
PubSubClient(Client& client); //!< PubSubClient
|
||||
PubSubClient(IPAddress, uint16_t, Client& client); //!< PubSubClient
|
||||
PubSubClient(IPAddress, uint16_t, Client& client, Stream&); //!< PubSubClient
|
||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); //!< PubSubClient
|
||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); //!< PubSubClient
|
||||
PubSubClient(uint8_t *, uint16_t, Client& client); //!< PubSubClient
|
||||
PubSubClient(uint8_t *, uint16_t, Client& client, Stream&); //!< PubSubClient
|
||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); //!< PubSubClient
|
||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); //!< PubSubClient
|
||||
PubSubClient(const char*, uint16_t, Client& client); //!< PubSubClient
|
||||
PubSubClient(const char*, uint16_t, Client& client, Stream&); //!< PubSubClient
|
||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); //!< PubSubClient
|
||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); //!< PubSubClient
|
||||
|
||||
PubSubClient& setServer(IPAddress ip, uint16_t port); //!< setServer
|
||||
PubSubClient& setServer(uint8_t * ip, uint16_t port); //!< setServer
|
||||
PubSubClient& setServer(const char * domain, uint16_t port); //!< setServer
|
||||
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE); //!< setCallback
|
||||
PubSubClient& setClient(Client& client); //!< setClient
|
||||
PubSubClient& setStream(Stream& stream); //!< setStream
|
||||
|
||||
boolean connect(const char* id); //!< connect
|
||||
boolean connect(const char* id, const char* user, const char* pass); //!< connect
|
||||
boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); //!< connect
|
||||
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); //!< connect
|
||||
void disconnect(); //!< disconnect
|
||||
boolean publish(const char* topic, const char* payload); //!< publish
|
||||
boolean publish(const char* topic, const char* payload, boolean retained); //!< publish
|
||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); //!< publish
|
||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); //!< publish
|
||||
boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); //!< publish_P
|
||||
boolean subscribe(const char* topic); //!< subscribe
|
||||
boolean subscribe(const char* topic, uint8_t qos); //!< subscribe
|
||||
boolean unsubscribe(const char* topic); //!< unsubscribe
|
||||
boolean loop(); //!< loop
|
||||
boolean connected(); //!< connected
|
||||
int state(); //!< state
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2015 Sensnology AB
|
||||
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
|
||||
*
|
||||
* Documentation: http://www.mysensors.org
|
||||
* Support Forum: http://forum.mysensors.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* Based on maniacbug's RF24 library, copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
|
||||
* RF24 driver refactored and optimized for speed and size, copyright (C) 2016 Olivier Mauti <olivier@mysensors.org>
|
||||
*/
|
||||
|
||||
#include "RF24.h"
|
||||
|
||||
LOCAL uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID };
|
||||
LOCAL uint8_t MY_RF24_NODE_ADDRESS = AUTO;
|
||||
|
||||
LOCAL void RF24_csn(bool level) {
|
||||
digitalWrite(MY_RF24_CS_PIN, level);
|
||||
}
|
||||
|
||||
LOCAL void RF24_ce(bool level) {
|
||||
digitalWrite(MY_RF24_CE_PIN, level);
|
||||
}
|
||||
|
||||
LOCAL uint8_t RF24_spiMultiByteTransfer(uint8_t cmd, uint8_t* buf, uint8_t len, bool aReadMode) {
|
||||
uint8_t* current = buf;
|
||||
#if !defined(MY_SOFTSPI)
|
||||
_SPI.beginTransaction(SPISettings(MY_RF24_SPI_MAX_SPEED, MY_RF24_SPI_DATA_ORDER, MY_RF24_SPI_DATA_MODE));
|
||||
#endif
|
||||
RF24_csn(LOW);
|
||||
// timing
|
||||
delayMicroseconds(10);
|
||||
uint8_t status = _SPI.transfer( cmd );
|
||||
while ( len-- ) {
|
||||
if (aReadMode) {
|
||||
status = _SPI.transfer( NOP );
|
||||
if(buf != NULL) *current++ = status;
|
||||
} else status = _SPI.transfer(*current++);
|
||||
}
|
||||
RF24_csn(HIGH);
|
||||
#if !defined(MY_SOFTSPI)
|
||||
_SPI.endTransaction();
|
||||
#endif
|
||||
// timing
|
||||
delayMicroseconds(10);
|
||||
return status;
|
||||
}
|
||||
|
||||
LOCAL uint8_t RF24_spiByteTransfer(uint8_t cmd) {
|
||||
return RF24_spiMultiByteTransfer( cmd, NULL, 0, false);
|
||||
}
|
||||
|
||||
LOCAL uint8_t RF24_RAW_readByteRegister(uint8_t cmd) {
|
||||
uint8_t value = RF24_spiMultiByteTransfer( cmd, NULL, 1, true);
|
||||
RF24_DEBUG(PSTR("RF24:read register, reg=%d, value=%d\n"), cmd & (R_REGISTER ^ 0xFF), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
LOCAL uint8_t RF24_RAW_writeByteRegister(uint8_t cmd, uint8_t value) {
|
||||
RF24_DEBUG(PSTR("RF24:write register, reg=%d, value=%d\n"), cmd & (W_REGISTER ^ 0xFF), value);
|
||||
return RF24_spiMultiByteTransfer( cmd , &value, 1, false);
|
||||
}
|
||||
|
||||
LOCAL void RF24_flushRX(void) {
|
||||
RF24_DEBUG(PSTR("RF24:flushRX\n"));
|
||||
RF24_spiByteTransfer( FLUSH_RX );
|
||||
}
|
||||
|
||||
LOCAL void RF24_flushTX(void) {
|
||||
RF24_DEBUG(PSTR("RF24:flushTX\n"));
|
||||
RF24_spiByteTransfer( FLUSH_TX );
|
||||
}
|
||||
|
||||
LOCAL uint8_t RF24_getStatus(void) {
|
||||
return RF24_spiByteTransfer( NOP );
|
||||
}
|
||||
|
||||
LOCAL void RF24_setChannel(uint8_t channel) {
|
||||
RF24_writeByteRegister(RF_CH, channel);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setRetries(uint8_t retransmitDelay, uint8_t retransmitCount) {
|
||||
RF24_writeByteRegister(SETUP_RETR, retransmitDelay << ARD | retransmitCount << ARC);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setAddressWidth(uint8_t width) {
|
||||
RF24_writeByteRegister(SETUP_AW, width - 2);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setRFSetup(uint8_t RFsetup) {
|
||||
RF24_writeByteRegister(RF_SETUP, RFsetup);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setFeature(uint8_t feature) {
|
||||
RF24_writeByteRegister(FEATURE, feature);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setPipe(uint8_t pipe) {
|
||||
RF24_writeByteRegister(EN_RXADDR, pipe);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setAutoACK(uint8_t pipe) {
|
||||
RF24_writeByteRegister(EN_AA, pipe);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setDynamicPayload(uint8_t pipe) {
|
||||
RF24_writeByteRegister(DYNPD, pipe);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setRFConfiguration(uint8_t configuration) {
|
||||
RF24_writeByteRegister(NRF_CONFIG, configuration);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setPipeAddress(uint8_t pipe, uint8_t* address, uint8_t width) {
|
||||
RF24_writeMultiByteRegister(pipe, address, width);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setPipeLSB(uint8_t pipe, uint8_t LSB) {
|
||||
RF24_writeByteRegister(pipe, LSB);
|
||||
}
|
||||
|
||||
LOCAL void RF24_setStatus(uint8_t status) {
|
||||
RF24_writeByteRegister(RF24_STATUS, status);
|
||||
}
|
||||
LOCAL void RF24_enableFeatures(void) {
|
||||
RF24_writeByteRegister(ACTIVATE, 0x73);
|
||||
}
|
||||
|
||||
LOCAL void RF24_openWritingPipe(uint8_t recipient) {
|
||||
RF24_DEBUG(PSTR("RF24:open writing pipe, recipient=%d\n"), recipient);
|
||||
// only write LSB of RX0 and TX pipe
|
||||
RF24_setPipeLSB(RX_ADDR_P0, recipient);
|
||||
RF24_setPipeLSB(TX_ADDR, recipient);
|
||||
}
|
||||
|
||||
LOCAL void RF24_startListening(void) {
|
||||
RF24_DEBUG(PSTR("RF24:start listening\n"));
|
||||
// toggle PRX
|
||||
RF24_setRFConfiguration(MY_RF24_CONFIGURATION | _BV(PWR_UP) | _BV(PRIM_RX) );
|
||||
// all RX pipe addresses must be unique, therefore skip if node ID is 0xFF
|
||||
if(MY_RF24_NODE_ADDRESS!=AUTO) RF24_setPipeLSB(RX_ADDR_P0, MY_RF24_NODE_ADDRESS);
|
||||
// start listening
|
||||
RF24_ce(HIGH);
|
||||
}
|
||||
|
||||
LOCAL void RF24_stopListening(void) {
|
||||
RF24_DEBUG(PSTR("RF24:stop listening\n"));
|
||||
RF24_ce(LOW);
|
||||
// timing
|
||||
delayMicroseconds(130);
|
||||
RF24_setRFConfiguration(MY_RF24_CONFIGURATION | _BV(PWR_UP) );
|
||||
// timing
|
||||
delayMicroseconds(100);
|
||||
}
|
||||
|
||||
LOCAL void RF24_powerDown(void) {
|
||||
RF24_ce(LOW);
|
||||
RF24_setRFConfiguration(MY_RF24_CONFIGURATION);
|
||||
RF24_DEBUG(PSTR("RF24:power down\n"));
|
||||
}
|
||||
|
||||
LOCAL bool RF24_sendMessage( uint8_t recipient, const void* buf, uint8_t len ) {
|
||||
uint8_t status;
|
||||
|
||||
RF24_stopListening();
|
||||
RF24_openWritingPipe( recipient );
|
||||
RF24_DEBUG(PSTR("RF24:send message to %d, len=%d\n"),recipient,len);
|
||||
// flush TX FIFO
|
||||
RF24_flushTX();
|
||||
// this command is affected in clones (e.g. Si24R1): flipped NoACK bit when using W_TX_PAYLOAD_NO_ACK / W_TX_PAYLOAD
|
||||
// RF24_spiMultiByteTransfer( recipient==BROADCAST_ADDRESS ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD, (uint8_t*)buf, len, false );
|
||||
// we are safe by disabling AutoACK on the broadcasting pipe
|
||||
RF24_spiMultiByteTransfer( W_TX_PAYLOAD, (uint8_t*)buf, len, false );
|
||||
// go, TX starts after ~10us
|
||||
RF24_ce(HIGH);
|
||||
// timeout counter to detect HW issues
|
||||
uint16_t timeout = 0xFFFF;
|
||||
do {
|
||||
status = RF24_getStatus();
|
||||
} while (!(status & ( _BV(MAX_RT) | _BV(TX_DS) )) && timeout--);
|
||||
// timeout value after successful TX on 16Mhz AVR ~ 65500, i.e. msg is transmitted after ~36 loop cycles
|
||||
RF24_ce(LOW);
|
||||
// reset interrupts
|
||||
RF24_setStatus(_BV(TX_DS) | _BV(MAX_RT) );
|
||||
// Max retries exceeded
|
||||
if( status & _BV(MAX_RT)){
|
||||
// flush packet
|
||||
RF24_DEBUG(PSTR("RF24:MAX_RT\n"));
|
||||
RF24_flushTX();
|
||||
}
|
||||
|
||||
RF24_startListening();
|
||||
// true if message sent and not timeout
|
||||
return (status & _BV(TX_DS) && timeout);
|
||||
}
|
||||
|
||||
LOCAL uint8_t RF24_getDynamicPayloadSize(void) {
|
||||
uint8_t result = RF24_spiMultiByteTransfer(R_RX_PL_WID, NULL, 1, true);
|
||||
// check if payload size invalid
|
||||
if(result > 32) {
|
||||
RF24_DEBUG(PSTR("RF24:invalid payload length = %d\n"),result);
|
||||
RF24_flushRX();
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
LOCAL bool RF24_isDataAvailable() {
|
||||
uint8_t status = RF24_getStatus();
|
||||
return (((status >> RX_P_NO) & 0b0111) <= 5);
|
||||
}
|
||||
|
||||
|
||||
LOCAL uint8_t RF24_readMessage( void* buf) {
|
||||
uint8_t len = RF24_getDynamicPayloadSize();
|
||||
RF24_DEBUG(PSTR("RF24:read message, len=%d\n"), len);
|
||||
RF24_spiMultiByteTransfer( R_RX_PAYLOAD , (uint8_t*)buf, len, true );
|
||||
// clear RX interrupt
|
||||
RF24_setStatus(_BV(RX_DR) );
|
||||
return len;
|
||||
}
|
||||
|
||||
LOCAL void RF24_setNodeAddress(uint8_t address) {
|
||||
if(address!=AUTO){
|
||||
MY_RF24_NODE_ADDRESS = address;
|
||||
// enable node pipe
|
||||
RF24_setPipe(_BV(ERX_P0 + BROADCAST_PIPE) | _BV(ERX_P0) );
|
||||
// enable autoACK on pipe 0
|
||||
RF24_setAutoACK(_BV(ENAA_P0) );
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL uint8_t RF24_getNodeID(void) {
|
||||
return MY_RF24_NODE_ADDRESS;
|
||||
}
|
||||
|
||||
LOCAL bool RF24_sanityCheck(void) {
|
||||
// detect HW defect ot interrupted SPI line, CE disconnect cannot be detected
|
||||
bool status = RF24_readByteRegister(RF_SETUP) == MY_RF24_RF_SETUP;
|
||||
status &= RF24_readByteRegister(RF_CH) == MY_RF24_CHANNEL;
|
||||
return status;
|
||||
}
|
||||
|
||||
LOCAL bool RF24_initialize(void) {
|
||||
// Initialize pins
|
||||
pinMode(MY_RF24_CE_PIN,OUTPUT);
|
||||
pinMode(MY_RF24_CS_PIN,OUTPUT);
|
||||
// Initialize SPI
|
||||
_SPI.begin();
|
||||
RF24_ce(LOW);
|
||||
RF24_csn(HIGH);
|
||||
// CRC and power up
|
||||
RF24_setRFConfiguration(MY_RF24_CONFIGURATION | _BV(PWR_UP) ) ;
|
||||
// settle >2ms
|
||||
delay(5);
|
||||
// set address width
|
||||
RF24_setAddressWidth(MY_RF24_ADDR_WIDTH);
|
||||
// auto retransmit delay 1500us, auto retransmit count 15
|
||||
RF24_setRetries(RF24_ARD, RF24_ARC);
|
||||
// set channel
|
||||
RF24_setChannel(MY_RF24_CHANNEL);
|
||||
// set data rate and pa level
|
||||
RF24_setRFSetup(MY_RF24_RF_SETUP);
|
||||
// sanity check
|
||||
#if defined(MY_RF24_SANITY_CHECK)
|
||||
if (!RF24_sanityCheck()) {
|
||||
RF24_DEBUG(PSTR("RF24:Sanity check failed: configuration mismatch! Check wiring, replace module or non-P version\n"));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
// toggle features (necessary on some clones)
|
||||
RF24_enableFeatures();
|
||||
// enable ACK payload and dynamic payload
|
||||
RF24_setFeature(MY_RF24_FEATURE);
|
||||
// enable broadcasting pipe
|
||||
RF24_setPipe(_BV(ERX_P0 + BROADCAST_PIPE));
|
||||
// disable AA on all pipes, activate when node pipe set
|
||||
RF24_setAutoACK(0x00);
|
||||
// enable dynamic payloads on used pipes
|
||||
RF24_setDynamicPayload(_BV(DPL_P0 + BROADCAST_PIPE) | _BV(DPL_P0));
|
||||
// listen to broadcast pipe
|
||||
MY_RF24_BASE_ADDR[0] = BROADCAST_ADDRESS;
|
||||
RF24_setPipeAddress(RX_ADDR_P0 + BROADCAST_PIPE, (uint8_t*)&MY_RF24_BASE_ADDR, BROADCAST_PIPE > 1 ? 1 : MY_RF24_ADDR_WIDTH);
|
||||
// pipe 0, set full address, later only LSB is updated
|
||||
RF24_setPipeAddress(RX_ADDR_P0, (uint8_t*)&MY_RF24_BASE_ADDR, MY_RF24_ADDR_WIDTH);
|
||||
RF24_setPipeAddress(TX_ADDR, (uint8_t*)&MY_RF24_BASE_ADDR, MY_RF24_ADDR_WIDTH);
|
||||
// reset FIFO
|
||||
RF24_flushRX();
|
||||
RF24_flushTX();
|
||||
// reset interrupts
|
||||
RF24_setStatus(_BV(TX_DS) | _BV(MAX_RT) | _BV(RX_DR));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2015 Sensnology AB
|
||||
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
|
||||
*
|
||||
* Documentation: http://www.mysensors.org
|
||||
* Support Forum: http://forum.mysensors.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* Based on maniacbug's RF24 library, copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
|
||||
* RF24 driver refactored and optimized for speed and size, copyright (C) 2016 Olivier Mauti <olivier@mysensors.org>
|
||||
*/
|
||||
|
||||
#ifndef __RF24_H__
|
||||
#define __RF24_H__
|
||||
|
||||
#define LOCAL static
|
||||
|
||||
// SPI settings
|
||||
#if !defined(MY_RF24_SPI_MAX_SPEED)
|
||||
// default 2Mhz - safe for nRF24L01+ clones
|
||||
#define MY_RF24_SPI_MAX_SPEED 2000000
|
||||
#endif
|
||||
#define MY_RF24_SPI_DATA_ORDER MSBFIRST
|
||||
#define MY_RF24_SPI_DATA_MODE SPI_MODE0
|
||||
|
||||
#if defined (ARDUINO) && !defined (__arm__) && !defined (_SPI)
|
||||
#if defined(MY_SOFTSPI)
|
||||
SoftSPI<MY_SOFT_SPI_MISO_PIN, MY_SOFT_SPI_MOSI_PIN, MY_SOFT_SPI_SCK_PIN, MY_RF24_SPI_DATA_MODE> _SPI;
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#include <SPI.h>
|
||||
#define _SPI SPI1
|
||||
#else
|
||||
#include <SPI.h>
|
||||
#define _SPI SPI
|
||||
#endif
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define _BV(x) (1<<(x))
|
||||
|
||||
#if defined(__arm__)
|
||||
#include <SPI.h>
|
||||
#else
|
||||
extern HardwareSPI SPI;
|
||||
#endif
|
||||
|
||||
#if !defined(_SPI)
|
||||
#define _SPI SPI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// RF24 settings
|
||||
#define MY_RF24_CONFIGURATION (uint8_t) (RF24_CRC_16 << 2)
|
||||
#define MY_RF24_FEATURE (uint8_t)( _BV(EN_DPL) | _BV(EN_ACK_PAY) )
|
||||
#define MY_RF24_RF_SETUP (uint8_t)( ((MY_RF24_DATARATE & 0b10 ) << 4) | ((MY_RF24_DATARATE & 0b01 ) << 3) | (MY_RF24_PA_LEVEL << 1) ) + 1 // +1 for Si24R1
|
||||
|
||||
// pipes
|
||||
#define BROADCAST_PIPE 1
|
||||
#define NODE_PIPE 0
|
||||
|
||||
// debug
|
||||
#if defined(MY_DEBUG_VERBOSE_RF24)
|
||||
#define RF24_DEBUG(x,...) debug(x, ##__VA_ARGS__)
|
||||
#else
|
||||
#define RF24_DEBUG(x,...)
|
||||
#endif
|
||||
|
||||
// PA levels
|
||||
#define RF24_PA_MIN 0
|
||||
#define RF24_PA_LOW 1
|
||||
#define RF24_PA_HIGH 2
|
||||
#define RF24_PA_MAX 3
|
||||
|
||||
// data rate
|
||||
#define RF24_1MBPS 0
|
||||
#define RF24_2MBPS 1
|
||||
#define RF24_250KBPS 2
|
||||
|
||||
// CRC
|
||||
#define RF24_CRC_DISABLED 0
|
||||
#define RF24_CRC_8 2
|
||||
#define RF24_CRC_16 3
|
||||
|
||||
// ARD, auto retry delay
|
||||
#define RF24_ARD 5 //=1500us
|
||||
|
||||
// ARD, auto retry count
|
||||
#define RF24_ARC 15
|
||||
|
||||
// nRF24L01(+) register definitions
|
||||
#define NRF_CONFIG 0x00
|
||||
#define EN_AA 0x01
|
||||
#define EN_RXADDR 0x02
|
||||
#define SETUP_AW 0x03
|
||||
#define SETUP_RETR 0x04
|
||||
#define RF_CH 0x05
|
||||
#define RF_SETUP 0x06
|
||||
#define RF24_STATUS 0x07
|
||||
#define OBSERVE_TX 0x08
|
||||
#define CD 0x09
|
||||
#define RX_ADDR_P0 0x0A
|
||||
#define RX_ADDR_P1 0x0B
|
||||
#define RX_ADDR_P2 0x0C
|
||||
#define RX_ADDR_P3 0x0D
|
||||
#define RX_ADDR_P4 0x0E
|
||||
#define RX_ADDR_P5 0x0F
|
||||
#define TX_ADDR 0x10
|
||||
#define RX_PW_P0 0x11
|
||||
#define RX_PW_P1 0x12
|
||||
#define RX_PW_P2 0x13
|
||||
#define RX_PW_P3 0x14
|
||||
#define RX_PW_P4 0x15
|
||||
#define RX_PW_P5 0x16
|
||||
#define FIFO_STATUS 0x17
|
||||
#define DYNPD 0x1C
|
||||
#define FEATURE 0x1D
|
||||
|
||||
// instructions
|
||||
#define R_REGISTER 0x00
|
||||
#define RPD 0x09
|
||||
#define W_REGISTER 0x20
|
||||
#define REGISTER_MASK 0x1F
|
||||
#define ACTIVATE 0x50
|
||||
#define R_RX_PL_WID 0x60
|
||||
#define R_RX_PAYLOAD 0x61
|
||||
#define W_TX_PAYLOAD 0xA0
|
||||
#define W_ACK_PAYLOAD 0xA8
|
||||
#define W_TX_PAYLOAD_NO_ACK 0xB0
|
||||
#define FLUSH_TX 0xE1
|
||||
#define FLUSH_RX 0xE2
|
||||
#define REUSE_TX_PL 0xE3
|
||||
#define NOP 0xFF
|
||||
|
||||
// bit mnemonics
|
||||
#define MASK_RX_DR 6
|
||||
#define MASK_TX_DS 5
|
||||
#define MASK_MAX_RT 4
|
||||
#define EN_CRC 3
|
||||
#define CRCO 2
|
||||
#define PWR_UP 1
|
||||
#define PRIM_RX 0
|
||||
|
||||
// auto ACK
|
||||
#define ENAA_P5 5
|
||||
#define ENAA_P4 4
|
||||
#define ENAA_P3 3
|
||||
#define ENAA_P2 2
|
||||
#define ENAA_P1 1
|
||||
#define ENAA_P0 0
|
||||
|
||||
// rx pipe
|
||||
#define ERX_P5 5
|
||||
#define ERX_P4 4
|
||||
#define ERX_P3 3
|
||||
#define ERX_P2 2
|
||||
#define ERX_P1 1
|
||||
#define ERX_P0 0
|
||||
|
||||
// dynamic payload
|
||||
#define DPL_P5 5
|
||||
#define DPL_P4 4
|
||||
#define DPL_P3 3
|
||||
#define DPL_P2 2
|
||||
#define DPL_P1 1
|
||||
#define DPL_P0 0
|
||||
|
||||
#define AW 0
|
||||
#define ARD 4
|
||||
#define ARC 0
|
||||
#define PLL_LOCK 4
|
||||
#define RF_DR 3
|
||||
#define RF_PWR 6
|
||||
#define RX_DR 6
|
||||
#define TX_DS 5
|
||||
#define MAX_RT 4
|
||||
#define RX_P_NO 1
|
||||
#define TX_FULL 0
|
||||
#define PLOS_CNT 4
|
||||
#define ARC_CNT 0
|
||||
#define TX_REUSE 6
|
||||
#define FIFO_FULL 5
|
||||
#define TX_EMPTY 4
|
||||
#define RX_FULL 1
|
||||
#define RX_EMPTY 0
|
||||
|
||||
// features
|
||||
#define EN_DPL 2
|
||||
#define EN_ACK_PAY 1
|
||||
#define EN_DYN_ACK 0
|
||||
|
||||
#define LNA_HCURR 0
|
||||
#define RF_DR_LOW 5
|
||||
#define RF_DR_HIGH 3
|
||||
#define RF_PWR_LOW 1
|
||||
#define RF_PWR_HIGH 2
|
||||
|
||||
// functions
|
||||
|
||||
LOCAL void RF24_csn(bool level);
|
||||
LOCAL void RF24_ce(bool level);
|
||||
LOCAL uint8_t RF24_spiMultiByteTransfer(uint8_t cmd, uint8_t* buf, uint8_t len, bool aReadMode);
|
||||
LOCAL uint8_t RF24_spiByteTransfer(uint8_t cmd);
|
||||
LOCAL uint8_t RF24_RAW_readByteRegister(uint8_t cmd);
|
||||
LOCAL uint8_t RF24_RAW_writeByteRegister(uint8_t cmd, uint8_t value);
|
||||
|
||||
#define RF24_readByteRegister(reg) RF24_RAW_readByteRegister( R_REGISTER | ( REGISTER_MASK & (reg) ) )
|
||||
#define RF24_writeByteRegister(reg, value) RF24_RAW_writeByteRegister( W_REGISTER | ( REGISTER_MASK & (reg) ), value )
|
||||
#define RF24_writeMultiByteRegister(reg, buf, len) RF24_spiMultiByteTransfer( W_REGISTER | ( REGISTER_MASK & (reg) ), (uint8_t*)buf, len, false )
|
||||
|
||||
LOCAL void RF24_flushRX(void);
|
||||
LOCAL void RF24_flushTX(void);
|
||||
LOCAL uint8_t RF24_getStatus(void);
|
||||
LOCAL void RF24_openWritingPipe(uint8_t recipient);
|
||||
LOCAL void RF24_startListening(void);
|
||||
LOCAL void RF24_stopListening(void);
|
||||
LOCAL void RF24_powerDown(void);
|
||||
LOCAL bool RF24_sendMessage(uint8_t recipient, const void* buf, uint8_t len);
|
||||
LOCAL uint8_t RF24_getDynamicPayloadSize(void);
|
||||
LOCAL bool RF24_isDataAvailable();
|
||||
LOCAL uint8_t RF24_readMessage(void* buf);
|
||||
LOCAL void RF24_setNodeAddress(uint8_t address);
|
||||
LOCAL uint8_t RF24_getNodeID(void);
|
||||
LOCAL bool RF24_sanityCheck(void);
|
||||
LOCAL bool RF24_initialize(void);
|
||||
LOCAL void RF24_setChannel(uint8_t channel);
|
||||
LOCAL void RF24_setRetries(uint8_t retransmitDelay, uint8_t retransmitCount);
|
||||
LOCAL void RF24_setAddressWidth(uint8_t width);
|
||||
LOCAL void RF24_setRFSetup(uint8_t RFsetup);
|
||||
LOCAL void RF24_setFeature(uint8_t feature);
|
||||
LOCAL void RF24_setPipe(uint8_t pipe);
|
||||
LOCAL void RF24_setAutoACK(uint8_t pipe);
|
||||
LOCAL void RF24_setDynamicPayload(uint8_t pipe);
|
||||
LOCAL void RF24_setRFConfiguration(uint8_t configuration);
|
||||
LOCAL void RF24_setPipeAddress(uint8_t pipe, uint8_t* address, uint8_t width);
|
||||
LOCAL void RF24_setPipeLSB(uint8_t pipe, uint8_t LSB);
|
||||
LOCAL void RF24_setStatus(uint8_t status);
|
||||
LOCAL void RF24_enableFeatures(void);
|
||||
|
||||
#endif // __RF24_H__
|
||||
@@ -0,0 +1,805 @@
|
||||
// **********************************************************************************
|
||||
// Driver definition for HopeRF RFM69W/RFM69HW/RFM69CW/RFM69HCW, Semtech SX1231/1231H
|
||||
// **********************************************************************************
|
||||
// Copyright Felix Rusu (2014), felix@lowpowerlab.com
|
||||
// http://lowpowerlab.com/
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
// **********************************************************************************
|
||||
#include "RFM69.h"
|
||||
#include "RFM69registers.h"
|
||||
#include <SPI.h>
|
||||
|
||||
volatile uint8_t RFM69::DATA[RF69_MAX_DATA_LEN];
|
||||
volatile uint8_t RFM69::_mode; // current transceiver state
|
||||
volatile uint8_t RFM69::DATALEN;
|
||||
volatile uint8_t RFM69::SENDERID;
|
||||
volatile uint8_t RFM69::TARGETID; // should match _address
|
||||
volatile uint8_t RFM69::PAYLOADLEN;
|
||||
volatile uint8_t RFM69::ACK_REQUESTED;
|
||||
volatile uint8_t RFM69::ACK_RECEIVED; // should be polled immediately after sending a packet with ACK request
|
||||
volatile int16_t RFM69::RSSI; // most accurate RSSI during reception (closest to the reception)
|
||||
RFM69* RFM69::selfPointer;
|
||||
|
||||
bool RFM69::initialize(uint8_t freqBand, uint8_t nodeID, uint8_t networkID)
|
||||
{
|
||||
const uint8_t CONFIG[][2] =
|
||||
{
|
||||
/* 0x01 */ { REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_LISTEN_OFF | RF_OPMODE_STANDBY },
|
||||
/* 0x02 */ { REG_DATAMODUL, RF_DATAMODUL_DATAMODE_PACKET | RF_DATAMODUL_MODULATIONTYPE_FSK | RF_DATAMODUL_MODULATIONSHAPING_00 }, // no shaping
|
||||
/* 0x03 */ { REG_BITRATEMSB, RF_BITRATEMSB_55555}, // default: 4.8 KBPS
|
||||
/* 0x04 */ { REG_BITRATELSB, RF_BITRATELSB_55555},
|
||||
/* 0x05 */ { REG_FDEVMSB, RF_FDEVMSB_50000}, // default: 5KHz, (FDEV + BitRate / 2 <= 500KHz)
|
||||
/* 0x06 */ { REG_FDEVLSB, RF_FDEVLSB_50000},
|
||||
|
||||
/* 0x07 */ { REG_FRFMSB, (uint8_t) (freqBand==RF69_315MHZ ? RF_FRFMSB_315 : (freqBand==RF69_433MHZ ? RF_FRFMSB_433 : (freqBand==RF69_868MHZ ? RF_FRFMSB_868 : RF_FRFMSB_915))) },
|
||||
/* 0x08 */ { REG_FRFMID, (uint8_t) (freqBand==RF69_315MHZ ? RF_FRFMID_315 : (freqBand==RF69_433MHZ ? RF_FRFMID_433 : (freqBand==RF69_868MHZ ? RF_FRFMID_868 : RF_FRFMID_915))) },
|
||||
/* 0x09 */ { REG_FRFLSB, (uint8_t) (freqBand==RF69_315MHZ ? RF_FRFLSB_315 : (freqBand==RF69_433MHZ ? RF_FRFLSB_433 : (freqBand==RF69_868MHZ ? RF_FRFLSB_868 : RF_FRFLSB_915))) },
|
||||
|
||||
// looks like PA1 and PA2 are not implemented on RFM69W, hence the max output power is 13dBm
|
||||
// +17dBm and +20dBm are possible on RFM69HW
|
||||
// +13dBm formula: Pout = -18 + OutputPower (with PA0 or PA1**)
|
||||
// +17dBm formula: Pout = -14 + OutputPower (with PA1 and PA2)**
|
||||
// +20dBm formula: Pout = -11 + OutputPower (with PA1 and PA2)** and high power PA settings (section 3.3.7 in datasheet)
|
||||
///* 0x11 */ { REG_PALEVEL, RF_PALEVEL_PA0_ON | RF_PALEVEL_PA1_OFF | RF_PALEVEL_PA2_OFF | RF_PALEVEL_OUTPUTPOWER_11111},
|
||||
///* 0x13 */ { REG_OCP, RF_OCP_ON | RF_OCP_TRIM_95 }, // over current protection (default is 95mA)
|
||||
|
||||
// RXBW defaults are { REG_RXBW, RF_RXBW_DCCFREQ_010 | RF_RXBW_MANT_24 | RF_RXBW_EXP_5} (RxBw: 10.4KHz)
|
||||
/* 0x19 */ { REG_RXBW, RF_RXBW_DCCFREQ_010 | RF_RXBW_MANT_16 | RF_RXBW_EXP_2 }, // (BitRate < 2 * RxBw)
|
||||
//for BR-19200: /* 0x19 */ { REG_RXBW, RF_RXBW_DCCFREQ_010 | RF_RXBW_MANT_24 | RF_RXBW_EXP_3 },
|
||||
/* 0x25 */ { REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_01 }, // DIO0 is the only IRQ we're using
|
||||
/* 0x26 */ { REG_DIOMAPPING2, RF_DIOMAPPING2_CLKOUT_OFF }, // DIO5 ClkOut disable for power saving
|
||||
/* 0x28 */ { REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN }, // writing to this bit ensures that the FIFO & status flags are reset
|
||||
/* 0x29 */ { REG_RSSITHRESH, 220 }, // must be set to dBm = (-Sensitivity / 2), default is 0xE4 = 228 so -114dBm
|
||||
///* 0x2D */ { REG_PREAMBLELSB, RF_PREAMBLESIZE_LSB_VALUE } // default 3 preamble bytes 0xAAAAAA
|
||||
/* 0x2E */ { REG_SYNCCONFIG, RF_SYNC_ON | RF_SYNC_FIFOFILL_AUTO | RF_SYNC_SIZE_2 | RF_SYNC_TOL_0 },
|
||||
/* 0x2F */ { REG_SYNCVALUE1, 0x2D }, // attempt to make this compatible with sync1 byte of RFM12B lib
|
||||
/* 0x30 */ { REG_SYNCVALUE2, networkID }, // NETWORK ID
|
||||
/* 0x37 */ { REG_PACKETCONFIG1, RF_PACKET1_FORMAT_VARIABLE | RF_PACKET1_DCFREE_OFF | RF_PACKET1_CRC_ON | RF_PACKET1_CRCAUTOCLEAR_ON | RF_PACKET1_ADRSFILTERING_OFF },
|
||||
/* 0x38 */ { REG_PAYLOADLENGTH, 66 }, // in variable length mode: the max frame size, not used in TX
|
||||
///* 0x39 */ { REG_NODEADRS, nodeID }, // turned off because we're not using address filtering
|
||||
/* 0x3C */ { REG_FIFOTHRESH, RF_FIFOTHRESH_TXSTART_FIFONOTEMPTY | RF_FIFOTHRESH_VALUE }, // TX on FIFO not empty
|
||||
/* 0x3D */ { REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_2BITS | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF }, // RXRESTARTDELAY must match transmitter PA ramp-down time (bitrate dependent)
|
||||
//for BR-19200: /* 0x3D */ { REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_NONE | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF }, // RXRESTARTDELAY must match transmitter PA ramp-down time (bitrate dependent)
|
||||
/* 0x6F */ { REG_TESTDAGC, RF_DAGC_IMPROVED_LOWBETA0 }, // run DAGC continuously in RX mode for Fading Margin Improvement, recommended default for AfcLowBetaOn=0
|
||||
{255, 0}
|
||||
};
|
||||
|
||||
digitalWrite(_slaveSelectPin, HIGH);
|
||||
pinMode(_slaveSelectPin, OUTPUT);
|
||||
SPI.begin();
|
||||
unsigned long start = millis();
|
||||
uint8_t timeout = 50;
|
||||
do {
|
||||
writeReg(REG_SYNCVALUE1, 0xAA);
|
||||
yield();
|
||||
} while (readReg(REG_SYNCVALUE1) != 0xaa && millis()-start < timeout);
|
||||
start = millis();
|
||||
do {
|
||||
writeReg(REG_SYNCVALUE1, 0x55);
|
||||
yield();
|
||||
} while (readReg(REG_SYNCVALUE1) != 0x55 && millis()-start < timeout);
|
||||
|
||||
for (uint8_t i = 0; CONFIG[i][0] != 255; i++)
|
||||
writeReg(CONFIG[i][0], CONFIG[i][1]);
|
||||
|
||||
// Encryption is persistent between resets and can trip you up during debugging.
|
||||
// Disable it during initialization so we always start from a known state.
|
||||
encrypt(0);
|
||||
|
||||
setHighPower(_isRFM69HW); // called regardless if it's a RFM69W or RFM69HW
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
start = millis();
|
||||
while (((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00) && millis()-start < timeout) {
|
||||
yield();
|
||||
} // wait for ModeReady
|
||||
if (millis()-start >= timeout)
|
||||
return false;
|
||||
attachInterrupt(_interruptNum, RFM69::isr0, RISING);
|
||||
|
||||
selfPointer = this;
|
||||
_address = nodeID;
|
||||
return true;
|
||||
}
|
||||
|
||||
// return the frequency (in Hz)
|
||||
uint32_t RFM69::getFrequency()
|
||||
{
|
||||
return RF69_FSTEP * (((uint32_t) readReg(REG_FRFMSB) << 16) + ((uint16_t) readReg(REG_FRFMID) << 8) + readReg(REG_FRFLSB));
|
||||
}
|
||||
|
||||
// set the frequency (in Hz)
|
||||
void RFM69::setFrequency(uint32_t freqHz)
|
||||
{
|
||||
uint8_t oldMode = _mode;
|
||||
if (oldMode == RF69_MODE_TX) {
|
||||
setMode(RF69_MODE_RX);
|
||||
}
|
||||
freqHz /= RF69_FSTEP; // divide down by FSTEP to get FRF
|
||||
writeReg(REG_FRFMSB, freqHz >> 16);
|
||||
writeReg(REG_FRFMID, freqHz >> 8);
|
||||
writeReg(REG_FRFLSB, freqHz);
|
||||
if (oldMode == RF69_MODE_RX) {
|
||||
setMode(RF69_MODE_SYNTH);
|
||||
}
|
||||
setMode(oldMode);
|
||||
}
|
||||
|
||||
void RFM69::setMode(uint8_t newMode)
|
||||
{
|
||||
if (newMode == _mode)
|
||||
return;
|
||||
|
||||
switch (newMode) {
|
||||
case RF69_MODE_TX:
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_TRANSMITTER);
|
||||
if (_isRFM69HW) setHighPowerRegs(true);
|
||||
break;
|
||||
case RF69_MODE_RX:
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_RECEIVER);
|
||||
if (_isRFM69HW) setHighPowerRegs(false);
|
||||
break;
|
||||
case RF69_MODE_SYNTH:
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_SYNTHESIZER);
|
||||
break;
|
||||
case RF69_MODE_STANDBY:
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_STANDBY);
|
||||
break;
|
||||
case RF69_MODE_SLEEP:
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_SLEEP);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// we are using packet mode, so this check is not really needed
|
||||
// but waiting for mode ready is necessary when going from sleep because the FIFO may not be immediately available from previous mode
|
||||
while (_mode == RF69_MODE_SLEEP && (readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady
|
||||
|
||||
_mode = newMode;
|
||||
}
|
||||
|
||||
//put transceiver in sleep mode to save battery - to wake or resume receiving just call receiveDone()
|
||||
void RFM69::sleep() {
|
||||
setMode(RF69_MODE_SLEEP);
|
||||
}
|
||||
|
||||
//set this node's address
|
||||
void RFM69::setAddress(uint8_t addr)
|
||||
{
|
||||
_address = addr;
|
||||
writeReg(REG_NODEADRS, _address);
|
||||
}
|
||||
|
||||
//set this node's network id
|
||||
void RFM69::setNetwork(uint8_t networkID)
|
||||
{
|
||||
writeReg(REG_SYNCVALUE2, networkID);
|
||||
}
|
||||
|
||||
// set *transmit/TX* output power: 0=min, 31=max
|
||||
// this results in a "weaker" transmitted signal, and directly results in a lower RSSI at the receiver
|
||||
// the power configurations are explained in the SX1231H datasheet (Table 10 on p21; RegPaLevel p66): http://www.semtech.com/images/datasheet/sx1231h.pdf
|
||||
// valid powerLevel parameter values are 0-31 and result in a directly proportional effect on the output/transmission power
|
||||
// this function implements 2 modes as follows:
|
||||
// - for RFM69W the range is from 0-31 [-18dBm to 13dBm] (PA0 only on RFIO pin)
|
||||
// - for RFM69HW the range is from 0-31 [5dBm to 20dBm] (PA1 & PA2 on PA_BOOST pin & high Power PA settings - see section 3.3.7 in datasheet, p22)
|
||||
void RFM69::setPowerLevel(uint8_t powerLevel)
|
||||
{
|
||||
_powerLevel = (powerLevel > 31 ? 31 : powerLevel);
|
||||
if (_isRFM69HW) _powerLevel /= 2;
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0xE0) | _powerLevel);
|
||||
}
|
||||
|
||||
bool RFM69::canSend()
|
||||
{
|
||||
if (_mode == RF69_MODE_RX && PAYLOADLEN == 0 && readRSSI() < CSMA_LIMIT) // if signal stronger than -100dBm is detected assume channel activity
|
||||
{
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RFM69::send(uint8_t toAddress, const void* buffer, uint8_t bufferSize, bool requestACK)
|
||||
{
|
||||
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART); // avoid RX deadlocks
|
||||
uint32_t now = millis();
|
||||
while (!canSend() && millis() - now < RF69_CSMA_LIMIT_MS) receiveDone();
|
||||
sendFrame(toAddress, buffer, bufferSize, requestACK, false);
|
||||
}
|
||||
|
||||
// to increase the chance of getting a packet across, call this function instead of send
|
||||
// and it handles all the ACK requesting/retrying for you :)
|
||||
// The only twist is that you have to manually listen to ACK requests on the other side and send back the ACKs
|
||||
// The reason for the semi-automaton is that the lib is interrupt driven and
|
||||
// requires user action to read the received data and decide what to do with it
|
||||
// replies usually take only 5..8ms at 50kbps@915MHz
|
||||
bool RFM69::sendWithRetry(uint8_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries, uint8_t retryWaitTime) {
|
||||
uint32_t sentTime;
|
||||
for (uint8_t i = 0; i <= retries; i++)
|
||||
{
|
||||
send(toAddress, buffer, bufferSize, true);
|
||||
sentTime = millis();
|
||||
while (millis() - sentTime < retryWaitTime)
|
||||
{
|
||||
if (ACKReceived(toAddress))
|
||||
{
|
||||
//Serial.print(" ~ms:"); Serial.print(millis() - sentTime);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//Serial.print(" RETRY#"); Serial.println(i + 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// should be polled immediately after sending a packet with ACK request
|
||||
bool RFM69::ACKReceived(uint8_t fromNodeID) {
|
||||
if (receiveDone())
|
||||
return (SENDERID == fromNodeID || fromNodeID == RF69_BROADCAST_ADDR) && ACK_RECEIVED;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check whether an ACK was requested in the last received packet (non-broadcasted packet)
|
||||
bool RFM69::ACKRequested() {
|
||||
return ACK_REQUESTED && (TARGETID != RF69_BROADCAST_ADDR);
|
||||
}
|
||||
|
||||
// should be called immediately after reception in case sender wants ACK
|
||||
void RFM69::sendACK(const void* buffer, uint8_t bufferSize) {
|
||||
ACK_REQUESTED = 0; // TWS added to make sure we don't end up in a timing race and infinite loop sending Acks
|
||||
uint8_t sender = SENDERID;
|
||||
int16_t _RSSI = RSSI; // save payload received RSSI value
|
||||
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART); // avoid RX deadlocks
|
||||
uint32_t now = millis();
|
||||
while (!canSend() && millis() - now < RF69_CSMA_LIMIT_MS) {
|
||||
receiveDone();
|
||||
yield();
|
||||
}
|
||||
SENDERID = sender; // TWS: Restore SenderID after it gets wiped out by receiveDone()
|
||||
sendFrame(sender, buffer, bufferSize, false, true);
|
||||
RSSI = _RSSI; // restore payload RSSI
|
||||
}
|
||||
|
||||
void RFM69::interruptHook(uint8_t CTLbyte) {
|
||||
(void)CTLbyte;
|
||||
};
|
||||
|
||||
// internal function
|
||||
void RFM69::sendFrame(uint8_t toAddress, const void* buffer, uint8_t bufferSize, bool requestACK, bool sendACK)
|
||||
{
|
||||
setMode(RF69_MODE_STANDBY); // turn off receiver to prevent reception while filling fifo
|
||||
while ((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady
|
||||
writeReg(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_00); // DIO0 is "Packet Sent"
|
||||
if (bufferSize > RF69_MAX_DATA_LEN) bufferSize = RF69_MAX_DATA_LEN;
|
||||
|
||||
// control byte
|
||||
uint8_t CTLbyte = 0x00;
|
||||
if (sendACK)
|
||||
CTLbyte = RFM69_CTL_SENDACK;
|
||||
else if (requestACK)
|
||||
CTLbyte = RFM69_CTL_REQACK;
|
||||
|
||||
// write to FIFO
|
||||
select();
|
||||
SPI.transfer(REG_FIFO | 0x80);
|
||||
SPI.transfer(bufferSize + 3);
|
||||
SPI.transfer(toAddress);
|
||||
SPI.transfer(_address);
|
||||
SPI.transfer(CTLbyte);
|
||||
|
||||
for (uint8_t i = 0; i < bufferSize; i++)
|
||||
SPI.transfer(((uint8_t*) buffer)[i]);
|
||||
unselect();
|
||||
|
||||
// no need to wait for transmit mode to be ready since its handled by the radio
|
||||
setMode(RF69_MODE_TX);
|
||||
uint32_t txStart = millis();
|
||||
while (digitalRead(_interruptPin) == 0 && millis() - txStart < RF69_TX_LIMIT_MS); // wait for DIO0 to turn HIGH signalling transmission finish
|
||||
//while (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PACKETSENT == 0x00); // wait for ModeReady
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
}
|
||||
|
||||
// internal function - interrupt gets called when a packet is received
|
||||
void RFM69::interruptHandler() {
|
||||
//pinMode(4, OUTPUT);
|
||||
//digitalWrite(4, 1);
|
||||
if (_mode == RF69_MODE_RX && (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY))
|
||||
{
|
||||
//RSSI = readRSSI();
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
select();
|
||||
SPI.transfer(REG_FIFO & 0x7F);
|
||||
PAYLOADLEN = SPI.transfer(0);
|
||||
PAYLOADLEN = PAYLOADLEN > 66 ? 66 : PAYLOADLEN; // precaution
|
||||
TARGETID = SPI.transfer(0);
|
||||
if(!(_promiscuousMode || TARGETID == _address || TARGETID == RF69_BROADCAST_ADDR) // match this node's address, or broadcast address or anything in promiscuous mode
|
||||
|| PAYLOADLEN < 3) // address situation could receive packets that are malformed and don't fit this libraries extra fields
|
||||
{
|
||||
PAYLOADLEN = 0;
|
||||
unselect();
|
||||
receiveBegin();
|
||||
//digitalWrite(4, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
DATALEN = PAYLOADLEN - 3;
|
||||
SENDERID = SPI.transfer(0);
|
||||
uint8_t CTLbyte = SPI.transfer(0);
|
||||
|
||||
ACK_RECEIVED = CTLbyte & RFM69_CTL_SENDACK; // extract ACK-received flag
|
||||
ACK_REQUESTED = CTLbyte & RFM69_CTL_REQACK; // extract ACK-requested flag
|
||||
|
||||
interruptHook(CTLbyte); // TWS: hook to derived class interrupt function
|
||||
|
||||
for (uint8_t i = 0; i < DATALEN; i++)
|
||||
{
|
||||
DATA[i] = SPI.transfer(0);
|
||||
}
|
||||
if (DATALEN < RF69_MAX_DATA_LEN) DATA[DATALEN] = 0; // add null at end of string
|
||||
unselect();
|
||||
setMode(RF69_MODE_RX);
|
||||
}
|
||||
RSSI = readRSSI();
|
||||
//digitalWrite(4, 0);
|
||||
}
|
||||
|
||||
// internal function
|
||||
void RFM69::isr0() { selfPointer->interruptHandler(); }
|
||||
|
||||
// internal function
|
||||
void RFM69::receiveBegin() {
|
||||
DATALEN = 0;
|
||||
SENDERID = 0;
|
||||
TARGETID = 0;
|
||||
PAYLOADLEN = 0;
|
||||
ACK_REQUESTED = 0;
|
||||
ACK_RECEIVED = 0;
|
||||
RSSI = 0;
|
||||
if (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY)
|
||||
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART); // avoid RX deadlocks
|
||||
writeReg(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_01); // set DIO0 to "PAYLOADREADY" in receive mode
|
||||
setMode(RF69_MODE_RX);
|
||||
}
|
||||
|
||||
// checks if a packet was received and/or puts transceiver in receive (ie RX or listen) mode
|
||||
bool RFM69::receiveDone() {
|
||||
//ATOMIC_BLOCK(ATOMIC_FORCEON)
|
||||
//{
|
||||
noInterrupts(); // re-enabled in unselect() via setMode() or via receiveBegin()
|
||||
if (_mode == RF69_MODE_RX && PAYLOADLEN > 0)
|
||||
{
|
||||
setMode(RF69_MODE_STANDBY); // enables interrupts
|
||||
return true;
|
||||
}
|
||||
else if (_mode == RF69_MODE_RX) // already in RX no payload yet
|
||||
{
|
||||
interrupts(); // explicitly re-enable interrupts
|
||||
return false;
|
||||
}
|
||||
receiveBegin();
|
||||
return false;
|
||||
//}
|
||||
}
|
||||
|
||||
// To enable encryption: radio.encrypt("ABCDEFGHIJKLMNOP");
|
||||
// To disable encryption: radio.encrypt(null) or radio.encrypt(0)
|
||||
// KEY HAS TO BE 16 bytes !!!
|
||||
void RFM69::encrypt(const char* key) {
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
if (key != 0)
|
||||
{
|
||||
select();
|
||||
SPI.transfer(REG_AESKEY1 | 0x80);
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
SPI.transfer(key[i]);
|
||||
unselect();
|
||||
}
|
||||
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFE) | (key ? 1 : 0));
|
||||
}
|
||||
|
||||
// get the received signal strength indicator (RSSI)
|
||||
int16_t RFM69::readRSSI(bool forceTrigger) {
|
||||
int16_t rssi = 0;
|
||||
if (forceTrigger)
|
||||
{
|
||||
// RSSI trigger not needed if DAGC is in continuous mode
|
||||
writeReg(REG_RSSICONFIG, RF_RSSI_START);
|
||||
while ((readReg(REG_RSSICONFIG) & RF_RSSI_DONE) == 0x00); // wait for RSSI_Ready
|
||||
}
|
||||
rssi = -readReg(REG_RSSIVALUE);
|
||||
rssi >>= 1;
|
||||
return rssi;
|
||||
}
|
||||
|
||||
uint8_t RFM69::readReg(uint8_t addr)
|
||||
{
|
||||
select();
|
||||
SPI.transfer(addr & 0x7F);
|
||||
uint8_t regval = SPI.transfer(0);
|
||||
unselect();
|
||||
return regval;
|
||||
}
|
||||
|
||||
void RFM69::writeReg(uint8_t addr, uint8_t value)
|
||||
{
|
||||
select();
|
||||
SPI.transfer(addr | 0x80);
|
||||
SPI.transfer(value);
|
||||
unselect();
|
||||
}
|
||||
|
||||
// select the RFM69 transceiver (save SPI settings, set CS low)
|
||||
void RFM69::select() {
|
||||
noInterrupts();
|
||||
#if defined (SPCR) && defined (SPSR)
|
||||
// save current SPI settings
|
||||
_SPCR = SPCR;
|
||||
_SPSR = SPSR;
|
||||
#endif
|
||||
// set RFM69 SPI settings
|
||||
SPI.setDataMode(SPI_MODE0);
|
||||
SPI.setBitOrder(MSBFIRST);
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV4); // decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present
|
||||
digitalWrite(_slaveSelectPin, LOW);
|
||||
}
|
||||
|
||||
// unselect the RFM69 transceiver (set CS high, restore SPI settings)
|
||||
void RFM69::unselect() {
|
||||
digitalWrite(_slaveSelectPin, HIGH);
|
||||
// restore SPI settings to what they were before talking to RFM69
|
||||
#if defined (SPCR) && defined (SPSR)
|
||||
SPCR = _SPCR;
|
||||
SPSR = _SPSR;
|
||||
#endif
|
||||
interrupts();
|
||||
}
|
||||
|
||||
// true = disable filtering to capture all frames on network
|
||||
// false = enable node/broadcast filtering to capture only frames sent to this/broadcast address
|
||||
void RFM69::promiscuous(bool onOff) {
|
||||
_promiscuousMode = onOff;
|
||||
//writeReg(REG_PACKETCONFIG1, (readReg(REG_PACKETCONFIG1) & 0xF9) | (onOff ? RF_PACKET1_ADRSFILTERING_OFF : RF_PACKET1_ADRSFILTERING_NODEBROADCAST));
|
||||
}
|
||||
|
||||
// for RFM69HW only: you must call setHighPower(true) after initialize() or else transmission won't work
|
||||
void RFM69::setHighPower(bool onOff) {
|
||||
_isRFM69HW = onOff;
|
||||
writeReg(REG_OCP, _isRFM69HW ? RF_OCP_OFF : RF_OCP_ON);
|
||||
if (_isRFM69HW) // turning ON
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | RF_PALEVEL_PA1_ON | RF_PALEVEL_PA2_ON); // enable P1 & P2 amplifier stages
|
||||
else
|
||||
writeReg(REG_PALEVEL, RF_PALEVEL_PA0_ON | RF_PALEVEL_PA1_OFF | RF_PALEVEL_PA2_OFF | _powerLevel); // enable P0 only
|
||||
}
|
||||
|
||||
// internal function
|
||||
void RFM69::setHighPowerRegs(bool onOff) {
|
||||
writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
|
||||
writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
|
||||
}
|
||||
|
||||
// set the slave select (CS) pin
|
||||
void RFM69::setCS(uint8_t newSPISlaveSelect) {
|
||||
_slaveSelectPin = newSPISlaveSelect;
|
||||
digitalWrite(_slaveSelectPin, HIGH);
|
||||
pinMode(_slaveSelectPin, OUTPUT);
|
||||
}
|
||||
|
||||
//for debugging
|
||||
#define REGISTER_DETAIL 0
|
||||
#if REGISTER_DETAIL
|
||||
// SERIAL PRINT
|
||||
// replace Serial.print("string") with SerialPrint("string")
|
||||
#define SerialPrint(x) SerialPrint_P(PSTR(x))
|
||||
void SerialWrite ( uint8_t c ) {
|
||||
Serial.write ( c );
|
||||
}
|
||||
|
||||
void SerialPrint_P(PGM_P str, void (*f)(uint8_t) = SerialWrite ) {
|
||||
for (uint8_t c; (c = pgm_read_byte(str)); str++) (*f)(c);
|
||||
}
|
||||
#endif
|
||||
|
||||
void RFM69::readAllRegs()
|
||||
{
|
||||
uint8_t regVal;
|
||||
|
||||
#if REGISTER_DETAIL
|
||||
int capVal;
|
||||
|
||||
//... State Variables for intelligent decoding
|
||||
uint8_t modeFSK = 0;
|
||||
int bitRate = 0;
|
||||
int freqDev = 0;
|
||||
long freqCenter = 0;
|
||||
#endif
|
||||
|
||||
Serial.println("Address - HEX - BIN");
|
||||
for (uint8_t regAddr = 1; regAddr <= 0x4F; regAddr++)
|
||||
{
|
||||
select();
|
||||
SPI.transfer(regAddr & 0x7F); // send address + r/w bit
|
||||
regVal = SPI.transfer(0);
|
||||
unselect();
|
||||
|
||||
Serial.print(regAddr, HEX);
|
||||
Serial.print(" - ");
|
||||
Serial.print(regVal,HEX);
|
||||
Serial.print(" - ");
|
||||
Serial.println(regVal,BIN);
|
||||
|
||||
#if REGISTER_DETAIL
|
||||
switch ( regAddr )
|
||||
{
|
||||
case 0x1 : {
|
||||
SerialPrint ( "Controls the automatic Sequencer ( see section 4.2 )\nSequencerOff : " );
|
||||
if ( 0x80 & regVal ) {
|
||||
SerialPrint ( "1 -> Mode is forced by the user\n" );
|
||||
} else {
|
||||
SerialPrint ( "0 -> Operating mode as selected with Mode bits in RegOpMode is automatically reached with the Sequencer\n" );
|
||||
}
|
||||
|
||||
SerialPrint( "\nEnables Listen mode, should be enabled whilst in Standby mode:\nListenOn : " );
|
||||
if ( 0x40 & regVal ) {
|
||||
SerialPrint ( "1 -> On\n" );
|
||||
} else {
|
||||
SerialPrint ( "0 -> Off ( see section 4.3)\n" );
|
||||
}
|
||||
|
||||
SerialPrint( "\nAborts Listen mode when set together with ListenOn=0 See section 4.3.4 for details (Always reads 0.)\n" );
|
||||
if ( 0x20 & regVal ) {
|
||||
SerialPrint ( "ERROR - ListenAbort should NEVER return 1 this is a write only register\n" );
|
||||
}
|
||||
|
||||
SerialPrint("\nTransceiver's operating modes:\nMode : ");
|
||||
capVal = (regVal >> 2) & 0x7;
|
||||
if ( capVal == 0b000 ) {
|
||||
SerialPrint ( "000 -> Sleep mode (SLEEP)\n" );
|
||||
} else if ( capVal = 0b001 ) {
|
||||
SerialPrint ( "001 -> Standby mode (STDBY)\n" );
|
||||
} else if ( capVal = 0b010 ) {
|
||||
SerialPrint ( "010 -> Frequency Synthesizer mode (FS)\n" );
|
||||
} else if ( capVal = 0b011 ) {
|
||||
SerialPrint ( "011 -> Transmitter mode (TX)\n" );
|
||||
} else if ( capVal = 0b100 ) {
|
||||
SerialPrint ( "100 -> Receiver Mode (RX)\n" );
|
||||
} else {
|
||||
Serial.print( capVal, BIN );
|
||||
SerialPrint ( " -> RESERVED\n" );
|
||||
}
|
||||
SerialPrint ( "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x2 : {
|
||||
|
||||
SerialPrint("Data Processing mode:\nDataMode : ");
|
||||
capVal = (regVal >> 5) & 0x3;
|
||||
if ( capVal == 0b00 ) {
|
||||
SerialPrint ( "00 -> Packet mode\n" );
|
||||
} else if ( capVal == 0b01 ) {
|
||||
SerialPrint ( "01 -> reserved\n" );
|
||||
} else if ( capVal == 0b10 ) {
|
||||
SerialPrint ( "10 -> Continuous mode with bit synchronizer\n" );
|
||||
} else if ( capVal == 0b11 ) {
|
||||
SerialPrint ( "11 -> Continuous mode without bit synchronizer\n" );
|
||||
}
|
||||
|
||||
SerialPrint("\nModulation scheme:\nModulation Type : ");
|
||||
capVal = (regVal >> 3) & 0x3;
|
||||
if ( capVal == 0b00 ) {
|
||||
SerialPrint ( "00 -> FSK\n" );
|
||||
modeFSK = 1;
|
||||
} else if ( capVal == 0b01 ) {
|
||||
SerialPrint ( "01 -> OOK\n" );
|
||||
} else if ( capVal == 0b10 ) {
|
||||
SerialPrint ( "10 -> reserved\n" );
|
||||
} else if ( capVal == 0b11 ) {
|
||||
SerialPrint ( "11 -> reserved\n" );
|
||||
}
|
||||
|
||||
SerialPrint("\nData shaping: ");
|
||||
if ( modeFSK ) {
|
||||
SerialPrint( "in FSK:\n" );
|
||||
} else {
|
||||
SerialPrint( "in OOK:\n" );
|
||||
}
|
||||
SerialPrint ("ModulationShaping : ");
|
||||
capVal = regVal & 0x3;
|
||||
if ( modeFSK ) {
|
||||
if ( capVal == 0b00 ) {
|
||||
SerialPrint ( "00 -> no shaping\n" );
|
||||
} else if ( capVal == 0b01 ) {
|
||||
SerialPrint ( "01 -> Gaussian filter, BT = 1.0\n" );
|
||||
} else if ( capVal == 0b10 ) {
|
||||
SerialPrint ( "10 -> Gaussian filter, BT = 0.5\n" );
|
||||
} else if ( capVal == 0b11 ) {
|
||||
SerialPrint ( "11 -> Gaussian filter, BT = 0.3\n" );
|
||||
}
|
||||
} else {
|
||||
if ( capVal == 0b00 ) {
|
||||
SerialPrint ( "00 -> no shaping\n" );
|
||||
} else if ( capVal == 0b01 ) {
|
||||
SerialPrint ( "01 -> filtering with f(cutoff) = BR\n" );
|
||||
} else if ( capVal == 0b10 ) {
|
||||
SerialPrint ( "10 -> filtering with f(cutoff) = 2*BR\n" );
|
||||
} else if ( capVal == 0b11 ) {
|
||||
SerialPrint ( "ERROR - 11 is reserved\n" );
|
||||
}
|
||||
}
|
||||
|
||||
SerialPrint ( "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x3 : {
|
||||
bitRate = (regVal << 8);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4 : {
|
||||
bitRate |= regVal;
|
||||
SerialPrint ( "Bit Rate (Chip Rate when Manchester encoding is enabled)\nBitRate : ");
|
||||
unsigned long val = 32UL * 1000UL * 1000UL / bitRate;
|
||||
Serial.println( val );
|
||||
SerialPrint( "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x5 : {
|
||||
freqDev = ( (regVal & 0x3f) << 8 );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x6 : {
|
||||
freqDev |= regVal;
|
||||
SerialPrint( "Frequency deviation\nFdev : " );
|
||||
unsigned long val = 61UL * freqDev;
|
||||
Serial.println( val );
|
||||
SerialPrint ( "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x7 : {
|
||||
unsigned long tempVal = regVal;
|
||||
freqCenter = ( tempVal << 16 );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x8 : {
|
||||
unsigned long tempVal = regVal;
|
||||
freqCenter = freqCenter | ( tempVal << 8 );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x9 : {
|
||||
freqCenter = freqCenter | regVal;
|
||||
SerialPrint ( "RF Carrier frequency\nFRF : " );
|
||||
unsigned long val = 61UL * freqCenter;
|
||||
Serial.println( val );
|
||||
SerialPrint( "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xa : {
|
||||
SerialPrint ( "RC calibration control & status\nRcCalDone : " );
|
||||
if ( 0x40 & regVal ) {
|
||||
SerialPrint ( "1 -> RC calibration is over\n" );
|
||||
} else {
|
||||
SerialPrint ( "0 -> RC calibration is in progress\n" );
|
||||
}
|
||||
|
||||
SerialPrint ( "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xb : {
|
||||
SerialPrint ( "Improved AFC routine for signals with modulation index lower than 2. Refer to section 3.4.16 for details\nAfcLowBetaOn : " );
|
||||
if ( 0x20 & regVal ) {
|
||||
SerialPrint ( "1 -> Improved AFC routine\n" );
|
||||
} else {
|
||||
SerialPrint ( "0 -> Standard AFC routine\n" );
|
||||
}
|
||||
SerialPrint ( "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xc : {
|
||||
SerialPrint ( "Reserved\n\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xd : {
|
||||
byte val;
|
||||
SerialPrint ( "Resolution of Listen mode Idle time (calibrated RC osc):\nListenResolIdle : " );
|
||||
val = regVal >> 6;
|
||||
if ( val == 0b00 ) {
|
||||
SerialPrint ( "00 -> reserved\n" );
|
||||
} else if ( val == 0b01 ) {
|
||||
SerialPrint ( "01 -> 64 us\n" );
|
||||
} else if ( val == 0b10 ) {
|
||||
SerialPrint ( "10 -> 4.1 ms\n" );
|
||||
} else if ( val == 0b11 ) {
|
||||
SerialPrint ( "11 -> 262 ms\n" );
|
||||
}
|
||||
|
||||
SerialPrint ( "\nResolution of Listen mode Rx time (calibrated RC osc):\nListenResolRx : " );
|
||||
val = (regVal >> 4) & 0x3;
|
||||
if ( val == 0b00 ) {
|
||||
SerialPrint ( "00 -> reserved\n" );
|
||||
} else if ( val == 0b01 ) {
|
||||
SerialPrint ( "01 -> 64 us\n" );
|
||||
} else if ( val == 0b10 ) {
|
||||
SerialPrint ( "10 -> 4.1 ms\n" );
|
||||
} else if ( val == 0b11 ) {
|
||||
SerialPrint ( "11 -> 262 ms\n" );
|
||||
}
|
||||
|
||||
SerialPrint ( "\nCriteria for packet acceptance in Listen mode:\nListenCriteria : " );
|
||||
if ( 0x8 & regVal ) {
|
||||
SerialPrint ( "1 -> signal strength is above RssiThreshold and SyncAddress matched\n" );
|
||||
} else {
|
||||
SerialPrint ( "0 -> signal strength is above RssiThreshold\n" );
|
||||
}
|
||||
|
||||
SerialPrint ( "\nAction taken after acceptance of a packet in Listen mode:\nListenEnd : " );
|
||||
val = (regVal >> 1 ) & 0x3;
|
||||
if ( val == 0b00 ) {
|
||||
SerialPrint ( "00 -> chip stays in Rx mode. Listen mode stops and must be disabled (see section 4.3)\n" );
|
||||
} else if ( val == 0b01 ) {
|
||||
SerialPrint ( "01 -> chip stays in Rx mode until PayloadReady or Timeout interrupt occurs. It then goes to the mode defined by Mode. Listen mode stops and must be disabled (see section 4.3)\n" );
|
||||
} else if ( val == 0b10 ) {
|
||||
SerialPrint ( "10 -> chip stays in Rx mode until PayloadReady or Timeout occurs. Listen mode then resumes in Idle state. FIFO content is lost at next Rx wakeup.\n" );
|
||||
} else if ( val == 0b11 ) {
|
||||
SerialPrint ( "11 -> Reserved\n" );
|
||||
}
|
||||
|
||||
|
||||
SerialPrint ( "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
default : {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
unselect();
|
||||
}
|
||||
|
||||
uint8_t RFM69::readTemperature(uint8_t calFactor) // returns centigrade
|
||||
{
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
writeReg(REG_TEMP1, RF_TEMP1_MEAS_START);
|
||||
while ((readReg(REG_TEMP1) & RF_TEMP1_MEAS_RUNNING));
|
||||
return ~readReg(REG_TEMP2) + COURSE_TEMP_COEF + calFactor; // 'complement' corrects the slope, rising temp = rising val
|
||||
} // COURSE_TEMP_COEF puts reading in the ballpark, user can add additional correction
|
||||
|
||||
void RFM69::rcCalibration()
|
||||
{
|
||||
writeReg(REG_OSC1, RF_OSC1_RCCAL_START);
|
||||
while ((readReg(REG_OSC1) & RF_OSC1_RCCAL_DONE) == 0x00);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// **********************************************************************************
|
||||
// Driver definition for HopeRF RFM69W/RFM69HW/RFM69CW/RFM69HCW, Semtech SX1231/1231H
|
||||
// **********************************************************************************
|
||||
// Copyright Felix Rusu (2014), felix@lowpowerlab.com
|
||||
// http://lowpowerlab.com/
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
// **********************************************************************************
|
||||
#ifndef RFM69_h
|
||||
#define RFM69_h
|
||||
#include <Arduino.h> // assumes Arduino IDE v1.0 or greater
|
||||
|
||||
#define RF69_MAX_DATA_LEN 61 // to take advantage of the built in AES/CRC we want to limit the frame size to the internal FIFO size (66 bytes - 3 bytes overhead - 2 bytes crc)
|
||||
#define RF69_SPI_CS SS // SS is the SPI slave select pin, for instance D10 on ATmega328
|
||||
|
||||
// INT0 on AVRs should be connected to RFM69's DIO0 (ex on ATmega328 it's D2, on ATmega644/1284 it's D2)
|
||||
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega88) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
||||
#define RF69_IRQ_PIN 2
|
||||
#define RF69_IRQ_NUM 0
|
||||
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
|
||||
#define RF69_IRQ_PIN 2
|
||||
#define RF69_IRQ_NUM 2
|
||||
#elif defined(__AVR_ATmega32U4__)
|
||||
#define RF69_IRQ_PIN 3
|
||||
#define RF69_IRQ_NUM 0
|
||||
#elif defined(__arm__)//Use pin 10 or any pin you want
|
||||
#define RF69_IRQ_PIN 10
|
||||
#define RF69_IRQ_NUM 10
|
||||
#else
|
||||
#define RF69_IRQ_PIN 2
|
||||
#define RF69_IRQ_NUM 0
|
||||
#endif
|
||||
|
||||
|
||||
#define CSMA_LIMIT -90 // upper RX signal sensitivity threshold in dBm for carrier sense access
|
||||
#define RF69_MODE_SLEEP 0 // XTAL OFF
|
||||
#define RF69_MODE_STANDBY 1 // XTAL ON
|
||||
#define RF69_MODE_SYNTH 2 // PLL ON
|
||||
#define RF69_MODE_RX 3 // RX MODE
|
||||
#define RF69_MODE_TX 4 // TX MODE
|
||||
|
||||
// available frequency bands
|
||||
#define RF69_315MHZ 31 // non trivial values to avoid misconfiguration
|
||||
#define RF69_433MHZ 43
|
||||
#define RF69_868MHZ 86
|
||||
#define RF69_915MHZ 91
|
||||
|
||||
#define null 0
|
||||
#define COURSE_TEMP_COEF -90 // puts the temperature reading in the ballpark, user can fine tune the returned value
|
||||
#define RF69_BROADCAST_ADDR 255
|
||||
#define RF69_CSMA_LIMIT_MS 1000
|
||||
#define RF69_TX_LIMIT_MS 1000
|
||||
#define RF69_FSTEP 61.03515625 // == FXOSC / 2^19 = 32MHz / 2^19 (p13 in datasheet)
|
||||
|
||||
// TWS: define CTLbyte bits
|
||||
#define RFM69_CTL_SENDACK 0x80
|
||||
#define RFM69_CTL_REQACK 0x40
|
||||
|
||||
/** RFM69 class */
|
||||
class RFM69 {
|
||||
public:
|
||||
static volatile uint8_t DATA[RF69_MAX_DATA_LEN]; //!< recv/xmit buf, including hdr & crc bytes
|
||||
static volatile uint8_t DATALEN; //!< DATALEN
|
||||
static volatile uint8_t SENDERID; //!< SENDERID
|
||||
static volatile uint8_t TARGETID; //!< should match _address
|
||||
static volatile uint8_t PAYLOADLEN; //!< PAYLOADLEN
|
||||
static volatile uint8_t ACK_REQUESTED; //!< ACK_REQUESTED
|
||||
static volatile uint8_t ACK_RECEIVED; //!< Should be polled immediately after sending a packet with ACK requestwith ACK request
|
||||
static volatile int16_t RSSI; //!< most accurate RSSI during reception (closest to the reception)
|
||||
static volatile uint8_t _mode; //!< should be protected?
|
||||
|
||||
RFM69(uint8_t slaveSelectPin=RF69_SPI_CS, uint8_t interruptPin=RF69_IRQ_PIN, bool isRFM69HW=false, uint8_t interruptNum=RF69_IRQ_NUM) { //!< Constructor
|
||||
_slaveSelectPin = slaveSelectPin;
|
||||
_interruptPin = interruptPin;
|
||||
_interruptNum = interruptNum;
|
||||
_mode = RF69_MODE_STANDBY;
|
||||
_promiscuousMode = false;
|
||||
_powerLevel = 31;
|
||||
_isRFM69HW = isRFM69HW;
|
||||
}
|
||||
|
||||
bool initialize(uint8_t freqBand, uint8_t ID, uint8_t networkID=1); //!< initialize
|
||||
void setAddress(uint8_t addr); //!< setAddress
|
||||
void setNetwork(uint8_t networkID); //!< setNetwork
|
||||
bool canSend(); //!< canSend
|
||||
virtual void send(uint8_t toAddress, const void* buffer, uint8_t bufferSize, bool requestACK=false); //!< send
|
||||
virtual bool sendWithRetry(uint8_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries=2, uint8_t retryWaitTime=40); //!< sendWithRetry (40ms roundtrip req for 61byte packets)
|
||||
virtual bool receiveDone(); //!< receiveDone
|
||||
bool ACKReceived(uint8_t fromNodeID); //!< ACKReceived
|
||||
bool ACKRequested(); //!< ACKRequested
|
||||
virtual void sendACK(const void* buffer = "", uint8_t bufferSize=0); //!< sendACK
|
||||
uint32_t getFrequency(); //!< getFrequency
|
||||
void setFrequency(uint32_t freqHz); //!< setFrequency
|
||||
void encrypt(const char* key); //!< encrypt
|
||||
void setCS(uint8_t newSPISlaveSelect); //!< setCS
|
||||
int16_t readRSSI(bool forceTrigger=false); //!< readRSSI
|
||||
void promiscuous(bool onOff=true); //!< promiscuous
|
||||
virtual void setHighPower(bool onOFF=true); //!< setHighPower (have to call it after initialize for RFM69HW)
|
||||
virtual void setPowerLevel(uint8_t level); //!< setPowerLevel (reduce/increase transmit power level)
|
||||
void sleep(); //!< sleep
|
||||
uint8_t readTemperature(uint8_t calFactor=0); //!< readTemperature (get CMOS temperature (8bit))
|
||||
void rcCalibration(); //!< rcCalibration (calibrate the internal RC oscillator for use in wide temperature variations - see datasheet section [4.3.5. RC Timer Accuracy])
|
||||
|
||||
// allow hacking registers by making these public
|
||||
uint8_t readReg(uint8_t addr); //!< readReg
|
||||
void writeReg(uint8_t addr, uint8_t val); //!< writeReg
|
||||
void readAllRegs(); //!< readAllRegs
|
||||
|
||||
protected:
|
||||
static void isr0(); //!< isr0
|
||||
void virtual interruptHandler(); //!< interruptHandler
|
||||
virtual void interruptHook(uint8_t CTLbyte); //!< interruptHook
|
||||
virtual void sendFrame(uint8_t toAddress, const void* buffer, uint8_t size, bool requestACK=false, bool sendACK=false); //!< sendFrame
|
||||
|
||||
static RFM69* selfPointer; //!< selfPointer
|
||||
uint8_t _slaveSelectPin; //!< _slaveSelectPin
|
||||
uint8_t _interruptPin; //!< _interruptPin
|
||||
uint8_t _interruptNum; //!< _interruptNum
|
||||
uint8_t _address; //!< _address
|
||||
bool _promiscuousMode; //!< _promiscuousMode
|
||||
uint8_t _powerLevel; //!< _powerLevel
|
||||
bool _isRFM69HW; //!< _isRFM69HW
|
||||
#if defined (SPCR) && defined (SPSR)
|
||||
uint8_t _SPCR; //!< _SPCR
|
||||
uint8_t _SPSR; //!< _SPSR
|
||||
#endif
|
||||
|
||||
virtual void receiveBegin(); //!< receiveBegin
|
||||
virtual void setMode(uint8_t mode); //!< setMode
|
||||
virtual void setHighPowerRegs(bool onOff); //!< setHighPowerRegs
|
||||
virtual void select(); //!< select
|
||||
virtual void unselect(); //!< unselect
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) 2013-2015 by Felix Rusu, LowPowerLab.com
|
||||
// SPI Flash memory library for arduino/moteino.
|
||||
// This works with 256byte/page SPI flash memory
|
||||
// For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes)
|
||||
// Minimal modifications should allow chips that have different page size but modifications
|
||||
// DEPENDS ON: Arduino SPI library
|
||||
// > Updated Jan. 5, 2015, TomWS1, modified writeBytes to allow blocks > 256 bytes and handle page misalignment.
|
||||
// > Updated Feb. 26, 2015 TomWS1, added support for SPI Transactions (Arduino 1.5.8 and above)
|
||||
// > Selective merge by Felix after testing in IDE 1.0.6, 1.6.4
|
||||
// > Updated May 19, 2016 D-H-R, added support for SST25/Microchip Flash which does not support Page programming with OPCode 0x02,
|
||||
// > use define MY_SPIFLASH_SST25TYPE for SST25 Type Flash Memory
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
|
||||
#include "SPIFlash.h"
|
||||
|
||||
uint8_t SPIFlash::UNIQUEID[8];
|
||||
|
||||
/// IMPORTANT: NAND FLASH memory requires erase before write, because
|
||||
/// it can only transition from 1s to 0s and only the erase command can reset all 0s to 1s
|
||||
/// See http://en.wikipedia.org/wiki/Flash_memory
|
||||
/// The smallest range that can be erased is a sector (4K, 32K, 64K); there is also a chip erase command
|
||||
|
||||
/// Constructor. JedecID is optional but recommended, since this will ensure that the device is present and has a valid response
|
||||
/// get this from the datasheet of your flash chip
|
||||
/// Example for Atmel-Adesto 4Mbit AT25DF041A: 0x1F44 (page 27: http://www.adestotech.com/sites/default/files/datasheets/doc3668.pdf)
|
||||
/// Example for Winbond 4Mbit W25X40CL: 0xEF30 (page 14: http://www.winbond.com/NR/rdonlyres/6E25084C-0BFE-4B25-903D-AE10221A0929/0/W25X40CL.pdf)
|
||||
SPIFlash::SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID) {
|
||||
_slaveSelectPin = slaveSelectPin;
|
||||
_jedecID = jedecID;
|
||||
}
|
||||
|
||||
/// Select the flash chip
|
||||
void SPIFlash::select() {
|
||||
//save current SPI settings
|
||||
#ifndef SPI_HAS_TRANSACTION
|
||||
noInterrupts();
|
||||
#endif
|
||||
#ifndef ESP8266
|
||||
_SPCR = SPCR;
|
||||
_SPSR = SPSR;
|
||||
#endif
|
||||
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
SPI.beginTransaction(_settings);
|
||||
#else
|
||||
// set FLASH SPI settings
|
||||
SPI.setDataMode(SPI_MODE0);
|
||||
SPI.setBitOrder(MSBFIRST);
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV4); //decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present
|
||||
SPI.begin();
|
||||
#endif
|
||||
digitalWrite(_slaveSelectPin, LOW);
|
||||
}
|
||||
|
||||
/// UNselect the flash chip
|
||||
void SPIFlash::unselect() {
|
||||
digitalWrite(_slaveSelectPin, HIGH);
|
||||
//restore SPI settings to what they were before talking to the FLASH chip
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
SPI.endTransaction();
|
||||
#else
|
||||
interrupts();
|
||||
#endif
|
||||
#ifndef ESP8266
|
||||
SPCR = _SPCR;
|
||||
SPSR = _SPSR;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// setup SPI, read device ID etc...
|
||||
boolean SPIFlash::initialize()
|
||||
{
|
||||
#ifndef ESP8266
|
||||
_SPCR = SPCR;
|
||||
_SPSR = SPSR;
|
||||
#endif
|
||||
pinMode(_slaveSelectPin, OUTPUT);
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
_settings = SPISettings(4000000, MSBFIRST, SPI_MODE0);
|
||||
#endif
|
||||
|
||||
unselect();
|
||||
wakeup();
|
||||
|
||||
if (_jedecID == 0 || readDeviceId() == _jedecID) {
|
||||
command(SPIFLASH_STATUSWRITE, true); // Write Status Register
|
||||
SPI.transfer(0); // Global Unprotect
|
||||
unselect();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Get the manufacturer and device ID bytes (as a short word)
|
||||
uint16_t SPIFlash::readDeviceId()
|
||||
{
|
||||
#if defined(__AVR_ATmega32U4__) // Arduino Leonardo, MoteinoLeo
|
||||
command(SPIFLASH_IDREAD); // Read JEDEC ID
|
||||
#else
|
||||
select();
|
||||
SPI.transfer(SPIFLASH_IDREAD);
|
||||
#endif
|
||||
uint16_t jedecid = SPI.transfer(0) << 8;
|
||||
jedecid |= SPI.transfer(0);
|
||||
unselect();
|
||||
return jedecid;
|
||||
}
|
||||
|
||||
/// Get the 64 bit unique identifier, stores it in UNIQUEID[8]. Only needs to be called once, ie after initialize
|
||||
/// Returns the byte pointer to the UNIQUEID byte array
|
||||
/// Read UNIQUEID like this:
|
||||
/// flash.readUniqueId(); for (uint8_t i=0;i<8;i++) { Serial.print(flash.UNIQUEID[i], HEX); Serial.print(' '); }
|
||||
/// or like this:
|
||||
/// flash.readUniqueId(); uint8_t* MAC = flash.readUniqueId(); for (uint8_t i=0;i<8;i++) { Serial.print(MAC[i], HEX); Serial.print(' '); }
|
||||
uint8_t* SPIFlash::readUniqueId()
|
||||
{
|
||||
command(SPIFLASH_MACREAD);
|
||||
SPI.transfer(0);
|
||||
SPI.transfer(0);
|
||||
SPI.transfer(0);
|
||||
SPI.transfer(0);
|
||||
for (uint8_t i=0;i<8;i++)
|
||||
UNIQUEID[i] = SPI.transfer(0);
|
||||
unselect();
|
||||
return UNIQUEID;
|
||||
}
|
||||
|
||||
/// read 1 byte from flash memory
|
||||
uint8_t SPIFlash::readByte(uint32_t addr) {
|
||||
command(SPIFLASH_ARRAYREADLOWFREQ);
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
uint8_t result = SPI.transfer(0);
|
||||
unselect();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// read unlimited # of bytes
|
||||
void SPIFlash::readBytes(uint32_t addr, void* buf, uint16_t len) {
|
||||
command(SPIFLASH_ARRAYREAD);
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
SPI.transfer(0); //"dont care"
|
||||
for (uint16_t i = 0; i < len; ++i)
|
||||
((uint8_t*) buf)[i] = SPI.transfer(0);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// Send a command to the flash chip, pass TRUE for isWrite when its a write command
|
||||
void SPIFlash::command(uint8_t cmd, boolean isWrite){
|
||||
#if defined(__AVR_ATmega32U4__) // Arduino Leonardo, MoteinoLeo
|
||||
DDRB |= B00000001; // Make sure the SS pin (PB0 - used by RFM12B on MoteinoLeo R1) is set as output HIGH!
|
||||
PORTB |= B00000001;
|
||||
#endif
|
||||
if (isWrite)
|
||||
{
|
||||
command(SPIFLASH_WRITEENABLE); // Write Enable
|
||||
unselect();
|
||||
}
|
||||
//wait for any write/erase to complete
|
||||
// a time limit cannot really be added here without it being a very large safe limit
|
||||
// that is because some chips can take several seconds to carry out a chip erase or other similar multi block or entire-chip operations
|
||||
// a recommended alternative to such situations where chip can be or not be present is to add a 10k or similar weak pulldown on the
|
||||
// open drain MISO input which can read noise/static and hence return a non 0 status byte, causing the while() to hang when a flash chip is not present
|
||||
while(busy());
|
||||
select();
|
||||
SPI.transfer(cmd);
|
||||
}
|
||||
|
||||
/// check if the chip is busy erasing/writing
|
||||
boolean SPIFlash::busy()
|
||||
{
|
||||
/*
|
||||
select();
|
||||
SPI.transfer(SPIFLASH_STATUSREAD);
|
||||
uint8_t status = SPI.transfer(0);
|
||||
unselect();
|
||||
return status & 1;
|
||||
*/
|
||||
return readStatus() & 1;
|
||||
}
|
||||
|
||||
/// return the STATUS register
|
||||
uint8_t SPIFlash::readStatus()
|
||||
{
|
||||
select();
|
||||
SPI.transfer(SPIFLASH_STATUSREAD);
|
||||
uint8_t status = SPI.transfer(0);
|
||||
unselect();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/// Write 1 byte to flash memory
|
||||
/// WARNING: you can only write to previously erased memory locations (see datasheet)
|
||||
/// use the block erase commands to first clear memory (write 0xFFs)
|
||||
void SPIFlash::writeByte(uint32_t addr, uint8_t byt) {
|
||||
command(SPIFLASH_BYTEPAGEPROGRAM, true); // Byte/Page Program
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
SPI.transfer(byt);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// write multiple bytes to flash memory (up to 64K)
|
||||
/// WARNING: you can only write to previously erased memory locations (see datasheet)
|
||||
/// use the block erase commands to first clear memory (write 0xFFs)
|
||||
/// This version handles both page alignment and data blocks larger than 256 bytes.
|
||||
/// See documentation of #MY_SPIFLASH_SST25TYPE define for more information
|
||||
void SPIFlash::writeBytes(uint32_t addr, const void* buf, uint16_t len) {
|
||||
#ifdef MY_SPIFLASH_SST25TYPE
|
||||
//SST25 Type of Flash does not support Page Programming but AAI Word Programming
|
||||
uint16_t i=0;
|
||||
uint8_t oddAdr=0;
|
||||
char s[5];
|
||||
|
||||
command(SPIFLASH_AAIWORDPROGRAM, true); // Byte/Page Program
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
|
||||
if (addr%2){
|
||||
//start address is not even, i.e. first byte of word must be 0xff
|
||||
SPI.transfer(0xff);
|
||||
SPI.transfer(((uint8_t*) buf)[0]);
|
||||
unselect();
|
||||
oddAdr=1; //following addresses must all be shifted one off
|
||||
len--;
|
||||
if (len > 0) command(SPIFLASH_AAIWORDPROGRAM); //If for loop will run issue Wordprogram command
|
||||
}
|
||||
|
||||
for (i=0;i<(len/2);i++){
|
||||
//AAI command must be set before every new word
|
||||
if (i>0) command(SPIFLASH_AAIWORDPROGRAM); //Wordprogram command for first write has been issued before
|
||||
SPI.transfer(((uint8_t*) buf)[i*2+oddAdr]);
|
||||
SPI.transfer(((uint8_t*) buf)[i*2+1+oddAdr]);
|
||||
unselect();
|
||||
}
|
||||
|
||||
if (len-i*2 == 1){
|
||||
//There is one byte (i.e. half word) left. This happens if len was odd or (len was even and addr odd)
|
||||
if (i>0) command(SPIFLASH_AAIWORDPROGRAM); //if for loop had not run wordprogram command from before is still valid
|
||||
SPI.transfer(((uint8_t*) buf)[i*2+oddAdr]);
|
||||
SPI.transfer(0xff);
|
||||
unselect();
|
||||
}
|
||||
|
||||
command(SPIFLASH_WRITEDISABLE); //end AAI programming
|
||||
unselect();
|
||||
#else
|
||||
uint16_t n;
|
||||
uint16_t maxBytes = 256-(addr%256); // force the first set of bytes to stay within the first page
|
||||
uint16_t offset = 0;
|
||||
while (len>0)
|
||||
{
|
||||
n = (len<=maxBytes) ? len : maxBytes;
|
||||
command(SPIFLASH_BYTEPAGEPROGRAM, true); // Byte/Page Program
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
|
||||
for (uint16_t i = 0; i < n; i++)
|
||||
SPI.transfer(((uint8_t*) buf)[offset + i]);
|
||||
unselect();
|
||||
|
||||
addr+=n; // adjust the addresses and remaining bytes by what we've just transferred.
|
||||
offset +=n;
|
||||
len -= n;
|
||||
maxBytes = 256; // now we can do up to 256 bytes per loop
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// erase entire flash memory array
|
||||
/// may take several seconds depending on size, but is non blocking
|
||||
/// so you may wait for this to complete using busy() or continue doing
|
||||
/// other things and later check if the chip is done with busy()
|
||||
/// note that any command will first wait for chip to become available using busy()
|
||||
/// so no need to do that twice
|
||||
void SPIFlash::chipErase() {
|
||||
command(SPIFLASH_CHIPERASE, true);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// erase a 4Kbyte block
|
||||
void SPIFlash::blockErase4K(uint32_t addr) {
|
||||
command(SPIFLASH_BLOCKERASE_4K, true); // Block Erase
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// erase a 32Kbyte block
|
||||
void SPIFlash::blockErase32K(uint32_t addr) {
|
||||
command(SPIFLASH_BLOCKERASE_32K, true); // Block Erase
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
unselect();
|
||||
}
|
||||
|
||||
void SPIFlash::sleep() {
|
||||
command(SPIFLASH_SLEEP);
|
||||
unselect();
|
||||
}
|
||||
|
||||
void SPIFlash::wakeup() {
|
||||
command(SPIFLASH_WAKE);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// cleanup
|
||||
void SPIFlash::end() {
|
||||
SPI.end();
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) 2013-2015 by Felix Rusu, LowPowerLab.com
|
||||
// SPI Flash memory library for arduino/moteino.
|
||||
// This works with 256byte/page SPI flash memory
|
||||
// For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes)
|
||||
// Minimal modifications should allow chips that have different page size but modifications
|
||||
// DEPENDS ON: Arduino SPI library
|
||||
// > Updated Jan. 5, 2015, TomWS1, modified writeBytes to allow blocks > 256 bytes and handle page misalignment.
|
||||
// > Updated Feb. 26, 2015 TomWS1, added support for SPI Transactions (Arduino 1.5.8 and above)
|
||||
// > Selective merge by Felix after testing in IDE 1.0.6, 1.6.4
|
||||
// > Updated May 19, 2016 D-H-R, added support for SST25/Microchip Flash which does not support Page programming with OPCode 0x02,
|
||||
// > use define MY_SPIFLASH_SST25TYPE for SST25 Type Flash Memory. Added / changed comments to better suit doxygen
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
|
||||
///
|
||||
/// @file SPIFlash.h
|
||||
///
|
||||
/// @brief SPIFlash provides access to a SPI Flash IC for OTA update or storing data
|
||||
///
|
||||
/// IMPORTANT: NAND FLASH memory requires erase before write, because
|
||||
/// it can only transition from 1s to 0s and only the erase command can reset all 0s to 1s
|
||||
/// See http://en.wikipedia.org/wiki/Flash_memory
|
||||
/// The smallest range that can be erased is a sector (4K, 32K, 64K); there is also a chip erase command
|
||||
///
|
||||
/// Standard SPI flash commands <BR>
|
||||
/// Assuming the WP pin is pulled up (to disable hardware write protection).<BR>
|
||||
/// To use any write commands the WEL bit in the status register must be set to 1.<BR>
|
||||
/// This is accomplished by sending a 0x06 command before any such write/erase command.<BR>
|
||||
/// The WEL bit in the status register resets to the logical ?0? state after a device power-up or reset.
|
||||
/// In addition, the WEL bit will be reset to the logical ?0? state automatically under the following conditions:<BR>
|
||||
/// - Write Disable operation completes successfully<BR>
|
||||
/// - Write Status Register operation completes successfully or aborts<BR>
|
||||
/// - Protect Sector operation completes successfully or aborts<BR>
|
||||
/// - Unprotect Sector operation completes successfully or aborts<BR>
|
||||
/// - Byte/Page Program operation completes successfully or aborts<BR>
|
||||
/// - Sequential Program Mode reaches highest unprotected memory location<BR>
|
||||
/// - Sequential Program Mode reaches the end of the memory array<BR>
|
||||
/// - Sequential Program Mode aborts<BR>
|
||||
/// - Block Erase operation completes successfully or aborts<BR>
|
||||
/// - Chip Erase operation completes successfully or aborts<BR>
|
||||
/// - Hold condition aborts
|
||||
///
|
||||
|
||||
#ifndef _SPIFLASH_H_
|
||||
#define _SPIFLASH_H_
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <wiring.h>
|
||||
#include "pins_arduino.h"
|
||||
#endif
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#define SPIFLASH_WRITEENABLE 0x06 //!< write enable
|
||||
#define SPIFLASH_WRITEDISABLE 0x04 //!< write disable
|
||||
|
||||
#define SPIFLASH_BLOCKERASE_4K 0x20 //!< erase one 4K block of flash memory
|
||||
#define SPIFLASH_BLOCKERASE_32K 0x52 //!< erase one 32K block of flash memory
|
||||
#define SPIFLASH_BLOCKERASE_64K 0xD8 //!< erase one 64K block of flash memory
|
||||
#define SPIFLASH_CHIPERASE 0x60 //!< @brief chip erase (may take several seconds depending on size)
|
||||
//!< Chip is erased but not actually waited for completion (instead need to check the status register BUSY bit)
|
||||
#define SPIFLASH_STATUSREAD 0x05 //!< read status register
|
||||
#define SPIFLASH_STATUSWRITE 0x01 //!< write status register
|
||||
#define SPIFLASH_ARRAYREAD 0x0B //!< read array (fast, need to add 1 dummy byte after 3 address bytes)
|
||||
#define SPIFLASH_ARRAYREADLOWFREQ 0x03 //!< read array (low frequency)
|
||||
|
||||
#define SPIFLASH_SLEEP 0xB9 //!< deep power down
|
||||
#define SPIFLASH_WAKE 0xAB //!< deep power wake up
|
||||
#define SPIFLASH_BYTEPAGEPROGRAM 0x02 //!< write (1 to 256bytes). Writing more than one Byte is not supported on all devices (e.g. SST25 Series)
|
||||
#define SPIFLASH_AAIWORDPROGRAM 0xAD //!< @brief Auto Address Increment Programming on Microchip SST Family Devices which do not support page program. <BR>
|
||||
//!< Use define #MY_SPIFLASH_SST25TYPE to use AAI prog instead of Bytepageprogram which does not work on SST Family Chips
|
||||
//!< tested with SST25PF020B80 http://ww1.microchip.com/downloads/en/DeviceDoc/20005135B.pdf
|
||||
#define SPIFLASH_IDREAD 0x9F //!< @brief read JEDEC manufacturer and device ID (2 bytes, specific bytes for each manufacturer and device)
|
||||
//!< Example for Atmel-Adesto 4Mbit AT25DF041A: 0x1F44 (page 27: http://www.adestotech.com/sites/default/files/datasheets/doc3668.pdf)
|
||||
//!< Example for Winbond 4Mbit W25X40CL: 0xEF30 (page 14: http://www.winbond.com/NR/rdonlyres/6E25084C-0BFE-4B25-903D-AE10221A0929/0/W25X40CL.pdf)
|
||||
#define SPIFLASH_MACREAD 0x4B //!< read unique ID number (MAC)
|
||||
|
||||
///
|
||||
/// @def MY_SPIFLASH_SST25TYPE
|
||||
/// @brief If set AAI Word Programming is used to support SST25 Family SPI Flash.
|
||||
///
|
||||
/// SST25 Family Flash does not support programming multiple Bytes with opcode 0x02 #SPIFLASH_BYTEPAGEPROGRAM. <BR>
|
||||
/// If SPIFLASH_SST25TYPE is set and writeBytes is called, it will use opcode 0xAD #SPIFLASH_AAIWORDPROGRAM and care for Byte alignment.<BR>
|
||||
/// Note: AAI Wordprogramming is independent of Pages, so pagebreaking is not an issue when using AAI Wordprogramming.
|
||||
///
|
||||
#ifdef DOXYGEN //needed to tell doxygen not to ignore the define which is actually made somewhere else
|
||||
#define MY_SPIFLASH_SST25TYPE
|
||||
#endif
|
||||
|
||||
/** SPIFlash class */
|
||||
class SPIFlash {
|
||||
public:
|
||||
static uint8_t UNIQUEID[8]; //!< Storage for unique identifier
|
||||
SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID=0); //!< Constructor
|
||||
boolean initialize(); //!< setup SPI, read device ID etc...
|
||||
void command(uint8_t cmd, boolean isWrite=false); //!< Send a command to the flash chip, pass TRUE for isWrite when its a write command
|
||||
uint8_t readStatus(); //!< return the STATUS register
|
||||
uint8_t readByte(uint32_t addr); //!< read 1 byte from flash memory
|
||||
void readBytes(uint32_t addr, void* buf, uint16_t len); //!< read unlimited # of bytes
|
||||
void writeByte(uint32_t addr, uint8_t byt); //!< Write 1 byte to flash memory
|
||||
void writeBytes(uint32_t addr, const void* buf, uint16_t len); //!< write multiple bytes to flash memory (up to 64K), if define SPIFLASH_SST25TYPE is set AAI Word Programming will be used
|
||||
boolean busy(); //!< check if the chip is busy erasing/writing
|
||||
void chipErase(); //!< erase entire flash memory array
|
||||
void blockErase4K(uint32_t address); //!< erase a 4Kbyte block
|
||||
void blockErase32K(uint32_t address); //!< erase a 32Kbyte block
|
||||
uint16_t readDeviceId(); //!< Get the manufacturer and device ID bytes (as a short word)
|
||||
uint8_t* readUniqueId(); //!< Get the 64 bit unique identifier, stores it in @ref UNIQUEID[8]
|
||||
|
||||
void sleep(); //!< Put device to sleep
|
||||
void wakeup(); //!< Wake device
|
||||
void end(); //!< end
|
||||
protected:
|
||||
void select(); //!< select
|
||||
void unselect(); //!< unselect
|
||||
uint8_t _slaveSelectPin; //!< Slave select pin
|
||||
uint16_t _jedecID; //!< JEDEC ID
|
||||
uint8_t _SPCR; //!< SPCR
|
||||
uint8_t _SPSR; //!< SPSR
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
SPISettings _settings;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
2.4
|
||||
* Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely
|
||||
whilst waiting for inbound data
|
||||
* Fixed return code when publishing >256 bytes
|
||||
|
||||
2.3
|
||||
* Add publish(topic,payload,retained) function
|
||||
|
||||
2.2
|
||||
* Change code layout to match Arduino Library reqs
|
||||
|
||||
2.1
|
||||
* Add MAX_TRANSFER_SIZE def to chunk messages if needed
|
||||
* Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE
|
||||
|
||||
2.0
|
||||
* Add (and default to) MQTT 3.1.1 support
|
||||
* Fix PROGMEM handling for Intel Galileo/ESP8266
|
||||
* Add overloaded constructors for convenience
|
||||
* Add chainable setters for server/callback/client/stream
|
||||
* Add state function to return connack return code
|
||||
|
||||
1.9
|
||||
* Do not split MQTT packets over multiple calls to _client->write()
|
||||
* API change: All constructors now require an instance of Client
|
||||
to be passed in.
|
||||
* Fixed example to match 1.8 api changes - dpslwk
|
||||
* Added username/password support - WilHall
|
||||
* Added publish_P - publishes messages from PROGMEM - jobytaffey
|
||||
|
||||
1.8
|
||||
* KeepAlive interval is configurable in PubSubClient.h
|
||||
* Maximum packet size is configurable in PubSubClient.h
|
||||
* API change: Return boolean rather than int from various functions
|
||||
* API change: Length parameter in message callback changed
|
||||
from int to unsigned int
|
||||
* Various internal tidy-ups around types
|
||||
1.7
|
||||
* Improved keepalive handling
|
||||
* Updated to the Arduino-1.0 API
|
||||
1.6
|
||||
* Added the ability to publish a retained message
|
||||
|
||||
1.5
|
||||
* Added default constructor
|
||||
* Fixed compile error when used with arduino-0021 or later
|
||||
|
||||
1.4
|
||||
* Fixed connection lost handling
|
||||
|
||||
1.3
|
||||
* Fixed packet reading bug in PubSubClient.readPacket
|
||||
|
||||
1.2
|
||||
* Fixed compile error when used with arduino-0016 or later
|
||||
|
||||
|
||||
1.1
|
||||
* Reduced size of library
|
||||
* Added support for Will messages
|
||||
* Clarified licensing - see LICENSE.txt
|
||||
|
||||
|
||||
1.0
|
||||
* Only Quality of Service (QOS) 0 messaging is supported
|
||||
* The maximum message size, including header, is 128 bytes
|
||||
* The keepalive interval is set to 30 seconds
|
||||
* No support for Will messages
|
||||
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2008-2015 Nicholas O'Leary
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Arduino Client for MQTT
|
||||
|
||||
This library provides a client for doing simple publish/subscribe messaging with
|
||||
a server that supports MQTT.
|
||||
|
||||
## Examples
|
||||
|
||||
The library comes with a number of example sketches. See File > Examples > PubSubClient
|
||||
within the Arduino application.
|
||||
|
||||
Full API documentation is available here: http://pubsubclient.knolleary.net
|
||||
|
||||
## Limitations
|
||||
|
||||
- It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.
|
||||
- The maximum message size, including header, is **128 bytes** by default. This
|
||||
is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h`.
|
||||
- The keepalive interval is set to 15 seconds by default. This is configurable
|
||||
via `MQTT_KEEPALIVE` in `PubSubClient.h`.
|
||||
- The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by
|
||||
changing value of `MQTT_VERSION` in `PubSubClient.h`.
|
||||
|
||||
|
||||
## Compatible Hardware
|
||||
|
||||
The library uses the Arduino Ethernet Client api for interacting with the
|
||||
underlying network hardware. This means it Just Works with a growing number of
|
||||
boards and shields, including:
|
||||
|
||||
- Arduino Ethernet
|
||||
- Arduino Ethernet Shield
|
||||
- Arduino YUN – use the included `YunClient` in place of `EthernetClient`, and
|
||||
be sure to do a `Bridge.begin()` first
|
||||
- Arduino WiFi Shield - if you want to send packets > 90 bytes with this shield,
|
||||
enable the `MQTT_MAX_TRANSFER_SIZE` define in `PubSubClient.h`.
|
||||
- Sparkfun WiFly Shield – [library](https://github.com/dpslwk/WiFly)
|
||||
- TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library)
|
||||
- Intel Galileo/Edison
|
||||
- ESP8266
|
||||
|
||||
The library cannot currently be used with hardware based on the ENC28J60 chip –
|
||||
such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an
|
||||
[alternative library](https://github.com/njh/NanodeMQTT) available.
|
||||
|
||||
## License
|
||||
|
||||
This code is released under the MIT License.
|
||||
Reference in New Issue
Block a user