ATmega328PBは、Atmel(現在はMicrochip Technologyによって買収)によって開発されたマイクロコントローラで、Arduino Unoなどの人気開発ボードに使われているATmega328Pの後継モデルです。このチップは2015年に発表され、さらに多くの機能とより多くのピン配置オプションを提供します。ATmega328PBはATmega328Pと完全互換ですが、2つのSPIインターフェース、2つのI2Cインターフェース、3つのタイマーとその他の機能強化が含まれています。発売当初の市場価格は、大量購入時に$1〜$2の範囲で、単品での購入価格は若干高めです。
328Pと328PBの比較表と特徴
機能 / 特徴 | 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® |
主な変更点
- ピン数: DIPパッケージにおいては、ATmega328PBは4ピン多くなることで、より多くの機能を搭載しています。
- UART, SPI, I2C (TWI): ATmega328PBはこれらの通信インターフェースを倍増しており、複数のデバイスとの通信が可能になっています。
- PWMチャンネルとアナログ入力: ATmega328PBはこれらのチャンネルが増えており、より複雑なアナログ処理や精密な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
- 機能と用途: Timer3 は 16ビットのタイマーで、高精度のタイムイベント生成や、PWM出力、周期測定などに使用されます。
- 注意点:
- 割り込みベクター: Timer3 は独自の割り込みベクターを持っており、これを適切に設定しないと予期せぬ動作が発生する可能性があります。
- リソースの競合: Timer3 を使用する場合、他の機能やタイマーとのリソース競合を避けるため、割り込み優先度やタイマーの設定に注意する必要があります。
Timer4
- 機能と用途: Timer4 は 10ビットの高分解能タイマーで、より精密なPWM制御や高速タイミングが求められる用途に適しています。
- 注意点:
- 解像度と設定: Timer4 は 10ビットの解像度を持っており、設定ミスが高精度制御に影響を与えるため、設定値の計算には特に注意が必要です。
- 特殊な機能: Timer4 には独自の機能(例えば高分解能PWM)があり、これを活用するには専門的な知識が必要になることがあります。
ポート設定に注意
Timer3 コンペアBとTimer4コンペアBがPD2に繋がっており、PORTB7(PB7)の状態が1の時にOR 0の時にANDとなるため、PWMピンとして使いたい場合はTimer3か4かのどちらかを使いPB7(PORTB bit7)を1にしなくてはなりません。
ATmega328PBの新機能
追加のUART
ATmega328PBは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秒待機
}
}
追加のSPIとI2Cインターフェース
2つのSPIと2つの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ライブラリを含める必要があります。Arduino IDEでは通常、追加のライブラリをインストールする必要がありますが、AVR GCC環境ではQTouchライブラリを手動でセットアップすることが一般的です。
より多くのPWM出力と高解像度のタイマー
ATmega328PBは、改善されたPWM機能を持ち、より多くのPWM出力と高解像度のタイマーを提供します。これにより、より精密なモータ制御やLEDの調光が可能になります。以下は、ATmega328PBのこれらの機能を活用するためのサンプルコードです。この例では、高解像度のタイマーを使用して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が滑らかに明るくなり、暗くなります。