Search

2015年12月18日 星期五

Electronic Stethoscope DIY

    先到這下載膜面模型:https://github.com/GliaX/Stethoscope,印出來head.stl後先將尾管套上熱縮套然後加熱收縮,另一端套上電容式麥克風加熱,接著再找一層塑膠薄膜套上膜面束緊(我是用實驗用橡膠手套)。





    實驗後敏感度還不錯,不過我下圖Bandpass只有到1~105Hz所以示波器訊號看起來頻率不高但是可以看出心跳,我試著換成人體心肺音1~3100Hz也可以收到第一二心肺音。







MQ Gas Sensors Breakout Board

    MQ系列轉板,搭配一顆SMD 104P電容和10k VR,下面第一張圖是MQ-2,第二張圖是MQ-3,把MQ系列接反或者電壓接反都能Work只影響偵測到目標氣體時電壓曲線上升或下降,板子Pin中間那根就是AnalogOUT。


示波器為酒精棉片靠近時的反應




Download:


2015年10月6日 星期二

Raspberry Pi 2 + Raspicam + wxWidgets + OpenCV

    最近在設計一些Prototype所以先找RPi2實驗後再轉到FPGA,因爲我是裝Raspbian,所以wxWidgets跟OpenCV編法跟Debian一樣,主要就多個Raspicam c++ API直接給OpenCV 2 API的格式,找來編完後依照要的Mode給參數就可以讀取Raspicam的Video Stream了。






2015年10月5日 星期一

換MBPR喇叭

    最近喇叭破音超誇張開音樂時整個機器會震動,試了一下發現喇叭壞了去對岸買了一個來換,換的時候又發現喇叭有三根螺絲,其中一根居然滑牙...?!最後只好把壞掉的喇叭用剪線鉗破壞然後再把螺絲用手轉下來...







2015年9月21日 星期一

中秋節放煙火 XD

    去年中秋節做的東西,用的是Kanthal 鎳鉻合金線,加熱速度要看Datasheet的長度電阻和電流量,短短的不到0.5sec通9v就能加熱到發紅點燃煙火引線了XD,記得把加熱合金線的電源和MCU分開XD



data :

DCMotor PID Speed Controller

    最近實驗需要弄一個簡易的有機溶液攪拌的裝置,就是在馬達轉子有兩個磁鐵並在溶液中放入小的鐵條,馬達轉動時就會帶動鐵條旋轉就能攪拌溶液了,只不過我這邊需要控制轉速否則太快可能破壞有機分子的三維結構。

    實作上用霍爾IC去感測馬達轉速,並且將結果回授到PID控制器,再由PID計算出PWM大小去控制,這邊我PWM OUTPUT直接接上L293D的ENABLE腳位即可,或者接上電晶體的控制Base Pin也可以。




code:


Reference:

2015年9月1日 星期二

Using MSP430 Emulation(Spy-Bi-Wire)

    最近發現Lab有很多MSP430系列的東西,好像是前人做device留下的,搜刮出來發現有大多型號的IC以及幾個MSP-EXP430G2 LaunchPad和Develop kit,剛好可以試著用MSP430 Spy-Bi-Wire Debug。

    下面都用MSP-EXP430G2 LaunchPad內附的東西,首先將板子的J3 jump都拔掉,可以得到TXD RXD RST TEST VCC(注意RXD RST TEST VCC要用Emulation虛線上那排排針,只有TXD用下面那排排針!<參考下方2015/09/01更新>),將內附的MSP430 IC與J3的Emulation部分連接如下:

Emulation            MSP430
   VCC      <=>     DVCC
 GND      <=>     DVSS
       RST        <=>   SBWTDIO
     TEST       <=>   SBWTCK

    接著開啟IDE選擇Board類型並開Blink Example(如下圖程式碼):


    設定完成之後Upload應該就會成功了,此時若用Blink Example,則P1.0的腳位會3.3V-0V變化。

 

    MSP430的User Guide有說RST要上拉47k的電阻以及下拉2.2nf的電容,不過我沒有使用依然可以寫入成功。



=============2015/09/01===============
    今天用Example CharacterAnalysis實驗UART的功能,將Emulation的UART連到M430G2553(這顆有UART,有的MSP430沒有),即TXD 與 RXD交互對接,發現Emulation必須使用虛線下的TXD與虛線上的RXD才能正常通信(如下圖)。




我比較納悶的是Emulation上的TXD接到RXD居然是Emulation收資料?!反之Emulation的RXD接TXD是接收端發送資料?!這個部分還要釐清。



2015年6月4日 星期四

HDC1000 Breakout

    HDC1000轉接版實驗用,STM32 PB15連接HDC1000 DRDYn開外部中斷等接收資料即可,轉換公式看Datasheet,下面是STM32 Example,話說STM32的Hardware IIC真的很鳥...


壹圓下面那個

example:

#include "stm32f10x.h"
#include "stdio.h"

#define HDC1000_ADDR  0x40 << 1

#define HDC1000_BOTH_TEMP_HUMI 0x10
#define HDC1000_TEMP_HUMI_14BIT 0x00
#define HDC1000_HEAT_ON 0x20

#define HDC1000_TEMP  0X00
#define HDC1000_HUMI  0X01
#define HDC1000_CONFIG 0x02

#define true 1
#define false 0

uint8_t HDC1000_SYNC = true;

uint8_t HDC1000_FLAG = HDC1000_TEMP;

double temperature = 0,humidity = 0;

volatile uint32_t delay = 0;

void Delay(volatile uint32_t n)
{
   delay = n;
   while(delay != 0);
}

void Init_I2C() {
   I2C_InitTypeDef I2C_InitStructure;
   GPIO_InitTypeDef GPIO_InitStructure;

   I2C_Cmd(I2C1,ENABLE);

   RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
   GPIO_Init(GPIOB, &GPIO_InitStructure);  

   I2C_InitStructure.I2C_Mode = I2C_Mode_SMBusHost;
   I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
   I2C_InitStructure.I2C_OwnAddress1 = 0x00;
   I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
   I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
   I2C_InitStructure.I2C_ClockSpeed = 100000 ;
   I2C_Init(I2C1, &I2C_InitStructure);
}

void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction){
    while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));

    I2C_GenerateSTART(I2Cx, ENABLE);

    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2Cx, address, direction);

    if(direction == I2C_Direction_Transmitter){
        while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    } else if(direction == I2C_Direction_Receiver){
        while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
    }
}

void I2C_write(I2C_TypeDef* I2Cx, uint8_t data)
{
    I2C_SendData(I2Cx, data);
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}

uint8_t I2C_read_ack(I2C_TypeDef* I2Cx){
    uint8_t data;
    I2C_AcknowledgeConfig(I2Cx, ENABLE);
    while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
    data = I2C_ReceiveData(I2Cx);
    return data;
}

uint8_t I2C_read_nack(I2C_TypeDef* I2Cx){
    uint8_t data;
    I2C_AcknowledgeConfig(I2Cx, DISABLE);
    I2C_GenerateSTOP(I2Cx, ENABLE);
    while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
    data = I2C_ReceiveData(I2Cx);
    return data;
}

void I2C_stop(I2C_TypeDef* I2Cx){
    I2C_GenerateSTOP(I2Cx, ENABLE);
}

void Init_BT(void){
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef interrup;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART2, &USART_InitStructure);

    USART_Cmd(USART2, ENABLE);
}

void BT_Send_String(volatile char *string)
{
   while(*string){
      USART_SendData(USART2,*string);
      ++string;
      while(USART_GetFlagStatus(USART2,USART_FLAG_TC) == RESET){}
   }
}

void HDC1000Start()
{
   uint8_t config = HDC1000_BOTH_TEMP_HUMI | HDC1000_TEMP_HUMI_14BIT | HDC1000_HEAT_ON;

    I2C_start(I2C1, HDC1000_ADDR, I2C_Direction_Transmitter);
    I2C_write(I2C1, HDC1000_CONFIG);
    I2C_write(I2C1, config);
    I2C_write(I2C1, 0x00);
    I2C_stop(I2C1);
    Delay(20);
}

void HDC1000DataRequest(uint8_t reg)
{
   if((HDC1000_FLAG == reg) && HDC1000_SYNC){
      HDC1000_SYNC = false;
      I2C_start(I2C1, HDC1000_ADDR, I2C_Direction_Transmitter);
      I2C_write(I2C1, reg);
      I2C_stop(I2C1);
   }
   else if((HDC1000_FLAG == reg) && HDC1000_SYNC){
      HDC1000_SYNC = false;
      I2C_start(I2C1, HDC1000_ADDR, I2C_Direction_Transmitter);
      I2C_write(I2C1, reg);
      I2C_stop(I2C1);
   }
}

double HDC1000GetTemp()
{
    double temp = 0;

    I2C_start(I2C1, HDC1000_ADDR, I2C_Direction_Receiver);

    temp = I2C_read_ack(I2C1) << 8;
    temp += I2C_read_nack(I2C1);

    return temp / 65536.0 * 165.0 - 40.0;

}

double HDC1000GetHumi()
{
    double humi = 0;

    I2C_start(I2C1, HDC1000_ADDR, I2C_Direction_Receiver);

    humi = I2C_read_ack(I2C1) << 8;
    humi += I2C_read_nack(I2C1);

    return humi / 65536.0 * 100.0;

}

void HDC1000EXTI15()
{
   GPIO_InitTypeDef GPIO_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;
   EXTI_InitTypeDef EXTI_InitStructure;

   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

   GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_15;
   GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

   GPIO_Init(GPIOB, &GPIO_InitStructure);

   GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource15);

   EXTI_InitStructure.EXTI_Line = EXTI_Line15;
   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
   EXTI_InitStructure.EXTI_LineCmd = ENABLE;
   EXTI_Init(&EXTI_InitStructure);

   NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);
}

int main(){
    Init_I2C();
    Init_BT();

    SystemInit();
    if(SysTick_Config(SystemCoreClock / 1000)){
       while(1);
    }

    HDC1000Start();
    HDC1000EXTI15();

    while(true){
       if(HDC1000_FLAG == HDC1000_TEMP){
 HDC1000DataRequest(HDC1000_TEMP);
       }
       else if(HDC1000_FLAG == HDC1000_HUMI){
 HDC1000DataRequest(HDC1000_HUMI);
       }
    }
}

void SysTick_Handler(void)
{
   if(delay != 0){
      delay--;
   }
}

void EXTI15_10_IRQHandler(void) {
   if(EXTI_GetITStatus(EXTI_Line15) != RESET) {
      if(HDC1000_FLAG == HDC1000_TEMP){
HDC1000_FLAG = HDC1000_HUMI;
temperature = HDC1000GetTemp();
      }
      else if(HDC1000_FLAG == HDC1000_HUMI){
HDC1000_FLAG = HDC1000_TEMP;
humidity = HDC1000GetHumi();
      }

      char str[256];
      sprintf(str, "temperature:%.2f humidity:%.2f%%\n",temperature,humidity);
      BT_Send_String(str);

      HDC1000_SYNC = true;
   }
   EXTI_ClearITPendingBit(EXTI_Line15);
}


 Download:

2015年5月14日 星期四

OVC3860

    OVC3860是藍芽音訊IC,只要上電源就可以直接用還不用直流阻隔電容,能直推40mW/32ohm不用放大器,也能從UART做操控,由於沒找到這個郵票孔接腳的Eagle Packge所以我畫了一個放在我的github。過去一年囤積太多材料,所以最近做的東西都是些實驗的小模組,到時候可以整到其他地方,最近消耗一些後才會再從淘寶買其他東西來玩玩。



Download:


2015年5月6日 星期三

PCM5102A

    兩年前拿到的I2S DAC IC,無奈最近一直很忙上週才找一天時間設計實驗版然後做出來,現在接在喬治查爾斯的系統(PCM2706+真空管buffer+前級放大)聽起來解析度明顯提高,接下來就在把PCM2706畫在一起,真不知道啥時有空,還有我的IV-18鐘要做...

    話說手邊有很多模組都找不到Eagle PCB的Packge,所以我建了一些放在Github,目前有PCM5102A,TEA5767,PJ-327A,S3860M-S等等...未來可能會再增加



Download:

github

PCM5102a.zip

2015年3月6日 星期五

Kossel mini E3D-V6 堵料

    前陣子把這問題搞定後做一個總結,其實當初真的很蠢試了一大堆方法搞了一兩週結果發現根本是賣家的PLA料列印溫度要高達240度?!我確認過不是ABS但是也不知道為什麼他要這麼高溫才能融化,後來印時候偶爾還是會卡料,結果我把喉管換成有內襯鐵佛龍的喉管這個問題就解決了...就最後我覺得只要確定幾步就可以了。


  1. 喉管有內襯鐵佛龍
  2. 確定列印料的溫度
  3. 確定進料長度正確(ex:進10mm就是進10mm如果少則,如只進9mm就將DEFAULT_AXIS_STEPS_PER_UNIT第四個數值乘上10/9重新寫入firmware即可)
  4. 有風扇且正確運轉(我覺得有風扇就好換大一點的感覺效果普通)

2015年1月28日 星期三

Kossel mini MarlinFirmware手動校正

    禮拜一收到料之後因為印出來X與Y的比例有問題,例如印10x10方塊會變成9x10,所以又只好再度研究是什麼問題,參考很多文章後和FB網友李派克說明後,最重要的其實是確定x與y的行程等比例接著再進行三點校正即可,以下我也把步驟整理出來,參考最關鍵的文章是日本人的以及FB網友李派克和Marlin的code。


  1. 首先你得先確定以下機器設計的參數並寫入Marlin。
  2. 將噴頭移動到 X 80 ~ -80與Y 80 ~ -80做記號(可用gcode,G1 X80 F2000以此類推去移動)
  3. 量測X兩點的長度與Y兩點的長度相加除以2(假設X = 155mm , Y = 163.6mm , L = (X+Y)/2 = 159.3)。
  4. 將L減去DELTA_EFFECTOR_OFFSET(假設32)與DELTA_CARRIAGE_OFFSET(假設25)得到102.3,接著將 ((102.3 / 15.93) x 16)得到新的L(102.7)。
  5. 將新的L加上DELTA_EFFECTOR_OFFSET與DELTA_CARRIAGE_OFFSET得到159.7。
  6. 將159.7寫入DELTA_SMOOTH_ROD_OFFSET後更新至Controller。
  7. 此時重複一次2~3步驟理論上X與Y兩點的長都應該都會接近160mm,如果沒有則再重複1~6步驟。
  8. 回到原點(G28),接著將噴頭移動到離盤面0.1mm的位置,然後輸入G114記下Z的值並且在列印平台做中心記號,此時將MANUAL_Z_HOME_POS加上此數值重新寫入控制板,接著輸入G28。
  9. 接著進行三點調整水平(即盤面靠近三個柱子的位置),我的分別是:Z:G1 X0 Y85 Z10 F3000,Y:G1 X73.6 Y-42.5 Z10 F3000,X:G1 X-73.6 Y-42.5 Z10 F3000。
  10. 將噴頭移動到這三個點接著將噴頭移動到離盤面0.1mm的位置然後輸入G114記下Z的值,一共有Xz Yz Zz三個數值要記。
  11. 假設Xz = 2.6 Yz = -1.6 Zz = 0.8。
  12. 若要以Zz作為基準調整水平,則你要將X柱子的滑快觸發現為開關的螺絲往下轉1.8mm,而Y柱子的滑快觸發現為開關的螺絲往上轉2.4mm以此類推。
  13. 接著重新回到原點(G28),再重複步驟9~12,直到Xz Yz 都和Zz一樣是0.8為止。
  14. 接著將MANUAL_Z_HOME_POS減去Zz的值重新寫入,到這邊應該盤面水平就調整完成了。
  15. 如果用自動校正則可以跳過步驟9~14。

    不過目前遇到的另一個問題是擠料不順暢...等我解決了再將方法整理上來...


參考:

2015年1月18日 星期日

Kossel mini MarlinFirmware自動校正(探針+限位開關)

    前天看code的公式理解後終於弄出來了,最後其實是我依照公式微調某個DELTA參數就可以了。這種方式還是會有誤差的缺點,但誤差已經在xy的z高0.1mm左右,所以印出來精度可以控制在0.1mm左右,如果要更精準可以用FSR Sensor還可以省一些步驟,我整理一下使用這個方法的順序,不過我目前放下與收起探針都是手動還沒調整修改Firmware :p。

  1. 打開調整Configuration.h(以下步驟提到的預定義編譯參數都在此),與printrun(有MAC版,以下步驟若要重新寫入控制板要記得切斷與printrun的連結)。
  2. 檢查步進馬達帶動齒輪幾齒並計算修改相應的DEFAULT_AXIS_STEPS_PER_UNIT。
  3. 輸入G28回原始點。
  4. 確認幾個機身的參數尺寸輸入無誤,且要確認在原點時噴頭中心距三軸距離一樣,如果不一樣則看要依照哪軸作為參考,然後調整其他兩軸滑塊上的限位開關觸發螺絲高低最後在輸入一次G28(此步驟重要)。
  5. 取消ENABLE_AUTO_BED_LEVELING註解。
  6. 量測RAMPS的ENDSTOP_Z_MIN是否輸出5VDC。
  7. 將探測的ENDSTOP安裝到RAMPS Z_MIN。
  8. 輸入G1 X0 Y0 Z20(Z的數值以不會碰撞到為準,可用printrun慢慢調整),接著將噴頭移動到離盤面0.1mm的位置,然後輸入G114記下Z的值並且在列印平台做中心記號,此時將MANUAL_Z_HOME_POS加上此數值重新寫入控制板,接著輸入G28。
  9. 用printrun將探針移動到中心上,接著慢慢將下Z一次以0.1mm為單位,每用將下一次就用G119看Z_MIN狀態有無改變,若發生改變時輸入G114記下X Y Z的數值並且乘上-1分別填入X_PROBE_OFFSET_FROM_EXTRUDER Y_PROBE_OFFSET_FROM_EXTRUDER Z_PROBE_OFFSET_FROM_EXTRUDER,重新寫入控制板(若步驟4量測錯誤此處數值會是錯的)。
  10. 輸入G29看是否能自動校正,如果會撞頭則快按停止檢查2-4步驟,如果一下G29後噴頭一直呈現階梯狀往上跑則修改Z_MIN_ENDSTOP_INVERTING的狀態。
  11. 若能自動校正則先跑一個列印,在start gcode的G28後插入G29,接著跑一次看是否程跑完自動校正,此時進到列印步驟看是否噴頭會撞到平台或者離平台太高,如果會要快停止並且修改Z_PROBE_OFFSET_FROM_EXTRUDER參數。
  12. 如果步驟11最後列印時的誤差太誇張要重新跑一次流程尤其確認2-4是正確的,如沒問題基本上就成功了。

    組裝到現在三天搞校正就花了快兩天最後還放棄手動校正直接弄自動,還好當初真的沒繼續念機械XD



STM32F10X Nano ver1.0.0

    跟STM32F10X Nano v0.0.1一樣,不過修正一些地方並且取消BOOT0的選擇,因為我實在太愛ST-LINK了XD,最後一樣是好製作的單面板。








Download:

2015年1月16日 星期五

Kossel mini 組裝心得



    很久以前就想要弄一台了,直到上週才把機構相關的零件找齊動工,過程中有遇到大大小小的問題啦...,我在拆上半部的三角固定時,太用力把一個列印角件弄爆...裝皮帶把一片咬合弄斷...還有我發現工具會影響某些部位的安裝順序,總之一些組裝技巧我想就記錄一下。



  1. 上面角件的皮帶軸承要先裝。
  2. 下面角件的步進馬達要先裝。
  3. 擠鋁裝不進角件要先幫擠鋁修切割面的毛邊。
  4. 如果末端效應器跟連桿連接是用釹鐵棚磁鐵,千萬要注意在用AB膠黏之前的方向。
  5. 運動時末端效應器的連桿會稍微脫落,可以在它和滑塊上各自裝個羊眼釘,再連接一個橡皮筋。



    目前我還在校正這一關有問題,官方的頂針我還不會用,有想過將頂針跟金屬底盤各連一條線到Z min 限位的孔,改成機械式接觸導通校正,或者用壓力及光學等等的方式,不過我現在還是手動校正,依然不太好搞等我有心得再補充吧。









    也因為弄了這台讓我又想重寫之前的Project XD , 3D Scanner - wxWidgets+OpenCV