2015に発表されたATmega328Pの後継モデルであるATmega328PBは、さらに多くの機能とより多くのピン配置オプションもっていてATmega328Pと完全互換です。特に、2つのSPIインターフェース、2つのI2Cインターフェース、3つのタイマーとその他の機能強化が含まれていて旧モデルに加えて使い勝手がよさそうです。今日はこのATmega328PBについてATmega328Pと比較しながら評価していきます。
328Pと328PBの基本仕様の比較
まず基本的な仕様部分においてATmega328PBとATmega328Pを比較してみました。
機能 / 特徴 | ATmega328P | ATmega328PB |
---|---|---|
ピン数(DIPパッケージ) | 28ピン | 32ピン |
CPU | 8-bit AVR | 8-bit AVR |
動作周波数 | 最大 20MHz | 最大 20MHz |
フラッシュメモリ | 32KB | 32KB |
SRAM | 2KB | 2KB |
EEPROM | 1KB | 1KB |
UART | 1 | 2 |
SPI | 1 | 2 |
I2C (TWI) | 1 | 2 |
タイマー | 3 (2x 8-bit, 1x 16-bit) | 3 (2x 8-bit, 1x 16-bit) |
アナログ比較器 | 1 | 1 |
PWMチャンネル | 6 | 8 |
アナログ入力 (ADCチャンネル) | 6 | 8 |
外部割り込み | 2 | 3 |
プログラム可能なゲイン増幅器 | なし | 4 |
タッチセンシング | なし | 16チャンネル QTouch® |
- ピン数
ATmega328PBは4ピン多くなることで、より多くの機能を搭載しています。 - UART, SPI, I2C (TWI)
通信インターフェースを倍増しており、複数のデバイスとの通信が可能になっています。 - PWMチャンネルとアナログ入力
これらのチャンネルが増えており、より複雑なアナログ処理や精密なPWM制御が可能です。 - プログラム可能なゲイン増幅器とタッチセンシング
新たに追加された機能です。
ピン機能比較表
変更されたピンを表示します。
ピン番号 | ATmega328Pの機能 | ATmega328PBの機能 |
---|---|---|
1 | PC6/RESET | PC6/RESET |
2 | PD0/RXD | PD0/RXD |
3 | PD1/TXD | PD1/TXD |
4 | PD2/INT0 | PD2/INT0 |
5 | PD3/INT1/OC2B | PD3/INT1/OC2B |
6 | PD4/XCK/T0 | PD4/XCK/T0 |
7 | VCC | VCC |
8 | GND | GND |
9 | PB6/XTAL1/TOSC1 | PB6/XTAL1/TOSC1 |
10 | PB7/XTAL2/TOSC2 | PB7/XTAL2/TOSC2 |
11 | PD5/T1/OC0B | PD5/T1/OC0B |
12 | PD6/AIN0/OC0A | PD6/AIN0/OC0A |
13 | PD7/AIN1 | PD7/AIN1 |
14 | PB0/CLKO/ICP1 | PB0/CLKO/ICP1 |
15 | PB1/OC1A | PB1/OC1A |
16 | PB2/SS/OC1B | PB2/SS/OC1B |
17 | PB3/MOSI/OC2A | PB3/MOSI/OC2A |
18 | PB4/MISO | PB4/MISO |
19 | PB5/SCK | PB5/SCK |
20 | AVCC | AVCC |
21 | AREF | AREF |
22 | GND | GND |
23 | PC0/ADC0 | PC0/ADC0 |
24 | PC1/ADC1 | PC1/ADC1 |
25 | PC2/ADC2 | PC2/ADC2 |
26 | PC3/ADC3 | PC3/ADC3 |
27 | PC4/SDA/ADC4 | PC4/SDA/ADC4 |
28 | PC5/SCL/ADC5 | PC5/SCL/ADC5 |
29 | – | PE0/RXD0 (USART0) |
30 | – | PE1/TXD0 (USART0) |
31 | – | PE2/XCK0 (USART0) |
32 | – | PE3/OC0B (Timer0) |
ATmega328PBを使う上での注意点
ATmega328Pの電源ピンがIOピンに変更
ATnega328PではGND(ピン番号3)、Vcc(ピン番号6)がATmega328PBではPE0とPE1に変更されています。よってATmega328P用に設計した基板でATmega328PBをのせる場合はPE0,1の出力設定でPE0をHIGH, PE1をLOWにした場合マイコンが破損する可能性があります。
Timer3とTimer4に注意
ATmega328PB における Timer3 と Timer4 は、標準的な ATmega328P には存在しない追加のタイマー機能であり、これらを使用する際には特定の注意点があります。これらのタイマーを効果的に利用するためには、それぞれの特性と機能を理解し、適切な設定を行う必要があります。
Timer3
16ビットのタイマーで、高精度のタイムイベント生成やPWM出力、周期測定などに使用されます。
注意点:
①Timer3 は独自の割り込みベクターを持っており、これを適切に設定しないと予期せぬ動作が発生する可能性があります。
②Timer3 を使用する場合、他の機能やタイマーとのリソース競合を避けるため、割り込み優先度やタイマーの設定に注意する必要があります。
Timer4
Timer4 は 10ビットの高分解能タイマーで、より精密なPWM制御や高速タイミングが求められる用途に適しています。
注意点:
①Timer4 は 10ビットの解像度を持っており、設定ミスが高精度制御に影響を与えるため、設定値の計算には特に注意が必要です。
②Timer4 には独自の機能(例えば高分解能PWM)があり、これを活用するには専門的な知識が必要になることがあります。
TIMER3とTIMER4ポート設定に注意
Timer3 コンペアBとTimer4コンペアBがPD2に繋がっており、PORTB7(PB7)の状態が1の時にOR 0の時にANDとなるため、PWMピンとして使いたい場合はTimer3か4かのどちらかを使いPB7(PORTB bit7)を1にしなくてはなりません。
追加された機能について
2つのUARTで同時に複数のシリアルデバイスと通信が可能
追加のUARTを利用するサンプルプログラムを例に示します。
/*
このサンプルでは、ATmega328PBの2つのUARTインターフェースを使用して、
2つの異なるシリアルデバイスと通信します。1つはデバッグメッセージ用、
もう1つは外部モジュール(例えばGPSモジュール)との通信用です。
*/
#include <avr/io.h>
#include <util/delay.h>
void USART0_init() {
// USART0を9600 bpsで初期化
UBRR0 = 103; // ボーレート9600の設定
UCSR0B = (1 << RXEN0) | (1 << TXEN0); // 受信と送信を有効化
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8-bitデータ
}
void USART1_init() {
// USART1を9600 bpsで初期化
UBRR1 = 103; // ボーレート9600の設定
UCSR1B = (1 << RXEN1) | (1 << TXEN1); // 受信と送信を有効化
UCSR1C = (1 << UCSZ11) | (1 << UCSZ10); // 8-bitデータ
}
void USART0_send(char data) {
while (!(UCSR0A & (1 << UDRE0))); // 送信バッファが空になるのを待つ
UDR0 = data;
}
void USART1_send(char data) {
while (!(UCSR1A & (1 << UDRE1))); // 送信バッファが空になるのを待つ
UDR1 = data;
}
int main(void) {
USART0_init(); // USART0の初期化
USART1_init(); // USART1の初期化
while (1) {
USART0_send('A'); // USART0を通じて文字を送信
USART1_send('B'); // USART1を通じて文字を送信
_delay_ms(1000); // 1秒待機
}
}
2つのSPIとI2Cインターフェース
SPI通信のサンプルコード
/*
SPI1インターフェースを使用して外部のSPIデバイス(例えばEEPROM)と通信します
*/
#include <avr/io.h>
#include <util/delay.h>
// SPIの初期化
void SPI1_init() {
DDRB |= (1 << PB2) | (1 << PB3) | (1 << PB1); // SS, MOSI, SCKを出力に設定
SPCR1 = (1 << SPE1) | (1 << MSTR1); // SPIをマスターモードで有効にする
}
// SPI通信関数
void SPI1_transmit(char data) {
SPDR1 = data; // データをSPIデータレジスタに書き込む
while (!(SPSR1 & (1 << SPIF1))); // 送信完了を待つ
}
int main(void) {
SPI1_init(); // SPIの初期化
while (1) {
SPI1_transmit(0xAA); // データ0xAAを送信
_delay_ms(1000);
}
}
I2C通信のサンプルコード
/*
I2C1インターフェースを使用してI2Cセンサー(例えば温度センサー)と通信します。
*/
#include <avr/io.h>
#include <util/twi.h>
#define F_SCL 100000UL // SCLクロック周波数(100kHz)
#define TWI1_ADDRESS 0x5A // I2Cデバイスアドレス
void I2C1_init() {
TWSR1 = 0x00; // プリスケーラ値を0に設定
TWBR1 = ((F_CPU / F_SCL) - 16) / 2; // TWIビットレートレジスタを設定
}
void I2C1_start() {
TWCR1 = (1 << TWINT1) | (1 << TWSTA1) | (1 << TWEN1);
while (!(TWCR1 & (1 << TWINT1))); // スタートコンディションの送信完了を待つ
}
void I2C1_stop() {
TWCR1 = (1 << TWINT1) | (1 << TWSTO1) | (1 << TWEN1);
}
void I2C1_write(uint8_t data) {
TWDR1 = data; // データをTWIデータレジスタに書き込む
TWCR1 = (1 << TWINT1) | (1 << TWEN1);
while (!(TWCR1 & (1 << TWINT1))); // データ送信完了を待つ
}
int main(void) {
I2C1_init(); // I2Cの初期化
while (1) {
I2C1_start();
I2C1_write(TWI1_ADDRESS << 1); // デバイスアドレスの書き込み(書き込みモード)
I2C1_write(0x01); // レジスタアドレスを書き込む
I2C1_stop();
_delay_ms(1000);
}
}
タッチセンシングインターフェース
組み込みのタッチセンサー機能をサポートしており、より簡単にタッチベースのインターフェースを実装できます。この機能を活用することで、ボタン、スライダー、ホイールなどのタッチ入力デバイスを作成できます。
単一のタッチチャンネルでのタッチ検出プログラム
#include <avr/io.h>
#include <util/delay.h>
#include "qtouch.h"
#define TOUCH_SENSOR_PIN 0 // A0ピンをタッチセンサとして使用
#define LED_PIN 2 // ピン2にLED接続
int main(void) {
// ピン設定
DDRD |= (1 << LED_PIN); // LEDピンを出力として設定
PORTD &= ~(1 << LED_PIN); // LEDをオフに
// QTouchライブラリの初期化
QTouch_Init(TOUCH_SENSOR_PIN);
while (1) {
if (QTouch_Sense(TOUCH_SENSOR_PIN)) {
PORTD |= (1 << LED_PIN); // タッチが検出されたらLEDをオン
} else {
PORTD &= ~(1 << LED_PIN); // タッチが検出されなければLEDをオフ
}
_delay_ms(100); // 状態チェック間隔
}
}
※QTouchライブラリが必要です
※AVR GCC環境ではQTouchライブラリを手動でセットアップすることが必要です。
より多くのPWM出力と高解像度のタイマー
ATmega328PBは、より多くのPWM出力と高解像度のタイマーを提供します。
これにより、より精密なモータ制御やLEDの調光が可能になります。
高解像度のタイマーでのPWM制御サンプル
/*
高解像度のタイマーを使用してLEDの明るさを滑らかに変化させるPWM制御
*/#include <avr/io.h>
#include <util/delay.h>
void PWM_Init() {
// タイマー1を高解像度PWMモードで設定
TCCR1A = (1 << WGM11) | (1 << COM1A1); // フェーズ正弦波PWMモード, 10ビット高速PWM
TCCR1B = (1 << WGM12) | (1 << WGM13) | (1 << CS10); // プリスケーラなし
ICR1 = 1023; // TOP値を1023に設定 (10-bit resolution)
DDRB |= (1 << PB1); // PB1ピンを出力に設定 (OC1A)
}
void SetBrightness(uint16_t brightness) {
if (brightness > 1023) {
brightness = 1023;
}
OCR1A = brightness; // OC1Aの出力を設定, LEDの明るさ調整
}
int main(void) {
PWM_Init(); // PWMの初期化
while (1) {
// 明るさを徐々に増加
for (uint16_t i = 0; i < 1024; i++) {
SetBrightness(i);
_delay_ms(2);
}
// 明るさを徐々に減少
for (uint16_t i = 1023; i > 0; i--) {
SetBrightness(i);
_delay_ms(2);
}
}
}
- タイマー設定
TCCR1A と TCCR1B レジスタを設定して、タイマー1を10ビットの高速PWMモードに設定します。WGM11, WGM12, WGM13 を設定することでフェーズ正弦波PWMを有効にし、COM1A1 でOC1Aピン(PB1)の出力を非反転モードに設定します。 - プリスケーラ
プリスケーラを無しに設定しています(CS10)。これにより、タイマーのカウント速度が最大になり、より滑らかなPWM波形が得られます。 - 明るさの設定
SetBrightness 関数では、OCR1Aレジスタに値を設定してPWMのデューティサイクルを制御します。この値を変更することで、接続されたLEDの明るさを変えることができます。 - ループ動作
メインループではLEDの明るさを徐々に上げた後、下げる動作を繰り返します。これによりLEDが滑らかに明るくなり、暗くなります。