Translate

顯示具有 AVR 標籤的文章。 顯示所有文章
顯示具有 AVR 標籤的文章。 顯示所有文章

2023年4月17日 星期一

使用tinkercad模擬arduino 控制LCD 16X2(I2C) PCF8574

 tinkercad 預設的LCD I2C範例係使用MCP23008型

電路接線

將LCD的VCC接到Arduino板上的5V、GND(LCD)接GND(Arduino)、SCL(LCD)接A5或Aref左邊第2腳(Arduino)、SDA(LCD)接A4或Aref左邊第1腳(Arduino)


但因為自已係使用PCF8574型,範例程式沒辦法使用。網路上找了一些資料如下面範例。

點選LCD可以將類型改成PCF8574型,地址用預設的32,也可以改成您想要的,但呼叫元件時第1個參數記得改您最後設定值。


#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(32,16,2);  //設定參數(地址,行數,列數)

void setup()
{
  lcd.init();                      // initialize the lcd 
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Hello, world!");
  lcd.setCursor(0,1);
  lcd.print("2023/04/17");
  lcd.setCursor(0,2);
}

void loop()
{

}

2018年7月9日 星期一

Arduino UNO R3 4×4鍵盤模組實驗

參考:https://swf.com.tw/?p=917

利用Time 1中斷,每0.1秒去掃描看沒有按鍵被按。
Time中斷可參考http://tomyam-yang.blogspot.com/2017/06/arduino-timer-as-counter.html

提升電阻參考https://www.arduino.cc/en/Tutorial/DigitalPins

volatile char key_pad;因為key_pad變數使用在中斷內如果不使用volatile去定義,有可能這一次編譯可以正常執行,下一次編譯卻有問題,詳細說明可參考http://newscienceview.blogspot.com/2013/09/c-volatile.html

將多維陣列keymap[][]傳入副函式scan_key的方法如下:
key_pad=scan_key(KEY_COLS,KEY_ROWS,(char *)keymap,colPins,rowPins);
key_pad=scan_key(KEY_COLS,KEY_ROWS,*keymap,colPins,rowPins);
key_pad=scan_key(KEY_COLS,KEY_ROWS,keymap[0],colPins,rowPins);
key_pad=scan_key(KEY_COLS,KEY_ROWS,&keymap[0][0],colPins,rowPins);

陣列的參數傳遞可以參考C陣列[C++]如何將多維陣列傳入副函式二維陣列與雙重指標之間的關係


char scan_key(byte,byte,char *,byte *,byte *);
#define KEY_ROWS 4 // 按鍵模組的列數
#define KEY_COLS 4 // 按鍵模組的行數
const byte colPins[KEY_COLS] = {9, 8, 7, 6};     // 設定「行」腳位
const byte rowPins[KEY_ROWS] = {13, 12, 11, 10}; // 設定「列」腳位
const char keymap[KEY_ROWS][KEY_COLS] = {
  {'A', '7', '8', '9'},
  {'B', '4', '5', '6'},
  {'C', '1', '2', '3'},
  {'D', 'E', 'F', '0'}
};
volatile char key_pad;
void setup() {
 byte i;
//設定中斷1參數:
  TCCR1A=0x00;
  TCCR1B=0b00000101;//設定除頻1024 15625HZ 
  TCNT1=-1562;      //約0.1秒掃描一次
  TIMSK1 |=0x01;    //啟動Timer中斷

  key_pad=0;
  for ( i = 0; i <= 3; i++) {
    pinMode(rowPins[i], INPUT);
    pinMode(colPins[i], OUTPUT);
    digitalWrite(colPins[i], HIGH);
    digitalWrite(rowPins[i], HIGH);//啟動上拉電阻
  }
  Serial.begin(9600);
}

void loop() {
  while(key_pad)
  {
    Serial.println(key_pad);
    key_pad=0;
  }
}
ISR(TIMER1_OVF_vect)
{
  TIMSK1 &=0xfe;
  key_pad=scan_key(KEY_COLS,KEY_ROWS,(char *)keymap,colPins,rowPins);
  TCNT1=-1562;
  TIMSK1 |=0x01;
 }
 
char scan_key(byte col,byte row,char *key_map,byte *col_pin,byte *row_pin)
{
  unsigned long time_;
  byte i,j;
  byte scanVal;   // 暫存掃描到的按鍵值low or high
  time_=millis();
  for (i = 0; i < row; i++) { //按著不放的情況下不用理它
    scanVal=digitalRead(*(row_pin+i));
    if (scanVal == LOW)
    {
      return 0;   
    }
  }
  for (i = 0; i < row; i++) {
    for (j = 0; j < col; j++) {
      digitalWrite(*(col_pin+j),LOW);
      scanVal=digitalRead(*(row_pin+i));
      if (scanVal == LOW) {    // 如果輸入值是「低電位」…      
        for(;;)
        {
          if((millis()-time_)>1000)
          {
            return 0;
          }
          delay(100);
          scanVal=digitalRead(*(row_pin+i));
          if (scanVal == LOW)
          {
            return *(key_map+i*row+j);
          }
        }
      }
      digitalWrite(*(col_pin+j),HIGH);    
    }
  }
  return 0;
 }

2017年6月17日 星期六

Arduino Time2 中斷計時微秒

參考資料:
http://coopermaa2nd.blogspot.tw/2011/07/4-timers.html
http://coopermaa2nd.blogspot.tw/2011/07/41-blink-with-timer.html

利用time2除頻256 16MHZ 剩62500HZ 1clok要0.000016秒 1ms需62.5個clock約63 63clok=0.001008秒
time2除頻與time0、time1不一樣
CS22 CS21 CS20 功能
0 0 0 停止
0 0 1 clk/1
0 1 0 clk/8
0 1 1 clk/32
1 0 0 clk/64
1 0 1 clk/128
1 1 0 clk/256
1 1 1 clk/1024
volatile unsigned int delay_ms_count;
void delay_ms(unsigned int i);
void setup() {
  // put your setup code here, to run once:
DDRB=0b00100000;//設定P13為output
TCCR2A=0x00;
TCCR2B=0b00000110;//設定除頻256
TCNT2=-63;
}
void loop() {
 // put your main code here, to run repeatedly:
 //設定P13 hight
 PORTB =PORTB | 0B00100000;
 delay_ms(1000);
 //設定P13 low
 PORTB=PORTB & 0B11011111;
 delay_ms(1000);
}
void delay_ms(unsigned int i)
{
  delay_ms_count=i;
  TCNT2=-63;
  TIMSK2 |=0x01;//啟動中斷time2
  while(delay_ms_count > 0){
  }
  TIMSK2 &=0xfe;//關閉中斷time2
}
ISR(TIMER2_OVF_vect)
{
 delay_ms_count--;
 TCNT2=-63;
}

2017年6月16日 星期五

Arduino Timer as a Counter

void uart9600_setup();
void serial_put(char *string);
void num2str(int i,char *a);
char nu2ch[3];
TCCRxA – Timer/Counter Control Register A (x 代表 0, 1 或 2):
TCCRxA 暫存器主要是用來設定 Timer 的模式,例如 PWM 輸出等進階的功能。一般來說,如果沒用到 PWM,只是要單純的 Timer/Counter 功能的話,
那麼把 TCCR0A 暫存器設定成 0x00 就行了。

TCCRxB – Timer/Counter Control Register B:
TCCR1B 暫存器主要是用來設定 clock source。比較重要的是 CS12、CS11 和 CS10 這三個位元,這三個元位就是用來選擇 clock 的
TCNTx – Timer/Counter Register:
TCNTx 暫存器比較簡單,它就是 Timer 的計數器。
void setup() {
  // put your setup code here, to run once:
  //Serial.begin(9600);
  uart9600_setup();
  TCCR1A=0x00;
  TCCR1B |=_BV(CS12);
  TCCR1B |=_BV(CS11);
  TCCR1B |=_BV(CS10);
  TCNT1=0;

}
   void loop() {
   serial_put("TCNT1:");
   num2str(TCNT1,nu2ch); 
   serial_put(nu2ch);
   serial_put("\n");
   delay(1000);
}
  
void num2str(int i,char *a)
{
  unsigned char t,h,z;
  if (i>999)
  {
    i=0;
  }
  else
  {
    t=(i/100);
    h=(i-t*100)/10;
    z=(i-t*100-h*10);
    a[2]=z+48;
    a[3]='\0';
    if (t==0)
    {
      *a=' ';
      if(h==0)
      {
        a[1]=' ';
      }
      else
      {
        a[1]=h+48;
      }
    }
    else
    {
      *a=t+48;
      a[1]=h+48;
    }
  }
}
UCSR0A – USART Control and Status Register A: 在 UCSR0A 這個暫存器中,比較重要的是 RXC0, TXC0, UDRE0 這三個旗號:
bit7 RXC0: USART Receive Complete,當 receiver buffer 中有未讀取的資料時,這個旗標會變1,當資料被讀走時,旗標會變0。
bit6 TXC0: USART Transmit Complete,當 Shift Register 的資料被傳送出去,而且 transmit buffer (UDR0) 中也沒有資料時,這個旗標會變1。
bit5 UDRE0: USART Data Register Empty,當 transmit buffer (UDR0) 準備好放下一個要傳送的資料時,這個旗標變1。

UDR0 – USART I/O Data Register 0:
UDR0 暫存器比較簡單,它是用來放資料的暫存器。比較特別的是,transmitter 跟 receiver 共用 UDR0 暫存器。不過,對 UDR0 寫資料其實是放到 TXB 暫存器,而讀取 UDR0 時資料則是從 RXB 暫存器中取出。
 
void serial_put(char *string)//傳送字串
{
   while(*string)
   {
      while(!(UCSR0A & 0b00100000)) //判斷UDRE0是否已經準備好要傳送下一個char
      {  }
      UDR0=*string++;//UDR0=*string;*string=*string+1;
   }
}

UBRR0L 和 UBRR0H – USART Baud Rate Registers: UBRR0H[11:8]
UBRR0L[7:0]
這兩個暫存器是用來設定 Baud Rate 的,公式是:
設定值 = (F_CPU/16/baud rate) – 1
其中 F_CPU 代表時脈頻率,以 Arduino UNO 而言是 16000000,baud rate 是想要的速率,例如 9600, 19200, 38400, 57600… 等。

UCSR0B – USART Control and Status Register B:
RXCIE0, TXCIE0, UDRIE0 則是用來決定要不要啟用中斷的位元:
bit7 RXCIE0: USART Receive Complete Interrupt Enable 0
bit6 TXCIE0: USART Transmit Complete Interrupt Enable 0
bit5 UDRIE0: USART Data Register Empty Interrupt Enable 0
bit4 RXEN0 接收器旗標 0:關閉 1:啟用
bit3 TXEN0 發射器旗標 0:關閉 1:啟用

UCSR0C – USART Control and Status Register C:
UCSR0C 是用來設定通訊所用的 Frame format (訊框格式),比如同步/非同步傳輸, Parity, Stop bit 及 Data bits 等設定:
相關設定可以參考http://coopermaa2nd.blogspot.tw/2011/07/5-usart.html
 
void uart9600_setup()
{
   //UART初始設定9600 n,8,1
   //ubrr 設定值 = (F_CPU/16/baud rate) – 1
   //ubrr=16000000/16/9600-1=103
   UBRR0H=(unsigned char)0;
   UBRR0L=(unsigned char)103;
   UCSR0B=0B00011000;//啟動接收中斷、啟動傳送接收
   UCSR0C=0B00000110;//usart control and status register c n,8,1
}
參考: http://coopermaa2nd.blogspot.tw/2011/07/42-timer-as-counter.html

材料:
開關 1個
電阻10K 1個

接線圖



























實驗結果