
写真1 AD9833 DDS MSOP 0.5ピッチ 変換基板に乗せる
それはさておき、
このチップは10ピンのMSOPで、写真にようにピンセットの先ほどの大きさです。
変換基板に取り付けましたが、ピンのピッチは0.5なので、半田付けにはちょっと技術が必要です。
まずチップをゲルタイプの瞬間接着剤で正確な位置に仮止めしておきます。
フラックスをほんのわずか塗って、すべてのピンにわざとまたがるくらい半田を盛ります。
半田吸い取り線で、余分な半田を吸い取って、出来上がり。
慣れれば簡単ですが、最初は数個パーにする覚悟がいります(笑)。
このDDSは、マスタクロック(MCLK)周波数は最高25MHzで、その場合最高発振周波数(ナイキスト周波数)は12.5MHzとなり、分解能は0.1Hzです。
今回は手持ち部品の関係で、20MHzのクリスタルを使用したので、10MHzまでのプログラマブルオシレータとして、実験してみました。この場合の分解能は約0.075Hzです。
STM32F3 DiscoveryにI2C LCDをつなぐ
投稿日 2014/09/13
STM32F3 Discoveryは1250円くらいと安く購入できますが、なんとSTM32 F3 MCUのほかに加速度センサー + 磁気センサー、角加速度(ジャイロ)センサーが搭載された優れものです。
購入時点では回転するLEDイルミネーション、加速度計、コンパスのデモプログラムが書き込まれており、電源をつなぐだけで楽しめるようになっています。

STM32 F3 Discovery
きれいなLEDイルミネーションデモ
そんな多機能なSTM32F3 Discoveryを活用しない手はありません。
今後加速度センサーやジャイロセンサーを使用して何か作るとして、まずはデバッグやデータ表示等のためにキャラクタLCDをつないでおくことにしました。
つなぐLCDはI2C LCDです。
STM32F3 Discoveryでは2つのI2Cインターフェース(I2C1とI2C2)が使えますが、加速度センサーがすでにI2C1を使用しています。(ジャイロセンサーはSPIを使用。)
I2C LCDもI2C1につないでもよいのですが、今回はI2C2につないでみました。
プログラムはSTM32F3 Discoveryのデモプログラムを改造して作ります。改造というか、I2C LCDの追加です。
I2C LCDの接続
使用したI2C LCDは秋月電子等で扱っているACM1602NI-FLWです。このLCDはI2C化するために裏面にPICが使用されていますが、反応が遅いのであまり評判のよくないLCDです。
LCDの電源はSTM32F3 Discoveryから3.3Vを供給しておきます。バックライトもつないでおきます。コントラスト調整の半固定抵抗もつないでおきます。
STM32F3 DiscoveryのI2C2の信号のうちクロック(I2C2_SCL)はPA9, データ(I2C2_SDA)はPA10に出ています。PA9はP2のピン44、PA10はP2のピン43です。これらをLCDのSCLとSDAにつなぎ、2.4KΩ程度で3.3Vにプルアップしておきます。

STM32F3DiscoveryにI2C LCD ACM1602NI-FLWを接続
開発環境を用意して、プログラムをオリジナルのままコンパイルしてみる
STM社のホームページからファームウェアパッケージSTM32F3 Discovery kit firmware packageをダウンロードします。これにデモプログラムも含まれています。(マニュアル類もこのページにあります)
ファームウェアパッケージのフォルダ構造は以下のようになっています。
STM32F3-Discovery_FW_V1.1.0
_htmresc <- STM社のロゴ
Libraries
CMSIS <- CMSISライブラリ
STM32_USB-FS-Device_Driver <- USBドライバ
STM32F30x_StdPeriph_Driver <- MCUが搭載するUSB以外の周辺のドライバ
Project
Demonstration <-デモプログラム(これを改造)
Master_Workspace <- 開発環境用プロジェクト・プロトタイプ
Peripheral_Examples <- 周辺機能の各種サンプル・プログラム
Utilities
STM32F3_Discovery <- F3 Discovery固有のデバイス用
MCD-ST Liberty SW License Agreement V2.pdf
Release_Notes.html
開発環境(ツールチェイン)は、私の場合Keil uVision V.5を使用しました。このほかのEWARMなどの開発用プロジェクトファイルが用意されているので好きな開発環境を選びます。
とりあえず開発環境がうまく動くことを確認するため、デモプログラムをオリジナルのまま、コンパイルし、ダウンロードして実行まで試しておきます。
uVisionを起動してデモプログラムのプロジェクトを読み込んだ後、メニューProjectのOption for target "demo"をクリックして表示します。
DeviceがSTM32F303VCになっていることを確認します。
もしSTM32F303VCになっておらず、一覧からも選べない場合は、ProjectのManageをクリックし、Pack Installerを起動してMCUパッケージをインストールしなければなりません。
DebugとUtilitiesがST-LINK Debuggerになっていることを確認します。
Settingsをクリックし、Falsh DownloadにSTM32F3xx Flashが無い場合は、Addしておきます。
プログラムはデモプログラムを改造
デモプログラムは、加速度センサーLSM303DLHCをI2C1でコントロールしていますので、I2Cの使い方として参考になります。Utilitiesフォルダの中のSTM32F3_Discoveryフォルダのstm32f3discovery_lsm303dlhc_i2c.cとstm32f3discovery_lsm303dlhc_i2c.hを同じutilitiesフォルダにコピーし、stm32f3discovery_ACM1602NI_i2c.cとstm32f3discovery_ACM1602NI_i2c.hとしておきます。
uVisionを起動してデモプログラムのプロジェクトを読み込み、上記2つのファイルをstm32f3discoveryのソースファイルに追加しておきます。
追加したらstm32f3discovery_ACM1602NI_i2c.cを右クリックし、Translate ...をクリックして、ヘッダファイルを展開しておきます。(やらなくても大丈夫ですが)
I2Cの使用手順概要
まずGPIOとI2C2がどのペリフェラルバスにつながっているか確認します。ペリフェラルバスにはクロックを供給しないと動きません。STM32F3の場合は、GPIOがAHBに、I2C2はAPB1につながっています。
/* Enable the I2C periph */
RCC_APB1PeriphClockCmd(ACM1602NI_I2C_LCD_I2C_CLK, ENABLE);
/* Enable SCK and SDA GPIO clocks */
RCC_AHBPeriphClockCmd(ACM1602NI_I2C_LCD_I2C_SCK_GPIO_CLK | ACM1602NI_I2C_LCD_I2C_SDA_GPIO_CLK , ENABLE);
I2C2のストラクチャとGPIOのストラクチャを初期化します。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; <- オルタネートファンクションを使う
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; <- オープンドレイン
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; <- 内部プルアップしない
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/* I2C SCK pin configuration */
GPIO_InitStructure.GPIO_Pin = ACM1602NI_I2C_LCD_I2C_SCK_PIN;
GPIO_Init(ACM1602NI_I2C_LCD_I2C_SCK_GPIO_PORT, &GPIO_InitStructure);
/* I2C SDA pin configuration */
GPIO_InitStructure.GPIO_Pin = ACM1602NI_I2C_LCD_I2C_SDA_PIN;
GPIO_Init(ACM1602NI_I2C_LCD_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
前述のようにI2C2_SCLとI2C2_SDAはPA9とPA10に出ています。PAはGPIOAの意味です。PA9とPA10ピンの標準機能はGPIOですので、オルタネート機能のI2C2に切り替えておく必要があります。(GPIOからI2C2へオルタネート)
GPIO_PinAFConfig(ACM1602NI_I2C_LCD_I2C_SCK_GPIO_PORT, ACM1602NI_I2C_LCD_I2C_SCK_SOURCE, ACM1602NI_I2C_LCD_I2C_SCK_AF);
GPIO_PinAFConfig(ACM1602NI_I2C_LCD_I2C_SDA_GPIO_PORT, ACM1602NI_I2C_LCD_I2C_SDA_SOURCE, ACM1602NI_I2C_LCD_I2C_SDA_AF);
I2C2のストラクチャを設定し、I2C2を初期化します。
/* I2C configuration -------------------------------------------------------*/
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
I2C_InitStructure.I2C_DigitalFilter = 0x00;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
//I2C_InitStructure.I2C_Timing = 0x00902025; //CLK = 10uS(100KHz)
I2C_InitStructure.I2C_Timing = 0x009FFFFF; //CLK = 62uS(16.8KHz) <- ここでクロック(SCL)のスピードが決まる。(後述)
/* Apply I2C_LCD_I2C configuration after enabling it */
I2C_Init(ACM1602NI_I2C_LCD_I2C, &I2C_InitStructure); <- I2C2初期化
最後にI2C2をイネーブルします。
/* I2C_LCD_I2C Peripheral Enable */
I2C_Cmd(ACM1602NI_I2C_LCD_I2C, ENABLE);
デモプログラムを改造する
パラメータは、stm32f3discovery_ACM1602NI_i2c.hにセットしておきます。
ACM1602NIのI2Cスレーブアドレスは0xA0です。
はじめに書いたように使用したI2C LCD ACM1602NIは一癖あるので注意が必要です。制御が早いとアクノリッジが返らないのです。このためカットアンドトライが必要です。これには、delayを挟んだりする方法があるようですが、今回はI2Cのクロック(SCL)の周期を遅くする方法で対処しました。
//I2C_InitStructure.I2C_Timing = 0x00902025; //CLK = 10us(100KHz)
I2C_InitStructure.I2C_Timing = 0x009FFFFF; //CLK = 62us(16.8KHz)
100KHzで動くはずなのですが、遅くしないとアクノリッジが返りません。I2C_Timingの数値と周期の関係はよくわかりませんので、オシロで見ながら調整し、16.8KHzくらいで動きました。
ソースコード
main.cの一部
...
/* Initialize LEDs and User Button available on STM32F3-Discovery board */
STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDInit(LED4);
STM_EVAL_LEDInit(LED5);
STM_EVAL_LEDInit(LED6);
STM_EVAL_LEDInit(LED7);
STM_EVAL_LEDInit(LED8);
STM_EVAL_LEDInit(LED9);
STM_EVAL_LEDInit(LED10);
STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_EXTI);
ACM1602NI_I2C_LCD_Init();
ACM1602NI_I2C_LCD_write(0, 0, "STM32F3Discovery");
ACM1602NI_I2C_LCD_write(0, 1, "I2C LCD byJF1VRR");
/* Configure the USB */
//Demo_USB();
/* Reset UserButton_Pressed variable */
UserButtonPressed = 0x00;
...
stm32f3_discovery_ACM1602NI_i2c_lcd.h
#include "stm32f30x.h"
/* I2C LCD ADDRESS TABLE */
const int lcd_adrbase[2] = { 0x80, 0xC0 };
#define ACM1602NI_I2C_LCD_OK ((uint32_t) 0)
#define ACM1602NI_I2C_LCD_FAIL ((uint32_t) 0)
#define USE_DEFAULT_TIMEOUT_CALLBACK
#define ACM1602NI_I2C_LCD_FLAG_TIMEOUT ((uint32_t)0x1000)
#define ACM1602NI_I2C_LCD_LONG_TIMEOUT ((uint32_t)(10 * ACM1602NI_I2C_LCD_FLAG_TIMEOUT))
#define ACM1602NI_I2C_LCD_I2C I2C2
#define ACM1602NI_I2C_LCD_I2C_CLK RCC_APB1Periph_I2C2
#define ACM1602NI_I2C_LCD_I2C_SCK_PIN GPIO_Pin_9 /* PA.09 */
#define ACM1602NI_I2C_LCD_I2C_SCK_GPIO_PORT GPIOA /* GPIOA */
#define ACM1602NI_I2C_LCD_I2C_SCK_GPIO_CLK RCC_AHBPeriph_GPIOA
#define ACM1602NI_I2C_LCD_I2C_SCK_SOURCE GPIO_PinSource9
#define ACM1602NI_I2C_LCD_I2C_SCK_AF GPIO_AF_4
#define ACM1602NI_I2C_LCD_I2C_SDA_PIN GPIO_Pin_10 /* PA.10 */
#define ACM1602NI_I2C_LCD_I2C_SDA_GPIO_PORT GPIOA /* GPIOA */
#define ACM1602NI_I2C_LCD_I2C_SDA_GPIO_CLK RCC_AHBPeriph_GPIOA
#define ACM1602NI_I2C_LCD_I2C_SDA_SOURCE GPIO_PinSource10
#define ACM1602NI_I2C_LCD_I2C_SDA_AF GPIO_AF_4
#define ACM1602NI_I2C_LCD_I2C_ADDRESS 0xA0
uint16_t I2C_LCD_IOCtl(unsigned char command, unsigned char data);
void ACM1602NI_I2C_LCD_reset(void);
void ACM1602NI_I2C_LCD_position(unsigned char xpos, unsigned char ypos);
void ACM1602NI_I2C_LCD_write(unsigned char xpos, unsigned char ypos, char* ptr);
void ACM1602NI_I2C_LCD_clear(void);
void ACM1602NI_I2C_LCD_str(unsigned char* ptr);
uint32_t ACM1602NI_I2C_LCD_TIMEOUT_UserCallback(void);
stm32f3_discovery_ACM1602NI_i2c_lcd.c
#include "stm32f3_discovery_ACM1602NI_i2c_lcd.h"
#include "main.h"
__IO uint32_t ACM1602NI_I2C_LCD_Timeout = ACM1602NI_I2C_LCD_LONG_TIMEOUT;
static void ACM1602NI_I2C_LCD_LowLevel_Init(void);
void ACM1602NI_I2C_LCD_Init(void);
void ACM1602NI_I2C_LCD_Init(void)
{
/* Configure the low level interface --------------------------------------*/
ACM1602NI_I2C_LCD_LowLevel_Init();
/* Configure MEMS: data rate, power mode, full scale and axes */
ACM1602NI_I2C_LCD_reset();
}
uint16_t ACM1602NI_I2C_LCD_IOCtl(unsigned char command, unsigned char data)
{
/* Test on BUSY Flag */
ACM1602NI_I2C_LCD_Timeout = ACM1602NI_I2C_LCD_LONG_TIMEOUT;
while(I2C_GetFlagStatus(ACM1602NI_I2C_LCD_I2C, I2C_ISR_BUSY) != RESET)
{
if((ACM1602NI_I2C_LCD_Timeout--) == 0) return ACM1602NI_I2C_LCD_TIMEOUT_UserCallback();
}
/* Configure slave address, nbytes, reload, end mode and start or stop generation */
I2C_TransferHandling(ACM1602NI_I2C_LCD_I2C, ACM1602NI_I2C_LCD_I2C_ADDRESS, 1, I2C_Reload_Mode, I2C_Generate_Start_Write);
/* Wait until TXIS flag is set */
ACM1602NI_I2C_LCD_Timeout = ACM1602NI_I2C_LCD_LONG_TIMEOUT;
while(I2C_GetFlagStatus(ACM1602NI_I2C_LCD_I2C, I2C_ISR_TXIS) == RESET)
{
if((ACM1602NI_I2C_LCD_Timeout--) == 0) return ACM1602NI_I2C_LCD_TIMEOUT_UserCallback();
}
/* Send Register address */
I2C_SendData(ACM1602NI_I2C_LCD_I2C, (uint8_t) command);
/* Wait until TCR flag is set */
ACM1602NI_I2C_LCD_Timeout = ACM1602NI_I2C_LCD_LONG_TIMEOUT;
while(I2C_GetFlagStatus(ACM1602NI_I2C_LCD_I2C, I2C_ISR_TCR) == RESET)
{
if((ACM1602NI_I2C_LCD_Timeout--) == 0) return ACM1602NI_I2C_LCD_TIMEOUT_UserCallback();
}
/* Configure slave address, nbytes, reload, end mode and start or stop generation */
I2C_TransferHandling(ACM1602NI_I2C_LCD_I2C, ACM1602NI_I2C_LCD_I2C_ADDRESS, 1, I2C_SoftEnd_Mode, I2C_No_StartStop);
/* Wait until TXIS flag is set */
ACM1602NI_I2C_LCD_Timeout = ACM1602NI_I2C_LCD_LONG_TIMEOUT;
while(I2C_GetFlagStatus(ACM1602NI_I2C_LCD_I2C, I2C_ISR_TXIS) == RESET)
{
if((ACM1602NI_I2C_LCD_Timeout--) == 0) return ACM1602NI_I2C_LCD_TIMEOUT_UserCallback();
}
/* Write data to TXDR */
I2C_SendData(ACM1602NI_I2C_LCD_I2C, data);
/* Wait until STOPF flag is set */
ACM1602NI_I2C_LCD_Timeout = ACM1602NI_I2C_LCD_LONG_TIMEOUT;
while(I2C_GetFlagStatus(ACM1602NI_I2C_LCD_I2C, I2C_FLAG_TC) == RESET)
{
if((ACM1602NI_I2C_LCD_Timeout--) == 0) return ACM1602NI_I2C_LCD_TIMEOUT_UserCallback();
}
I2C_TransferHandling(ACM1602NI_I2C_LCD_I2C, ACM1602NI_I2C_LCD_I2C_ADDRESS, 0, I2C_SoftEnd_Mode, I2C_Generate_Stop);
/* Clear STOPF flag */
I2C_ClearFlag(ACM1602NI_I2C_LCD_I2C, I2C_ICR_STOPCF);
return ACM1602NI_I2C_LCD_OK;
}
//I2C LCD ACM1602N1
void ACM1602NI_I2C_LCD_reset(void)
{
Delay(100);
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x30);
Delay(5);
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x30);
Delay(1);
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x30);
Delay(1);
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x38);
Delay(1);
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x08);
Delay(1);
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x01);
Delay(1);
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x06);
Delay(1);
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x0C);
Delay(1);
}
void ACM1602NI_I2C_LCD_position(unsigned char xpos, unsigned char ypos)
{
ACM1602NI_I2C_LCD_IOCtl(0x00, lcd_adrbase[ypos] + xpos );
}
void ACM1602NI_I2C_LCD_write(unsigned char xpos, unsigned char ypos, char* ptr)
{
ACM1602NI_I2C_LCD_position(xpos, ypos);
while(*ptr != 0x00)
{
ACM1602NI_I2C_LCD_IOCtl(0x80, *ptr++);
}
}
void ACM1602NI_I2C_LCD_clear(void)
{
ACM1602NI_I2C_LCD_IOCtl(0x00, 0x01);
Delay(10);
}
void ACM1602NI_I2C_LCD_str(unsigned char* ptr)
{
while(*ptr != 0) ACM1602NI_I2C_LCD_IOCtl(0x80, *ptr++);
}
static void ACM1602NI_I2C_LCD_LowLevel_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
/* Enable the I2C periph */
RCC_APB1PeriphClockCmd(ACM1602NI_I2C_LCD_I2C_CLK, ENABLE);
/* Enable SCK and SDA GPIO clocks */
RCC_AHBPeriphClockCmd(ACM1602NI_I2C_LCD_I2C_SCK_GPIO_CLK | ACM1602NI_I2C_LCD_I2C_SDA_GPIO_CLK , ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/* I2C SCK pin configuration */
GPIO_InitStructure.GPIO_Pin = ACM1602NI_I2C_LCD_I2C_SCK_PIN;
GPIO_Init(ACM1602NI_I2C_LCD_I2C_SCK_GPIO_PORT, &GPIO_InitStructure);
/* I2C SDA pin configuration */
GPIO_InitStructure.GPIO_Pin = ACM1602NI_I2C_LCD_I2C_SDA_PIN;
GPIO_Init(ACM1602NI_I2C_LCD_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ACM1602NI_I2C_LCD_I2C_SCK_GPIO_PORT, ACM1602NI_I2C_LCD_I2C_SCK_SOURCE, ACM1602NI_I2C_LCD_I2C_SCK_AF);
GPIO_PinAFConfig(ACM1602NI_I2C_LCD_I2C_SDA_GPIO_PORT, ACM1602NI_I2C_LCD_I2C_SDA_SOURCE, ACM1602NI_I2C_LCD_I2C_SDA_AF);
/* I2C configuration -------------------------------------------------------*/
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
I2C_InitStructure.I2C_DigitalFilter = 0x00;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
//I2C_InitStructure.I2C_Timing = 0x00902025; //CLK = 10uS(100KHz)
I2C_InitStructure.I2C_Timing = 0x009FFFFF; //CLK = 62uS(16.8KHz)
/* Apply I2C_LCD_I2C configuration after enabling it */
I2C_Init(ACM1602NI_I2C_LCD_I2C, &I2C_InitStructure);
/* I2C_LCD_I2C Peripheral Enable */
I2C_Cmd(ACM1602NI_I2C_LCD_I2C, ENABLE);
}
#ifdef USE_DEFAULT_TIMEOUT_CALLBACK
uint32_t ACM1602NI_I2C_LCD_TIMEOUT_UserCallback(void)
{
/* Block communication and all processes */
while (1)
{
}
}
#endif /* USE_DEFAULT_TIMEOUT_CALLBACK */
(JF1VRR)