前提:
シリアル入力からASCIIコード数字でYYMMDDWの順でデータが流し込まれていること。(Serial.available()等で全てのデータが受信済みであることを確認するとか、ある一定時間内にデータが流し込まれる手順が保証されている等。Serial.read()は受信データがないと-1を返して来ますから)
ASCIIコードは、数字'0'に対し数値0x30(十進数では48, 二進数では0b00110000)、'1'に対し0x31(49,0b00110001)...'9'に対し0x39(57,0b00111001)が対応しています。
送られてきた数値48~57を元の数字が表す数値0~9に変換するには...
普通は'0'に対応した値を引くという処理をする(48-48=0 ~ 57-48=9)ことが多いのですが、
そのサンプルでは下位4bit(16進数で言えば下位1桁)を取り出す、という考え方をしたようです。
&はビット論理演算積(and演算)の演算子です。まぁ、記号って検索するのは難しいですけど、C/C++の入門書の一冊も読み通せば出てきていると思うので確認しておいてください。
二進数で表記したとき、二つの数の各桁の両方が1になっていればその桁に1を出力、そうでなければその桁を0を出力するという働きを持ちます。そうすると、0xfとandを取るということは
'0' & 0xf = 0b00110000 & 0b00001111 同じ桁で両方1のビットは無いので 結果は0
'1' & 0xf = 0b00110001 & 0b00001111 最下位が両方1 結果は0b00000001 (つまり1)
'2' & 0xf = 0b00110010 & 0b00001111 結果は0b00000010つまり2
<略>
'9' & 0xf = 0b00111001 & 0b00001111 結果は0b00001001つまり9
(andの演算はbyteデータに対してもint幅に拡張して行われますが、この場合は特に気にしなくてもいいでしょう。符号付き整数ではたまにハマることがありますがそれは別の話)
これで送られてきた'0'~'9'の「数字」から「数値」0~9を取り出すことが出来ました。
曜日は、元データが'0'~'6'の可能性しか無いので、別に0xfとのandでもよかったのでしょうがなんとなく(?)7=0b00000111とのandをとることにしたのでしょう。'0'~'6'ではbit3が1になることはないので0xfとandをとっても0x7とandをとっても同じことになります。
このように&演算で特定のビットだけを取り出すこと(言い方を変えれば特定のビットを0にすること)を(ビット)マスクと言ったりもします。マイコンをいじる人なら覚えておくべき言葉かと思います。
さて。DS3107データシート 8ページを見ると、設定すべき値の形式がわかります。
8bitデータの上位に日/月/年の10の桁が入るような形式になっていますね(二進化十進数:BCDってやつ)。
ここで、<<はビット左シフトの演算子。二進数の各桁を指定数だけ左にシフトします。
例えば、1 << 3は 0b00000001を3ビット左シフトして0b00001000が得られます。
(「はみ出した」分はなくなってしまいます。0b1111 0000 0000 0001 << 2 をintの範囲で演算すれば 0b1100 0000 0000 0100)
今回は、例えば'1'を4ビット左シフトしてbyte型の変数に格納するなら
0b00110001 << 4 = 0b001100010000 この下位8bitが変数に入るので
0b00010000(十六進では0x10)が得られます。ちなみに、二進数を一桁左シフトすることは2を掛けることと同義ですから、0b00110001 << 4は0b00110001 * 2 * 2 * 2 * 2と同じ結果になります。つまり、
0b00110001 << 4 は 0b00110001 * 16 と置き換え可能で、そういう表記をすることも時々ある、というのは覚えておいて損はないと思います。(単純に考えれば、シフト演算の方が簡単なので速度的には有利です。ただし、コンパイラもお利口なので、*16を見ると<<4に置き換えてたりすることもあるようです)
余談ですが、<<は優先度の低い演算子なので、
0b00110001 << 4 + 1 とすると4+1が優先的に行われて0b00100000になります。1+0b00110001<<4では先に0b00110001+1が計算されて0b00110010 << 4の演算になります。
'10'に1を足して11を求めるイメージのときには(0b00110001 << 4 )+1としないといけません。これ、割とハマります。
以上より、
C++
1byte year=(Serial.read()<<4);
2year=year+(Serial.read()&0x0F);
に対し、シリアルからデータ'2''1'が流し込まれれば
byte year=(Serial.read()<<4);
でyearは0b00100000(0x20)になり
year=year+(Serial.read()&0x0F);
でyearに0b00100001(0x21)が求まり、これをRTCに流し込めばよい、ということになります。