#include
#include
void sample_sw(void);
void LED_disp(void);
void led7seg(uint8_t num);
uint32_t bin2bcd(uint16_t n);
volatile uint32_t rdbuf = 0;
volatile uint8_t sw_stat, sw_stat2;
volatile uint8_t sw_stat_prev, sw_stat2_prev;
volatile uint8_t updated = 1; // 周波数設定更新 0:何もしない 1:表示更新
volatile uint8_t preset_ch = 1; // プリセットチャンネル
volatile uint8_t preset_ch_memory[16] = {28,50,57,75,125,18,35,47,65,0,0,0,0,0,0,0}; // プリセットチャンネルメモリ
volatile uint8_t preset_enable = 1; // 0: チューニングモード、1: プリセットモード
volatile uint8_t frequency = 0; // 周波数 0: 76.0MHz, 141: 90.0MHz
volatile uint16_t freq_value[141] = { // DDS周波数設定値 0-141; 76.0-90.0MHz
0x07E3,0x083C,0x0895,0x08EE,0x0947,0x099F,0x09F8,0x0A51,0x0AAA,0x0B03,
0x0B5C,0x0BB5,0x0C0E,0x0C67,0x0CBF,0x0D18,0x0D71,0x0DCA,0x0E23,0x0E7C,
0x0ED5,0x0F2E,0x0F87,0x0FDF,0x1038,0x1091,0x10EA,0x1143,0x119C,0x11F5,
0x124E,0x12A7,0x12FF,0x1358,0x13B1,0x140A,0x1463,0x14BC,0x1515,0x156E,
0x15C7,0x161F,0x1678,0x16D1,0x172A,0x1783,0x17DC,0x1835,0x188E,0x18E7,
0x193F,0x1998,0x19F1,0x1A4A,0x1AA3,0x1AFC,0x1B55,0x1BAE,0x1C07,0x1C5F,
0x1CB8,0x1D11,0x1D6A,0x1DC3,0x1E1C,0x1E75,0x1ECE,0x1F27,0x1F7F,0x1FD8,
0x2031,0x208A,0x20E3,0x213C,0x2195,0x21EE,0x2247,0x229F,0x22F8,0x2351,
0x23AA,0x2403,0x245C,0x24B5,0x250E,0x2567,0x25BF,0x2618,0x2671,0x26CA,
0x2723,0x277C,0x27D5,0x282E,0x2887,0x28DF,0x2938,0x2991,0x29EA,0x2A43,
0x2A9C,0x2AF5,0x2B4E,0x2BA7,0x2BFF,0x2C58,0x2CB1,0x2D0A,0x2D63,0x2DBC,
0x2E15,0x2E6E,0x2EC7,0x2F1F,0x2F78,0x2FD1,0x302A,0x3083,0x30DC,0x3135,
0x318E,0x31E7,0x323F,0x3298,0x32F1,0x334A,0x33A3,0x33FC,0x3455,0x34AE,
0x3507,0x355F,0x35B8,0x3611,0x366A,0x36C3,0x371C,0x3775,0x37CE,0x3827,
0x387F
};
volatile struct { // ロータリエンコーダ 軸位置構造体
long position; // 軸位置
int moved; // 移動フラグ
} Encoder;
void port_init(void)
{
// ポート設定 0:入力, 1:出力
DDRA = 0b11111111;
DDRB = 0b00000000;
DDRC = 0b11111111;
DDRD = 0b01000000;
DDRE = 0b01110000;
DDRF = 0b11111100;
DDRG = 0b00100;
// 出力ポート初期化
PORTA = 0b00000000;
PORTC = 0b00000000;
PORTD = 0b00000000;
PORTE = 0b00000000;
PORTF = 0b00000000;
PORTG = 0b00000;
// 入力ポート、1:プルアップ
PORTB = 0b11110010;
PORTD = 0b10000000;
PORTE = 0b00000011;
PORTF = 0b00000011;
PORTG = 0b11011;
}
void timer1_init(uint8_t t)
{
// CTC動作
TCNT1 = 0; // タイマ1の初期値設定
OCR1A = t; // タイマ/カウンタ1比較レジスタA
// 15.11.1 タイマ/カウンタ1制御レジスタA (初期値は0x00なので必要ない)
// WGM10=0, WGM11=0
TCCR1A = 0b00000000;
// 15.11.2 タイマ/カウンタ1制御レジスタB
// || ++--- WGM13:WGM12 01 波形生成種別(4bitの上位2bit) CTC top=OCR1A
// || ||+++
TCCR1B = 0b00001101; // タイマ/カウンタ1制御レジスタ CS12:CS11:CS10 101 1024分周
// 15.11.8 タイマ/カウンタ1割り込みマスクレジスタ
TIMSK = 0b00010000;
}
void init_devices(void)
{
cli();
port_init();
timer1_init(2); // 割込間隔 clk/1024, 上限255
}
ISR (TIMER1_COMPA_vect)
//C:\WinAVR-20090313\avr\include\avr\iom64
{
sample_sw(); // Switch読み込み
change_detect(); // Switch状態変化検出
sample_encoder(); // ロータリエンコーダ読み込み
freq_set_update(); // Switchの指定により周波数設定更新
if (updated == 1) {
LED_disp(); // 7セグLED表示
}
if_mb();
updated = 0;
}
void sample_sw(void)
{
static uint8_t sw_prev, sw_prev2;
uint8_t a, a2;
a = ~PIND & 0b00111111; // ビットマスク
a2 = ~PINE & 0b00000100;
// PD0: Tuning/Preset
// PD5: CH1, PD4: CH2, PD3: CH3, PD2: CH4, PD1: CH5
// PE2: Store to Memory
if (a == sw_prev) {
sw_stat = a; // 前回値と同じなら有効として格納
}
sw_prev = a;
if (a2 == sw_prev2) {
sw_stat2 = a2;
}
sw_prev2 = a2;
}
void change_detect(void)
{
uint8_t a=0, a2=0, n=0;
a = sw_stat;
// PD0~PD5が押されているか確認
for (n = 0; n < 6; n++) {
a2 += a & 0x01;
a >>= 1;
}
// スイッチが1つだけ、あるいは押されていない場合のみ処理継続
if (a2 == 0) {
sw_stat_prev = a; // 前値として保存
} else if (a2 == 1) {
if (sw_stat_prev == 0) { // 新たにボタンが押されたとき
updated = 1; // 状態変化フラグ
}
sw_stat_prev = a; // 前値として保存
}
}
void freq_set_update(void)
{
static uint8_t sw_prev; // Preset/Tuningスイッチ切替確認用
// プッシュボタンの処理
if (updated == 1) {
if (sw_stat == 0x20) { // PD5: CH1
preset_enable = 1;
preset_ch = 0;
frequency = preset_ch_memory[preset_ch];
}
if (sw_stat == 0x10) { // PD4: CH2
preset_enable = 1;
preset_ch = 1;
frequency = preset_ch_memory[preset_ch];
}
if (sw_stat == 0x08) { // PD3: CH3
preset_enable = 1;
preset_ch = 2;
frequency = preset_ch_memory[preset_ch];
}
if (sw_stat == 0x04) { // PD2: CH4
preset_enable = 1;
preset_ch = 3;
frequency = preset_ch_memory[preset_ch];
}
if (sw_stat == 0x02) { // PD1: CH5
preset_enable = 1;
preset_ch = 4;
frequency = preset_ch_memory[preset_ch];
}
if ((sw_stat == 0x01) && (sw_prev == 0)){ // PD0: TUNING/PRESET SELECT、前値と現在値が異なる場合
if (preset_enable ==1) {
preset_enable = 0;
} else {
preset_enable = 1;
frequency = preset_ch_memory[preset_ch];
}
}
}
// ロータリエンコーダの処理
if (Encoder.moved == 1) {
if (preset_enable == 1) {
if ((Encoder.position == 1) && (preset_ch < 15)) {
preset_ch = preset_ch + 1;
frequency = preset_ch_memory[preset_ch];
}
if ((Encoder.position == -1) && (preset_ch > 0)) {
preset_ch = preset_ch - 1;
frequency = preset_ch_memory[preset_ch];
}
} else {
if ((Encoder.position == 1) && (frequency < 140)) {
frequency = frequency + 1;
}
if ((Encoder.position == -1) && (frequency > 0)) {
frequency = frequency - 1;
}
}
updated = 1;
Encoder.moved = 0;
}
}
void if_mb(void)
{
// PE6: SCLKout, PE5: SDout, PE4: ENout, PE3:RDin
uint16_t n;
uint32_t sdbuf;
// 周波数設定読み込み
sdbuf = freq_value[frequency];
PORTE |= _BV(4); // EN->1 : PE4
// for (n = 0; n <= 15; n = n + 1) {
for (n = 0; n <= 31; n = n + 1) {
// SDout
// if(sdbuf & _BV(15 - n)){
if(sdbuf & _BV(31 - n)){
PORTE |= _BV(5);
}else{
PORTE &= ~_BV(5);
}
// SCLKout
PORTE |= _BV(6); // SCLKout->1 : PE6
PORTE &= ~_BV(6); // SCLKout->0 : PE6
// RDin
// if(PINE & _BV(3)){
if(bit_is_set(PINE, 3)){
// rdbuf |= _BV(15 - n);
rdbuf |= _BV(31 - n);
}else{
// rdbuf &= ~_BV(15 - n);
rdbuf &= ~_BV(31 - n);
}
}
PORTE &= ~_BV(4); // EN->0 : PE4
}
void LED_disp(void)
{
uint32_t a;
a = bin2bcd(preset_ch + 1);
// チューニングモードの時は非表示
// preset_enable 0: チューニングモード、1: プリセットモード
if(preset_enable == 0){ // ゼロサプレス
a = 0x00FF;
}
// メモリチャンネル表示設定 x1桁
PORTF &= ~_BV(5); // LE->0 : PF5
led7seg(a);
PORTG |= _BV(2); // DP->1: PG2
PORTF |= _BV(5); // LE->1 : PF5
a = a >> 4;
// メモリチャンネル表示設定 x10桁
if(a == 0){ // ゼロサプレス
a = 0x000F;
}
PORTF &= ~_BV(6); // LE->0 : PF6
led7seg(a);
PORTG |= _BV(2); // DP->1: PG2
PORTF |= _BV(6); // LE->1 : PF6
a = bin2bcd(frequency + 760);
// 周波数表示設定 0.1MHz桁
PORTF &= ~_BV(3); // LE->0 : PF3
led7seg(a);
PORTG |= _BV(2); // DP->1: PG2
PORTF |= _BV(3); // LE->1 : PF3
a = a >> 4;
// 周波数表示設定 1MHz桁
PORTD &= ~_BV(6); // LE->0 : PD6
led7seg(a);
PORTG &= ~_BV(2); // DP->0: PG2
PORTD |= _BV(6); // LE->1 : PD6
a = a >> 4;
// 周波数表示設定 10MHz桁
PORTF &= ~_BV(4); // LE->0 : PF4
led7seg(a);
PORTG |= _BV(2); // DP->1: PG2
PORTF |= _BV(4); // LE->1 : PF4
}
void led7seg(uint8_t num) // 7セグ表示データ設定
{
if(num & _BV(0)){ // x1: PF2
PORTF |= _BV(2);
}else{
PORTF &= ~_BV(2);
}
if(num & _BV(1)){ // x2: PA6
PORTA |= _BV(6);
}else{
PORTA &= ~_BV(6);
}
if(num & _BV(2)){ // x4: PA7
PORTA |= _BV(7);
}else{
PORTA &= ~_BV(7);
}
if(num & _BV(3)){ // x8: PC7
PORTC |= _BV(7);
}else{
PORTC &= ~_BV(7);
}
}
void sample_encoder(void)
{
// PB0 : CHA
// PE7 : CHB
static uint8_t reinput, rencoder, rencoder_prev;
static const int dir[] = { 0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0 }; // 回転方向テーブル
static int i; // インデックス
int n;
reinput = (PINB & 0x01) + ((PINE >> 6) & 0x02);
if (reinput == rencoder_prev) {
rencoder = reinput; // 前回値と同じなら有効として格納
}
rencoder_prev = reinput;
i = (i << 2) + (rencoder & 3); // 前回値と今回値でインデックスとする
// i = (i << 2) + (PINB & 0x01) + ((PINE >> 6) & 0x02); // 前回値と今回値でインデックスとする
n = dir[i & 15]; // 変化パターンから動きを得る
if (n == 0) { // 動きがあったら位置更新
Encoder.moved = 0;
} else {
Encoder.position = n;
Encoder.moved = 1;
}
}
void wait(unsigned int time) // 100us wait ルーチン。 クロック設定のこと。
{ // http://www.geocities.jp/kuman2600/c_memo.html
time*=1; // timeにF_CPU(MHz)をかける。重要!!
unsigned char lpcnt;
__asm__ __volatile__("\n"
"Entry%=: \n\t"
"ldi %0,27\n" // 計算値は25。24の方が実測値は良い。
"Loop%=: \n\t" // 上の行で 24 のときは100us、249にすれば1msのルーチンになる。
"nop\n\t"
"dec %0\n\t"
"brne Loop%=\n\t"
"sbiw %1,1\n\t"
"brne Entry%=\n\t"
:"=&a"(lpcnt)
:"w"(time)
);
return;
}
uint32_t bin2bcd(uint16_t n)
{
// n-> 16bit
// http://oshiete1.goo.ne.jp/qa1110530.html
uint32_t bcd = 0;
uint8_t base = 0;
while (n > 0){
bcd |= (n%10) << base;
base += 4;
n /= 10;
}
return bcd;
}
int main(void)
{
init_devices();
frequency = preset_ch_memory[preset_ch];
// 割込 初期設定
SREG |= _BV(7); // 全割込許可
sei(); // 割り込み許可
while(1); // 無限ループ
}
2010年8月26日木曜日
FMステレオチューナ: 操作表示部のAVRソース
前回に続き、操作表示部のAVRソースを掲載します。現状では、初期の機能を実装できておらず、プリセットボタンを押すと、所定の放送局に切り替わるだけでしかありません。はっきり言って未完成。本来なら公表できるシロモノではありませんが、ここは趣味のブログですので・・・。ちなみに初めてのAVRプログラムなので、ネット上のサンプルを断片的にコピペしてます。今、よく見たらvolatileの使い方を勘違いしていますね。なお、ロータリーエンコーダ周りはChaNさんのWeb資料を使わせて頂いています。
登録:
コメントの投稿 (Atom)
0 件のコメント:
コメントを投稿