ATmega328PBとATmega328Pの違い・比較

全般

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ではPE0PE1に変更されています。よってATmega328P用に設計した基板でATmega328PBをのせる場合はPE0,1の出力設定でPE0HIGH, PE1LOWにした場合マイコンが破損する可能性があります。

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が滑らかに明るくなり、暗くなります。
タイトルとURLをコピーしました