Translate

2018年7月23日 星期一

Arduino UNO R3 自製Library RS232通訊

參考:葉難Arduino撰寫自已的程式庫如何編寫ARDUINO LIBRARY

將自已寫的Arduino UNO R3 使用自建C函式庫改寫成Library

UART相關設定參考:http://coopermaa2nd.blogspot.com/2011/07/5-usart.html 在arduino\libraries資料夾下新增一個自已的函式庫資料夾RS232


USART 暫存器

UCSR0A 這個暫存器中,比較重要的是 RXC0, TXC0, UDRE0 這三個旗號:

bit7:RXC0: USART Receive Complete,當 receiver buffer 中有未讀取的資料時,這個旗號會豎起來,當資料被讀走時,旗號會清除。

bit6:TXC0: USART Transmit Complete,當 Shift Register 的資料被傳送出去,而且 transmit buffer (UDR0) 中也沒有資料時,這個旗號會豎起來。

bit5:UDRE0: USART Data Register Empty,當 transmit buffer (UDR0) 準備好放下一個要傳送的資料時,這個旗號會豎起來。





while(!(UCSR0A & 0b00100000)){ }//等待bit5變成1,否則一直等待。等待RS232空閒。
void RS232::put_string(char *string) {
   while(*string) { 
            while(!(UCSR0A & 0b00100000)) { } 
            UDR0=*string++; 
   }
 }
while(!(UCSR0A & 0b10000000)){}//等待bit7變成1,否則一直等待。一直等待RS232傳送資料過來。
char RS232::get_char() {
   char recchar;
   while(!(UCSR0A & 0b10000000)){}
   recchar=UDR0;
   put_char(recchar);
   return recchar; 
}


新增一個RS232.h內容如下:

#ifndef RS232_H
#define RS232_H
#include <avr/interrupt.h> 
class RS232 {
public:
 RS232();
 ~RS232();
 void begin(unsigned long baud);
 void put_char(char str);
 void put_string(char *string);
 char get_char(void); 

};
#endif
新增一個RS232.cpp內容如下:
#include <RS232.h>
RS232::RS232(){ 
}
RS232::~RS232(){ 
}
void RS232::begin(unsigned long baud){
    unsigned int baud_setting=(F_CPU/16/baud)-1;
    UBRR0H=(unsigned char)(baud_setting>>8);
    UBRR0L=(unsigned char)baud_setting;
    UCSR0B=0B00011000;//啟用transmitter和receiver,並且不使用中斷。RXEN0=1,TXEN0=1,RXCIE0=0,TXCIE0=0。
    UCSR0C=0B00000110;//設定frame format為N,8,1
}
void RS232::put_string(char *string)
{
    while(*string)
    {
        while(!(UCSR0A & 0b00100000))
        {  }
        UDR0=*string++;
    }
}
void RS232::put_char(char str)
{
       while(!(UCSR0A & 0b00100000))
       {  }
       UDR0=str;
}
char RS232::get_char()
{
 char recchar;
 while(!(UCSR0A & 0b10000000)){}
 recchar=UDR0;
 put_char(recchar);
 return recchar;
}


新增一個檔案keywords.txt內容如下:
RS232 KEYWORD1
begin KEYWORD2
put_char KEYWORD2
put_string KEYWORD2
get_char KEYWORD2
新增一個資料檔案example\rs232_lib.ino內容如下:
#include <RS232.h>
RS232 RS232;
void setup(){
  RS232.begin(9600);
}
void loop(){
  char char_reg;
  char_reg=RS232.get_char();
  RS232.put_char('Z');
  RS232.put_string("Hello World");
}

2018年7月19日 星期四

Arduino UNO R3藍牙實驗 App Inventor2 透過藍牙傳送訊號給 Arduino

參考:[雙A計劃] Part0:App Inventor 透過藍牙傳送訊號給 Arduino

材料清單:
1、Android手機
2、Arduino UNO R3
3、藍芽模組(BT06),請仿照電路圖接線(藍芽RX接Arduino D5、藍芽TX接Arduino D4、藍芽VCC接Arduino 5V、藍芽GND接Arduino GND)




'
接完線依照文章內容打上程式,編輯後上傳到板子上。




#include <SoftwareSerial.h>
#include <Wire.h>  //引用二個函式庫SoftwareSerial及Wire SoftwareSerial 
SoftwareSerial I2CBT(4,5); //定義PIN4及PIN5分別為Arduino RX及TX腳位接藍芽的TX腳位與RX腳位 
void setup() { 
  Serial.begin(9600); //Arduino起始鮑率9600 
  I2CBT.begin(9600); //藍牙鮑率9600
  //(注意!此鮑率每個藍牙晶片不一定相同,請先確認完再填寫進去) 
} 

void loop() { 
  byte cmmd[20]; 
  int insize; 
  while(1){ 
  if ((insize=(I2CBT.available()))>0)
  {  //判斷有沒有訊息接收 
    Serial.print("input size = "); 
    Serial.println(insize);  //顯示接收多少訊息 
    for (int i=0; i<insize; i++) {
      Serial.print(cmmd[i]=char(I2CBT.read()));//將接收的訊息顯示出來
      Serial.println(" ");  
    }//if
  } //for 
  }//while
}






2018年7月18日 星期三

Arduino 藍芽BT06(相容HC06)測試AT指令,行結尾使用NL&CR

BT06從淘寶買來的相容HT06

參考資料:http://coopermaa2nd.blogspot.com/search?q=bluetooth

參考[雙A計劃] Part1:App Inventor 經由藍牙控制 Arduino LED 亮滅做實驗這個實驗電路也很簡單四條線,一開始接完燒錄完程式,手機按BT List沒有反應。最後發現原來手機要開啟藍芽功能,先去連線Arduino的藍芽輸入PIN碼才能使用。

後來參考HC-05與HC-06藍牙模組補充說明(三):使用Arduino設定AT命令去使用裡面的接線,去測試一下我的藍牙模組有沒有反應,一開始輸入AT也是沒有反應,
後來去修改通訊速率一個一個試都沒有反應。有看到HC-06要使用沒有行結尾、HC-05要使用\n\r行結尾。最後設定使用\n\r輸入AT有反應了。

雖然BT-06相容HC-06但行結尾是相容HC-05。

參考[雙A計畫]藍牙模組(HC05、HC06)常見的指令使用教學裡的電路圖接線,用arduino IDE的序列埠監控視窗,行結尾使用NL&CR 。
AT:用於確認通訊。
AT+VERSION:查看韌體版本
AT+NAMEOOOO:設定模組的識別名稱。

AT+PINOOOO:更改配對密碼。假如你不想讓其他人輕易地連接到你的藍牙裝置,可以透過這個AT命令修改配對密碼。



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;
 }

2018年7月5日 星期四

Arduino UNO R3 使用自建C函式庫

參考資料:https://arduino.stackexchange.com/questions/946/how-to-call-c-functions-from-arduino-sketch

UART的使用可以參考http://tomyam-yang.blogspot.com/2017/06/arduino-timer-as-counter.html


要在arduino主程式使用c函式庫,需要把函式庫放在arduino/libraries資料夾下。


並在主程式使用extern "C"{} include 函式


我在libraries底下建一個資料夾放我的.c與.h檔,分別是io.h、iom328p.h、serial.c、serial.h


io.h與iom328p.h是內建的,是定義arduino uno r3的參數,但當你使用自建c函式庫驗證會找不到,所以就把它放進來同一個資料夾內。


底下的程式是自建uart程式,bitrate:9600。

主要副程式

void uart9600_setup(); //初始設定

void serial_put(char *string); //輸出字串

char serial_get(void);//取的字元

void serial_putchar(char string);//輸出字元

主程式:


extern "C"{
#include <serial.h>
}
char rechchar;
char message[]="Hello World";
void setup() {
  // put your setup code here, to run once:
  uart9600_setup();
  serial_put(message);
}

void loop() {
    rechchar=serial_get();
}
serial.c

#include <iom328p.h>
#include <io.h>
void serial_put(char *string)
{
   while(*string)
   {
      while(!(UCSR0A & 0b00100000))
      {  }
      UDR0=*string++;
   }
}
void serial_putchar(char string)
{

      while(!(UCSR0A & 0b00100000))
      {  }
      UDR0=string;
}
char serial_get()
{
  char recchar;
  while(!(UCSR0A & 0b10000000))
  {

  }
  recchar=UDR0;
  //UDR0=*recchar;
  serial_putchar(recchar);
  return recchar;
}
void uart9600_setup()
{
   //unsigned int baud_setting=103;
   UBRR0H=(unsigned char)0;
   UBRR0L=(unsigned char)103;
   UCSR0B=0B00011000;//啟動接收中斷、啟動傳送接收
   UCSR0C=0B00000110;
}
ISR(USART_RX_vect)
{
  char ReceivedChar;
  ReceivedChar=UDR0;
  UDR0=ReceivedChar;
}
serial.h


void uart9600_setup();

void serial_put(char *string);

char serial_get(void);

void serial_putchar(char string);


















2018年7月4日 星期三

Arduino UNO LCD 2004A I2C

參考資料:https://makerpro.cc/2017/02/how-arduino-use-i2c-to-control-lcd-module/

這個看起來很簡單只需接四條線,也有範例程式,但花了我快一個星期才試好。最後發現是LCD出廠把光源調到最亮,字才會跑不出來。最後也是去購買的拍賣看到留言才知道問題。
參考資料用的I2C位址係0x27,因我使用的LCD I2C晶片不一樣所以不同。
可以參考https://docs.labs.mediatek.com/resource/linkit7697-arduino/en/tutorial/driving-1602-lcd-with-pcf8574-pcf8574a

PCF8574A這個IC的位址0X3f
PCF8574這個IC的位址0X27
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR    0x3f  // 定義I2C使用PCF8574A這個IC的位址
// 其實位址可以利用A0,A1或A2改變
// 定義LiquidCrystal函式庫裡LCD各個腳位
#define BACKLIGHT_PIN 3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin,BACKLIGHT_PIN, POSITIVE);
void setup() {
  lcd.begin(20,4);        // 4行,每行20個字
  lcd.setBacklight(HIGH); // 開啟LCD背光,要關閉就用LOW
  lcd.setCursor ( 0, 0 );            // 到第一行第一個字
  lcd.print("hello, world!");
}
 int n = 1; // 顯示LCD刷新次數
 void loop() {
  lcd.noCursor();
  lcd.setCursor (16,3);         // 到第四行的第16個字元
  lcd.print(n++,DEC);           // 更新次數
                                // 覆蓋前一個數字
  delay(500);                   // 等待半秒鐘後更新
  lcd.cursor();
  delay(500);
}