Code Examples Forum Discussions
So I posted "A dumb typewriter sample (I2C keyboard and SPI TFT)" yesterday,
and I was expecting the porting to CY8CKIT-044 was a matter of re-target and re-compile.
昨日、5LP版のこのサンプルをアップしたのですが、CY8CKIT-044 への移植は
デバイスとピン配置を変えて、ほぼ再コンパイルだけで済むかと思ってたのですが。
A dumb typewriter sample (I2C keyboard and SPI TFT)
But as CY8CKIT-044 has Arduino Connector, I wanted to mount the TFT on it.
Then SPI uses SCB[3], which was also used by the UART to the KitProg.
CY8CKIT-044 には Arduino のコネクタがついてるので、TFT を直接マウントすると
Arduino タイプでは SPI は SCB[3] を使用することになります。
しかし SCB[3] は KitProg へ接続されている UART とかぶっていました。
So I had to change the UART to SCB[1] and connect an external USB-Serial adapter.
Anyway, so I decided to post this as another sample.
(Well I2C functions and isr were also modified)
UART を SCB[1] に変更して、外付けの USB-Serial を接続することにしました。
といった感じでバタバタしたので、別サンプルとして上げさせていただきます。
(I2C関連の関数も全変更、isr も外したなどもありましたが・・・)
Tera Term log
Note: The cursor move keys are not reflected.
注:カーソル移動矢印キーはTera Term には反映されていません。
schematic
pins
main.c
===============
#include "project.h"
#include <stdio.h>
#define TIMEOUT_MS 500
#define CARDKB_I2C_SLAVE_ADDR 0x5F
#include "TFT.h"
#include "Terminal6x8.h"
#define LOOP_INTERVAL 10 /* ms */
#define CURSOR_INTERVAL 50 /* LOOP_INTERVAL * CURSOR_INTERVAL */
int pos_x = 0 ;
int pos_y = 0 ;
char str[128] ; /* print buffer */
void print(char *str)
{
UART_UartPutString(str) ;
}
void cls(void)
{
print("\033c") ; /* reset */
CyDelay(100) ;
print("\033[2J") ; /* clear screen */
CyDelay(100) ;
}
uint8_t myI2C_ReadByte(uint8_t reg_addr)
{
uint8_t status ;
uint8_t value = 0 ;
I2C_I2CMasterClearStatus();
status = I2C_I2CMasterSendStart(CARDKB_I2C_SLAVE_ADDR, I2C_I2C_WRITE_XFER_MODE, TIMEOUT_MS) ;
if (I2C_I2C_MSTR_NO_ERROR == status) {
I2C_I2CMasterWriteByte(reg_addr, TIMEOUT_MS);
status = I2C_I2CMasterSendRestart(CARDKB_I2C_SLAVE_ADDR, I2C_I2C_READ_XFER_MODE, TIMEOUT_MS);
}
if (I2C_I2C_MSTR_NO_ERROR == status) {
status = I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA, &value, TIMEOUT_MS);
}
I2C_I2CMasterSendStop(TIMEOUT_MS);
return(value) ;
}
uint8_t read_cardkb(void)
{
uint8_t key = 0 ;
key = myI2C_ReadByte(1) ;
return( key ) ;
}
void init_hardware(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
UART_Start() ;
TFT_CS_Write(1) ;
TFT_DC_Write(0) ;
TFT_BL_Write(1) ;
SPI_Start() ;
SPI_Enable() ;
TFT_Init() ;
TFT_BusEnable(1) ;
TFT_foreground(White) ;
TFT_background(Black) ;
TFT_set_orientation(0) ;
TFT_cls() ;
TFT_set_font(Terminal6x8) ;
TFT_BL_Write(1) ;
TFT_BusEnable(0) ;
I2C_Start() ;
}
void font_cursor_up(void)
{
pos_y -= TFT_font_height() ;
if (pos_y < 0) {
pos_y = TFT_height() - TFT_font_height() - 1 ;
}
}
void font_cursor_down(void)
{
pos_y += TFT_font_height() ;
if (pos_y >= (TFT_height() - TFT_font_height() - 1)) {
pos_y = 0 ;
}
}
void font_cursor_forward(void)
{
pos_x += TFT_font_width() ;
if (pos_x >= (TFT_width() - TFT_font_width() - 1)) {
pos_x = 0 ;
font_cursor_down() ;
}
}
void font_cursor_backward(void)
{
pos_x -= TFT_font_width() ;
if (pos_x < 0) {
pos_x = TFT_width() - TFT_font_width() - 1 ;
font_cursor_up() ;
}
}
void printc(uint8_t key)
{
TFT_locate(pos_x, pos_y) ;
TFT_putc(key) ;
font_cursor_forward() ;
UART_UartPutChar(key) ;
}
void do_key(uint8_t key)
{
switch(key) {
case 0: // no key received, skip!
case 0xFF: // also not a letter <- new
break ;
case 0x1B: // ESC (clear screen for now)
pos_x = 0 ;
pos_y = 0 ;
TFT_cls() ;
break ;
case 0x0D: // Enter
pos_x = 0 ;
font_cursor_down() ;
UART_UartPutString("\r\n") ;
break ;
case 0x08: // BackSpace
font_cursor_backward() ;
printc(' ') ;
font_cursor_backward() ;
break ;
case 0xB4: // Left Arrow
font_cursor_backward() ;
break ;
case 0xB5: // Up Arrow
font_cursor_up() ;
break ;
case 0xB6: // Down Arrow
font_cursor_down() ;
break ;
case 0xB7: // Right Arrow
font_cursor_forward() ;
break ;
case 0x09: // Tab
// currently no support
break ;
case 0x20: // Space
default:
printc(key) ;
break ;
}
}
void print_cursor(int color)
{
int x0, x1, y0, y1 ;
x0 = pos_x ;
x1 = pos_x + TFT_font_width() ;
y0 = pos_y ;
y1 = pos_y + TFT_font_height() ;
TFT_line(x0, y1, x1, y1, color) ;
}
void blink_cursor(void)
{
static int num_count = 0 ;
static int mode = 0 ;
int16_t color ;
if (num_count >= CURSOR_INTERVAL) {
if (mode == 0) {
mode = 1 ;
color = White ;
} else {
mode = 0 ;
color = Black ;
}
print_cursor(color) ;
num_count = 0 ;
} else {
num_count++ ;
}
}
int main(void)
{
uint8_t key = 0 ;
init_hardware() ;
cls() ;
sprintf(str, "TFT Test Program Started (%s %s)\n", __DATE__, __TIME__) ;
UART_UartPutString(str) ;
TFT_BusEnable(1) ;
TFT_set_orientation(0) ;
for(;;)
{
blink_cursor() ;
key = read_cardkb() ;
if (key) {
print_cursor(Black) ;
do_key(key) ;
}
CyDelay(LOOP_INTERVAL) ;
}
}
===============
Keybaord:
M5Stack用カード型キーボードユニット - スイッチサイエンス
TFT Display:
Adafruit 2.8" TFT Touch Shield.
https://www.adafruit.com/product/1651
But I hope that this will work with SPI driven ILI9341 TFTs...
この TFT.[ch] は他の SPI 接続の ILI9341 TFT も動くとは思います・・・
moto
Show LessWhile ago, I wrote a sample for using the I2C keyboard (CardKb)
少し前に、I2C経由のフルキーボード (CardKb)のサンプルを書く機会がありました。
I2C Full Keyboard Sample (CardKb)
And a long ago, I ported the UniGraph library from mbed and converted it from C++ to C.
(So I screamed that I want C++ !!!)
大分前に、mbed で使っていた UniGraph ライブラリを C++ から C に移植しました。
(だから C++ 欲しいっていったのに~ (T^T) )
Re: What LCD screen to use with a psoc 5lp-059 to create an LCD oscilliscope?
So I decided that it's a time to connect both of them.
で、この機会に両方をつないでみることにしました。
This time I implemented the minimum type-write like functions.
And also it supports cursor key (up, down, right, left), backspace.
ESC key to clear screen.
And the letters entered are also output to UART.
今回は最低限のタイプライタもどきな動きができるのと
カーソルキーの移動 (上、下、右、左)、バックスペースを
付けてみました。 ESC を押すと画面クリアになります。
As usual only the minimum test has been done.
But anyway you get the source code 😜
例によって、テストは最低限しか行っていませんが、
まぁ、ソースコードあるので・・・ということで (^ ^;;
Now we have keyboard input and display output,
what shall we do next?
さてキーボード入力とディスプレイ出力が付いたわけですが、
次は何をしましょう?
schematic
pins
main.c
=============
#include "project.h"
#include <stdio.h>
#define CARDKB_I2C_SLAVE_ADDR 0x5F
#include "TFT.h"
#include "Terminal6x8.h"
volatile int spi_done_flag = 0 ;
#define LOOP_INTERVAL 10 /* ms */
#define CURSOR_INTERVAL 50 /* LOOP_INTERVAL * CURSOR_INTERVAL */
int pos_x = 0 ;
int pos_y = 0 ;
char str[128] ; /* print buffer */
void print(char *str)
{
UART_PutString(str) ;
}
CY_ISR(spi_done_isr)
{
SPI_ReadTxStatus() ;
spi_done_flag = 1;
}
void cls(void)
{
print("\033c") ; /* reset */
CyDelay(100) ;
print("\033[2J") ; /* clear screen */
CyDelay(100) ;
}
uint8_t myI2C_ReadByte(uint8_t reg_addr)
{
uint8_t status ;
uint8_t value = 0 ;
I2C_MasterClearStatus();
status = I2C_MasterSendStart(CARDKB_I2C_SLAVE_ADDR, I2C_WRITE_XFER_MODE) ;
if (I2C_MSTR_NO_ERROR == status) {
I2C_MasterWriteByte(reg_addr);
status = I2C_MasterSendRestart(CARDKB_I2C_SLAVE_ADDR, I2C_READ_XFER_MODE);
}
if (I2C_MSTR_NO_ERROR == status) {
value = I2C_MasterReadByte(I2C_ACK_DATA);
}
I2C_MasterSendStop();
return(value) ;
}
uint8_t read_cardkb(void)
{
uint8_t key = 0 ;
key = myI2C_ReadByte(1) ;
return( key ) ;
}
void init_hardware(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
UART_Start() ;
TFT_CS_Write(1) ;
TFT_DC_Write(0) ;
TFT_BL_Write(1) ;
isr_tx_StartEx(spi_done_isr) ;
SPI_Start() ;
SPI_Enable() ;
TFT_Init() ;
TFT_BusEnable(1) ;
TFT_foreground(White) ;
TFT_background(Black) ;
TFT_set_orientation(0) ;
TFT_cls() ;
TFT_set_font(Terminal6x8) ;
TFT_BL_Write(1) ;
TFT_BusEnable(0) ;
I2C_Start() ;
}
void font_cursor_up(void)
{
pos_y -= TFT_font_height() ;
if (pos_y < 0) {
pos_y = TFT_height() - TFT_font_height() - 1 ;
}
}
void font_cursor_down(void)
{
pos_y += TFT_font_height() ;
if (pos_y >= (TFT_height() - TFT_font_height() - 1)) {
pos_y = 0 ;
}
}
void font_cursor_forward(void)
{
pos_x += TFT_font_width() ;
if (pos_x >= (TFT_width() - TFT_font_width() - 1)) {
pos_x = 0 ;
font_cursor_down() ;
}
}
void font_cursor_backward(void)
{
pos_x -= TFT_font_width() ;
if (pos_x < 0) {
pos_x = TFT_width() - TFT_font_width() - 1 ;
font_cursor_up() ;
}
}
void printc(uint8_t key)
{
TFT_locate(pos_x, pos_y) ;
TFT_putc(key) ;
font_cursor_forward() ;
UART_PutChar(key) ;
}
void do_key(uint8_t key)
{
switch(key) {
case 0: // no key received, skip!
break ;
case 0x1B: // ESC (clear screen for now)
pos_x = 0 ;
pos_y = 0 ;
TFT_cls() ;
break ;
case 0x0D: // Enter
pos_x = 0 ;
font_cursor_down() ;
UART_PutString("\r\n") ;
break ;
case 0x08: // BackSpace
font_cursor_backward() ;
printc(' ') ;
font_cursor_backward() ;
break ;
case 0xB4: // Left Arrow
font_cursor_backward() ;
break ;
case 0xB5: // Up Arrow
font_cursor_up() ;
break ;
case 0xB6: // Down Arrow
font_cursor_down() ;
break ;
case 0xB7: // Right Arrow
font_cursor_forward() ;
break ;
case 0x09: // Tab
// currently no support
break ;
case 0x20: // Space
default:
printc(key) ;
break ;
}
}
void print_cursor(int color)
{
int x0, x1, y0, y1 ;
x0 = pos_x ;
x1 = pos_x + TFT_font_width() ;
y0 = pos_y ;
y1 = pos_y + TFT_font_height() ;
TFT_line(x0, y1, x1, y1, color) ;
}
void blink_cursor(void)
{
static int num_count = 0 ;
static int mode = 0 ;
int16_t color ;
if (num_count >= CURSOR_INTERVAL) {
if (mode == 0) {
mode = 1 ;
color = White ;
} else {
mode = 0 ;
color = Black ;
}
print_cursor(color) ;
num_count = 0 ;
} else {
num_count++ ;
}
}
int main(void)
{
uint8_t key = 0 ;
init_hardware() ;
cls() ;
sprintf(str, "TFT Test Program Started (%s %s)\n", __DATE__, __TIME__) ;
UART_PutString(str) ;
TFT_BusEnable(1) ;
TFT_set_orientation(0) ;
for(;;)
{
blink_cursor() ;
key = read_cardkb() ;
if (key) {
print_cursor(Black) ;
do_key(key) ;
}
CyDelay(LOOP_INTERVAL) ;
}
}
=============
Keybaord:
M5Stack用カード型キーボードユニット - スイッチサイエンス
TFT Display:
Adafruit 2.8" TFT Touch Shield.
https://www.adafruit.com/product/1651
But I hope that this will work with SPI driven ILI9341 TFTs...
moto
Show LessHi,
This attached sample code is sample code for simultaneous output from 1 waveform of PWM to 7ports for LED dimming.
This is for CY8CKIT-041-40xxx PSoC4S series pioneer Kit.
Thanks and regards,
Show Less
When I saw a proximity sample in the CY8CKIT-044 kit guide, I thought that I'd like to make a theremin alike.
CY8CKIT-044 キットガイドに近接センサの例が紹介されているのをみて、テルミンのようなものを作ろうと思い立ちました。
But with CapSense v7.0, I could not figure out how to extract the signal. (It should exist somewhere though)
しかし CapSense v7.0 で信号値を直接読み出す方法が見つかりませんでした。 (きっとあるとは思うのですが・・・)
When we use the CapSense Tuner, we see the "analog" signal.
So the must the value somewhere..
キャップセンスチューナーを使用すると、ちゃんと測定値が表示されています。
ということはどこかにその値があるはずです。
So I peeked the source of run_tuner() function and there it is.
run_tuner() のソースを覗いてみたらありました。
Note: To use the CapSense Tuner, please edit the file cs_utils.h and define USE_CAPSENSE_TUNER to 1u
注:本プログラムでキャップセンスチューナーを使用する場合は、cs_utils.h 内の USE_CAPSENSE_TUNER を 1u に define してください。
Now we have the input, so let's talk about the output.
これで入力は確保できたので、出力のお話になります。
In the Orgel Sample I wrote last year, I created the note table.
去年、書いたオルゴールのサンプルで音階のテーブルが作ってありました。
TSoC CY8C4146LQI-S433 4-Voice Orgel (四声のオルゴール)
Although it had 54 notes (4 octaves?), since the effective distance range of proximity was not too large,
assigning all notes to the signal value made it a chaos.
テーブルには54の半音階音程 (4オクターブくらいかな?) あったのですが、近接センサの有効範囲が
それほど広くなかったので、その範囲に全ての音を割り当てたらカオスになりました。
As I chatted with my colleagues, I was suggested that notes enough for Froschgesang will be fine.
同僚とチャットで相談したところ、カエルの歌が出来るくらいの音程範囲でいいんじゃね?と助言されました。
So I reduced the table to just about 1 octave.
Then the output was much more bearable.
という訳で音程数を一オクターブ程度にしてみました。
すると出音は少しマシになってくれました。
========================
note_type note3[] = { /* 1 octave only */
/* count, freq, name */
{ 0, 0.00, "Rest" }, /* 0 */
{ 9555, 1046.50, "C6" }, /* 33 */
{ 8513, 1174.66, "D6" }, /* 35 */
{ 7584, 1318.51, "E6" }, /* 37 */
{ 7158, 1396.91, "F6" }, /* 38 */
{ 6377, 1567.98, "G6" }, /* 40 */
{ 5681, 1760.00, "A6" }, /* 42 */
{ 5061, 1975.53, "B6" }, /* 44 */
{ 4777, 2093.00, "C7" }, /* 45 */
{ 4256, 2349.32, "D7" }, /* 47 */
{ 3792, 2637.02, "E7" }, /* 49 */
{ 3579, 2793.83, "F7" }, /* 50 */
{ 3188, 3135.96, "G7" }, /* 52 */
{ 2840, 3520.00, "A7" } /* 54 */
} ;
========================
The real(?) Theremin has 2 handles, left one for volume and the right one for note (height).
本物(?)のテルミンでは2本腕があって、左側が音量、右側が音程になっているようです。
Although I can not control volume as I cheated by using PWM to generate sound,
I used the left hand sensor as volume. (For the time being only ON and OFF)
音の発生に PWM を使って手抜きをしてしまっているため音量の調整は出来ないのですが、
臨場感(?)の為に左側のセンサは音量に割り当てました。(今のところオンとオフしかできません)
Now the project...
さてプロジェクトですが・・・
schematic
pins
main.c
=================
#include "project.h"
#include <stdio.h>
#include <stdbool.h>
#include "tty_utils.h"
#include "cs_utils.h"
#include "note.h"
#define TONE_MIN 0
#define TONE_MAX 500
#define VOLUME_MIN 100
#define VOLUME_MAX 500
#define TONE_OFFSET 0
#define MAX_NOTE 54
void init_hardware(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
#if USE_CAPSENSE_TUNER
EZI2C_Start() ;
#else
tty_init() ;
splash("A Thermin Wannabe") ;
#endif
prepare_buffer() ;
PWM_Start() ;
CapSense_Start() ; /* Initialize CapSense Component */
CapSense_ScanAllWidgets() ; /* Scan all widgets */
}
void play_note(uint16_t tone_value, uint16_t volume_value)
{
static uint16_t prev_tone = 0 ;
static uint16_t prev_volume = 0 ;
uint16_t tone_index ;
uint16_t note_delta ;
if (tone_value > TONE_MAX) {
tone_value = TONE_MAX ;
}
if (tone_value < 0 ) {
tone_value = 0 ;
}
note_delta = (TONE_MAX - TONE_OFFSET) / num_notes ;
tone_index = TONE_OFFSET + tone_value / note_delta ;
if (tone_index >= num_notes) {
tone_index = num_notes - 1 ;
}
if ((prev_tone == tone_index) && (prev_volume == volume_value)) {
return ;
}
if ((volume_value > VOLUME_MIN) && (prev_tone != tone_index)) {
print(note[tone_index].name) ;
print("\n\r") ;
}
prev_tone = tone_index ;
prev_volume = volume_value ;
PWM_Stop() ;
PWM_WritePeriod(note[tone_index].count) ;
PWM_WriteCompare(note[tone_index].count / 2) ;
if (volume_value > VOLUME_MIN) {
PWM_Enable() ;
}
}
int main(void)
{
uint32 proxy_tone = 0 ;
uint32 proxy_volume = 0 ;
init_hardware() ;
for(;;) {
if (CapSense_IsBusy() == CapSense_NOT_BUSY) {
CapSense_ProcessAllWidgets() ;
proxy_volume = CapSense_IsProximitySensorActive(
CapSense_PROXIMITY0_WDGT_ID,
CapSense_PROXIMITY0_SNS0_ID) ;
proxy_volume = CapSense_IsProximitySensorActive(
CapSense_PROXIMITY0_WDGT_ID,
CapSense_PROXIMITY0_SNS1_ID) ;
#if !USE_CAPSENSE_TUNER
// snprintf(str, STR_BUF_LEN, "Tone: %u Volume: %u\n\r",
// cs_signal[0], cs_signal[1]) ;
// print(str) ;
play_note(cs_signal[0], cs_signal[1]) ;
#endif
cs_tuner() ;
CapSense_ScanAllWidgets() ;
}
}
}
=================
Tera Term log
Barely played the first part of the Froschgesang. (C, D, E, F, E, D, C)
テラタームの出力
かろうじてカエルの歌の冒頭を弾いた(?)ところ。(ド、レ、ミ、ファ、ミ、レ、ド)
At first I was trying to control by using only my index finger and it was very difficult,
but later I found that using palm makes control much easier.
最初、人差し指だけで制御しようとしたら非常に難しかったのですが、
手のひらを使うと随分と楽になることがわかりました。
moto
P.S. CY8CKIT-044 with 2 proximity sensor wire looks like a Kirkaldyia deyrolli.
https://en.wikipedia.org/wiki/Lethocerus_deyrollei
追伸 プロキシミティセンサ用の線を二本付けている CY8CKIT-044 はなんかタガメに似ています。
https://ja.wikipedia.org/wiki/%E3%82%BF%E3%82%AC%E3%83%A1
Show LessHi, I feel somewhat guilty about calling this a sample,
but knowing that we have access to an I2C Full Keyboard may be good!
これをサンプルと言い張るのは少しずうずうしいかもしれませんが、
I2C で接続できるフルキーボードが安価に入手できるのは良い知らせかも知れません。
In the discussion of Data entry from keyboard to PSOC
I encountered M5Stack Official CardKB Mini Keyboard Unit MEGA328P GROVE I2C USB ISP Programmer for ESP32 Arduino Development Board STE…
Data entry from keyboard to PSOC のスレで、I2C で接続できるミニキーボードを見つけました。
So I ordered a couple of these and they arrived today.
スイッチサイエンスさんのウェブにあったのでポチッたところ、今日到着しました。
M5Stack用カード型キーボードユニット - スイッチサイエンス
Pins from top to bottom are
============
1 GND
2 5V
3 SDA
4 SCL
============
schematic
pins
main.c
====================
#include "project.h"
#include "stdio.h"
#define CARDKB_I2C_SLAVE_ADDR 0x5F
#define STR_LEN 64
char str[STR_LEN+1] ;
void print(char *str)
{
UART_PutString(str) ;
}
void printc(char c)
{
UART_PutChar(c) ;
}
void cls(void)
{
print("\033c") ; /* reset */
CyDelay(20) ;
print("\033[2J") ; /* clear screen */
CyDelay(20) ;
}
void splash(void)
{
cls() ;
print("5LP CardKb Test") ;
snprintf(str, STR_LEN, "(%s %s)\n", __DATE__, __TIME__) ;
print(str) ;
}
void init_hardware(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
UART_Start() ;
splash() ;
I2C_Start() ;
}
uint8_t myI2C_ReadByte(uint8_t reg_addr)
{
uint8_t status ;
uint8_t value = 0 ;
I2C_MasterClearStatus();
status = I2C_MasterSendStart(CARDKB_I2C_SLAVE_ADDR, I2C_WRITE_XFER_MODE) ;
if (I2C_MSTR_NO_ERROR == status) {
I2C_MasterWriteByte(reg_addr);
status = I2C_MasterSendRestart(CARDKB_I2C_SLAVE_ADDR, I2C_READ_XFER_MODE);
}
if (I2C_MSTR_NO_ERROR == status) {
value = I2C_MasterReadByte(I2C_ACK_DATA);
}
I2C_MasterSendStop();
return(value) ;
}
uint8_t read_cardkb(void)
{
uint8_t key = 0 ;
key = myI2C_ReadByte(1) ;
return( key ) ;
}
int main(void)
{
uint8_t key ;
init_hardware() ;
for(;;)
{
key = read_cardkb() ;
if (key) {
// snprintf(str, STR_LEN, "%02X ", key) ;
// print(str) ;
printc(key) ;
}
CyDelay(10) ;
}
}
====================
Tera Term log
moto
Show LessSo it was early 2018, I found a sale on a 5-axis Robot Arm at Saint Smart
https://www.sainsmart.com/collections/robotics/products/s5-5-axis-desktop-robotic-arm-with-servos
それは 2018年の初頭、Saint Smart のウェブで5軸ロボットアームがセールになっているのを見つけました。
https://www.sainsmart.com/collections/robotics/products/s5-5-axis-desktop-robotic-arm-with-servos
Since it was less than $70.00 at that time, I decided that even if I will fail the damage will not be big.
まぁ、1万円で結構おつりがくる代物だったので、失敗しても、まっいいか、くらいのノリで注文しました。
As I am a man of "Think after I act", I noticed that I need a driver board between the PSoC Board and the RobotArm.
例によって“論よりRun”で行ってしまった為、サーボを動かすのにドライバ基板が必要なことに後から気が付きました。
At first I ordered a DC/DC up converter from Akizuki
http://akizukidenshi.com/catalog/g/gK-13065/
先ずは秋月で DC/DCコンバータを注文してみました。
http://akizukidenshi.com/catalog/g/gK-13065/
I thought it has enough current and voltage, but it did not work (T^T)
because it can not keep up with the PWM for the Servo.
電流も電圧も充分だったはずなのですが、動きませんでした (;_;)周波数応答性がサーボに必要な全然追いついていないようでした。
Finally I decided that it was time to ask help for my hardware colleague.
After let him laugh a while, he suggested me to get this device (IC BUFF/DVR NON-INVERT).
https://www.marutsu.co.jp/pc/i/15339510/
ようやく、これはハードウェアさんに助けを求めないとダメだなと思い、
同僚のハードウェアの匠に話に行きました。
私の選んだデバイスをみて少し笑ってから、
彼は下記のデバイスを入手するように指示してくれました。(IC BUFF/DVR NON-INVERT).
https://www.marutsu.co.jp/pc/i/15339510/
So I asked him to make me a board with following connections.
そこで、以下のような配線で基板を作ってもらえるようにお願いしました。
Now time to rock with the PSoC Creator!
さて、ようやく PSoC Creator でブイブイ言わせるところに到達しました。
Schematic
Note: Well for the robot arm, circuit is not so complicated.
I used a 5x5 Matrix Key Pad to control the arm.
注:ロボットアーム用の回路自体は簡単なものですね。
制御用に以前買って持っていた 5x5 マトリックキーパッドを使用しました。
Pins
main.c
==================
/* ========================================
* 5ch Servo Robot ARM controller
*
* According to the servo datasheets
* usual RC servo PWM is
* frequency: 50Hz = 20ms/cycle
* PWM width of center: 1.5ms = 1500 us
* Dynamic Rage is -500us ~ +500us
* which makes the PWM width from 1000us to 2000us
*
* Servos
* A: ch0 : Base
* B: ch1 : Shoulder
* C: ch2 : Elbow
* 😧 ch3 : Wrist
* E: ch4 : Grip
*
* ========================================
*/
#include "project.h"
#include "main.h"
#include "servo_ctrl.h"
#include "num_key.h"
#include <stdio.h>
#include <stdlib.h>
int gVerbose = 0 ;
#if 0 /* general servo */
#define MIN_PULSE 1000
#define CENTER_PULSE 1500
#define MAX_PULSE 2000
#else /* Servo = SG90 */
#define MIN_PULSE 500
#define CENTER_PULSE 1500
#define MAX_PULSE 2500
#endif
/****
* Key layout
* 20 21 22 23 24
* 15 16 17 18 19
* 10 11 12 12 14
* 5 6 7 8 9
* 0 1 2 3 4
*/
char str[STRING_LENGTH+1] ;
int pause[][5] = {
{1500, 1500, 1500, 1500, 1500},
{1450, 820, 640, 1460, 1480},
{1450, 1870, 1550, 1460, 990},
{1530, 2020, 1440, 1460, 1080},
{1530, 1380, 2500, 1460, 1720}
} ;
int num_pause = sizeof(pause)/(sizeof(int) * 5) ;
void doHelp(void)
{
UART_UartPutString("\n===== HELP =====\n") ;
UART_UartPutString("HELP SPEED1 SPEED2 SPEED3 SPEED4\n") ;
UART_UartPutString("REPORT pause1 Wrist<- Elbow(Up) Wrist->\n") ;
UART_UartPutString("pause0 pause2 (NC) Elbow(Down) (NC)\n") ;
UART_UartPutString("SILENT pause3 (NC) Shoulder(Up) (NC)\n") ;
UART_UartPutString("Grip(Open) Grip(Close) Base(Right) Shoulder(Down) Base(Left)\n") ;
UART_UartPutString("==================\n") ;
}
void doReport(void)
{
int i ;
UART_UartPutString("\n==== REPORT ====\n") ;
for (i = 0 ; i < 5 ; i++ ) {
print_servo(i) ;
}
if (gVerbose) {
sprintf(str, "Verbose Mode ") ;
} else {
sprintf(str, "Silent Mode ") ;
}
UART_UartPutString(str) ;
UART_UartPutString("\n==================\n") ;
}
void set_pause(void)
{
pause_to(pause[0]) ;
#if 0
int i ;
for (i = 0 ; i < NUM_CHANNEL ; i++ ) {
servo.WriteCompare(servo.value) ;
}
CyDelay(40) ;
#endif
}
void doPause(int num)
{
pause_to(pause[num]) ;
#if 0
int i, j ;
int current[NUM_CHANNEL] ;
int delta[20] ;
for (i = 0 ; i < NUM_CHANNEL ; i++) {
current = servo.value ;
delta = (pause[num] - current) / 20 ;
}
for (j = 0 ; j < 19 ; j++ ) {
for (i = 0 ; i < NUM_CHANNEL ; i++ ) {
servo.value = current + delta * j ;
}
set_pause() ;
CyDelay(40) ;
}
for (i = 0 ; i < NUM_CHANNEL ; i++) {
servo.value = pause[num] ;
}
set_pause() ;
#endif
}
void reset_pause(void)
{
pause_to(pause[0]) ;
#if 0
int i ;
for (i = 0 ; i < NUM_CHANNEL ; i++ ) {
ch_move_to(i, servo.center) ;
// servo.value = servo.center ;
}
set_pause() ;
#endif
}
void list_pause(void)
{
int i ;
for (i = 0 ; i < NUM_CHANNEL ; i++ ) {
sprintf(str, "%s[%d] ", servo.name, servo.value) ;
UART_UartPutString(str) ;
}
UART_UartPutString("\n") ;
}
void assign_servo(int ch_no, int pulse)
{
ch_move_to(ch_no, pulse) ;
#if 0
if (pulse > servo[ch_no].max) {
servo[ch_no].value = servo[ch_no].max ;
} else if (pulse < servo[ch_no].min) {
servo[ch_no].value = servo[ch_no].min ;
} else {
servo[ch_no].value = pulse ;
}
servo[ch_no].WriteCompare(servo[ch_no].value) ;
#endif
}
void doSpeed(int value)
{
int i, delta = 20;
switch(value) {
case 1: delta = 50 ; break ;
case 2: delta = 100 ; break ;
case 3: delta = 300 ; break ;
case 4: delta = 600 ; break ;
}
for (i = 0 ; i < 5 ; i++ ) {
servo.step = delta ;
}
}
void toggle_verbose(void)
{
if (gVerbose) {
UART_UartPutString("Silent Mode\n") ;
gVerbose = 0 ;
} else {
UART_UartPutString("Verbose Mode\n") ;
gVerbose = 1 ;
}
}
void doCommand(uint32_t key)
{
if (key & CH0_DOWN) { ch_down(0) ; }
if (key & CH0_UP) { ch_up(0) ; }
if (key & CH1_DOWN) { ch_down(1) ; }
if (key & CH1_UP) { ch_up(1) ; }
if (key & CH2_DOWN) { ch_down(2) ; }
if (key & CH2_UP) { ch_up(2) ; }
if (key & CH3_DOWN) { ch_down(3) ; }
if (key & CH3_UP) { ch_up(3) ; }
if (key & CH4_DOWN) { ch_down(4) ; }
if (key & CH4_UP) { ch_up(4) ; }
if (key & SPEED1) { doSpeed(1) ; }
if (key & SPEED2) { doSpeed(2) ; }
if (key & SPEED3) { doSpeed(3) ; }
if (key & SPEED4) { doSpeed(4) ; }
if (key & SHOW_HELP) { doHelp() ; }
if (key & SILENT) { toggle_verbose() ; }
if (key & REPORT) { doReport() ; }
if (key & PAUSE0) { doPause(0) ; }
if (key & PAUSE1) { doPause(1) ; }
if (key & PAUSE2) { doPause(2) ; }
if (key & PAUSE3) { doPause(3) ; }
if (key & PAUSE4) { doPause(4) ; }
}
void init_hardware(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
UART_Start() ;
PWMA_Start() ;
PWMB_Start() ;
PWMC_Start() ;
PWMD_Start() ;
PWME_Start() ;
reset_pause() ;
}
int main(void)
{
uint32_t key ;
init_hardware() ;
UART_UartPutString("Program Started\n") ;
for (;;) {
key = num_key_read() ;
if (key) {
doCommand(key) ;
if (gVerbose) {
sprintf(str, "key: %08X\n", (unsigned int)key) ;
UART_UartPutString(str) ;
}
}
CyDelay(100) ;
}
}
==================
Well, well, well, this time the story does not end here.
Although the robot arm is a little one, still it's servo has rather strong power
and the inertia of the weight of servos and arm is not ignore-able.
さて、さて、さて、いつもはここで終わりなのですが、
今回のロボットアーム、小型とはいえサーボも強力ですし、
サーボとアーム自体の重さによる慣性も無視できません。
So I skimmed some motion control webs.
And decided that I need some control over the movement.
そこでモーションコントロールに関するウェブを少しサーフィンして
動作を少し制御してやる必要があると感じました。
If I set the servo's target angle directly, the arm acts like a wild horse.
So we need to increase the velocity gradually and decrease it gradually, too.
Which is the velocity control, but to realize smooth movement we need to control
the acceleration and farther more the jerk.
もし、サーボに移動目的の角度を直接設定するとジャジャ馬のような動きになってしまいます。
その為速度を徐々に上げて、また徐々に下げる必要があります。
これが速度制御と呼ばれるものなのですが、スムーズな動作制御には
加速の制御、さらには躍度の制御が必要になるようです。
Unfortunately neither of PSoC nor I have enough CPU power for a real-time differential equation solving.
I approached it with hand curve fitting with Excel.
Ideally there should be not "edge" in the jerk curve I could not avoid a couple of them with this picture.
But still even with this level, the movement of servos were much smoother and sound was more quiet.
残念ながら PSoC も 私もリアルタイムで微分方程式を解くのに充分は CPU パワーを有していないため
エクセルを使いながら、カーブの手動フィッテングを行いました。
理想的には躍度の曲線に角が無い方が良いのですが、流石に2点残ってしまいました。
それでもこのレベルの調整で動作はかなりスムーズになりましたし、音も静かになりました。
Finally, it was difficult or almost impossible to control 5 axis same time,
I added a feature "pause #", so that I can store some number of pauses
and recall it by one button.
最後に 5軸を同時に手で制御するのはほぼ無理だったので、
"pause #" という機能を増やして、予め登録しておいた設定を
ボタン一つで呼び起せるようにしました。
moto
Show LessAlthough I posted a sample for CY8CKIT-059 a few days ago, to make it easier to find this for TSoC user I'm posting this.
数日前に CY8CKIT-059 用のサンプルをポストしましたが、TSoC 用に見つけやすいように別にポストさせていただきます。
In another topic, I encountered the following I2C keyboard while discussing a keyboard for PSoC.
別のスレで PSoC に繋ぐことのできるキーボードという話をしていたところ下記の I2C キーボードに遭遇しました。
The CardKb
I ordered mine from Switch Science
私は自分用にスイッチサイエンスさんからポチらせていただきました。
https://www.switch-science.com/catalog/5689/
This keyboard is surely not for a long text typing, but having full keyboard input capability for a MCU is nice.
長文を打つのに向いているとは言えませんが、MCU 用に手軽なフルキーボードがあるのは便利です。
schematic
Pins
Tera Term log
main.c
=============
#include "project.h"
#include "stdio.h"
#define CARDKB_I2C_SLAVE_ADDR 0x5F
#define I2C_TIMEOUT_MS 500
#define STR_LEN 64
char str[STR_LEN+1] ;
void print(char *str)
{
UART_UartPutString(str) ;
}
void printc(char c)
{
UART_UartPutChar(c) ;
}
void cls(void)
{
print("\033c") ; /* reset */
CyDelay(20) ;
print("\033[2J") ; /* clear screen */
CyDelay(20) ;
}
void splash(void)
{
cls() ;
print("TSoC CardKb Test") ;
snprintf(str, STR_LEN, "(%s %s)\n", __DATE__, __TIME__) ;
print(str) ;
}
void init_hardware(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
UART_Start() ;
splash() ;
I2C_Start() ;
}
uint8_t myI2C_ReadByte(uint8_t reg_addr)
{
uint8_t status ;
uint8_t value = 0 ;
I2C_ClearPendingInt();
status = I2C_I2CMasterSendStart(CARDKB_I2C_SLAVE_ADDR, I2C_I2C_WRITE_XFER_MODE, I2C_TIMEOUT_MS) ;
if (I2C_I2C_MSTR_NO_ERROR == status) {
I2C_I2CMasterWriteByte(reg_addr, I2C_TIMEOUT_MS);
status = I2C_I2CMasterSendRestart(CARDKB_I2C_SLAVE_ADDR, I2C_I2C_READ_XFER_MODE, I2C_TIMEOUT_MS);
}
if (I2C_I2C_MSTR_NO_ERROR == status) {
status = I2C_I2CMasterReadByte(I2C_I2C_ACK_DATA, &value, I2C_TIMEOUT_MS);
}
I2C_I2CMasterSendStop(I2C_TIMEOUT_MS);
return(value) ;
}
uint8_t read_cardkb(void)
{
uint8_t key = 0 ;
key = myI2C_ReadByte(1) ;
return( key ) ;
}
int main(void)
{
uint8_t key ;
init_hardware() ;
for(;;)
{
key = read_cardkb() ;
if (key) {
// snprintf(str, STR_LEN, "%02X ", key) ;
// print(str) ;
printc(key) ;
}
CyDelay(10) ;
}
}
=============
moto
Show LessHi,
almost every application requires to save and recall some custom settings in EEPROM between power off. Attached is simple component which accomplishes just that. All you need to do is declare some structure to hold application settings and to initialize component. Demo project attached. Tested with PSoc5LP (Creator 3.0).
Several options available: CRC8 check upon recall, variable CRC seed value (can change for different apps), variable EEPROM start address (can use multiple instances of the component to save several buffers), Temperature check for operation in adverse conditions.
odissey1
Show Less
Hi,
Provided below is custom component (MedianFilter v0.0) for filtering streamed data.
The MedianFilter component implements Phil Extrom’s sliding window median algorithm for scalar data. Sampled signals with few amounts of erroneous data can be effectively de-noised using this filter. Component doesn’t consume hardware resources, performing all operations by CPU, which is useful for systems with little resources, such as PoC4. The filter is non-decimating, producing the output on every sample added. Multiple instances of the component can be added to the project for processing independent signal streams.
Component features:
-Data range: int8, int16, int32, uint8, uint16, uint32
-Has fixed execution time
-Grows linear with size
-Non-decimating
The component was tested using CY8CKIT-059 PSoC5 prototyping kit and CY8CKIT-042 PSoC4 Pioneer Kit. Demo projects are provided.
Attached archive contains component library, component datasheet and demo projects for PSoC5 and PSoC4. Please read installation instructions in the readme.txt.
The component provided as-is, no liabilities. It is free to use and modify.
/odissey1
P.S. Demo projects use optional annotation components (which are also provided here in Support_libs.zip):
PSoC Annotation Library: PSoC Annotation Library v1.0
KIT-042 annotation stub: KIT-042: annotation component for CY8CKIT-042 Pioneer Kit
SerialPlot library: SerialPlot: interface to real-time data charts
SerialPlot open source charting tool for Linux/Windows can be downloaded here
SerialPlot charting SW: SerialPlot – Realtime Plotting Software for UART/Serial Port
Download SerialPlot: Hackaday.io: SerialPlot - Realtime Plotting Software
Figure 1. PSoC5 demo project schematic.
Figure 2. Data output using SerialPlot charting software. Black - signal w/o noise, Green - signal with disturbance, Red - FIR output, Blue - median filter output (recovered signal).
Figure 3. Project annotation using PSoC Annotation Library.
Show Less
So yesterday I encountered another chance to write a sample program for a Matrix Keypad.
Data entry from keyboard to PSOC
昨日、再びマトリクスキーパッドのサンプルを書く機会に恵まれました。
Although I'm quite sure that I have written PSoC program(s) using Matrix Keypad I could not find any in my PC. orz
So here is a yet another newly written sample of 4x4 Matrix Keypad for CY8CKIT-059.
確か、一度ならず書いていたはずなのですが例によって見つかりません。 orz
という訳で懲りずに再び CY8CKIT-059 用に書いてみました。
I hope that next time I need this, I will be find it here...
次に必要になったときには、ここで見つけられることを期待しつつ...
schematic
pins
Tera Term log
main.c
========================
#include "project.h"
#include "stdio.h"
#define NUM_ROW 4
#define NUM_COL 4
volatile uint8_t key[NUM_ROW][NUM_COL] = { 0u } ;
uint8_t letter[NUM_ROW][NUM_COL] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
} ;
volatile int row_no = 0 ;
volatile int col_no = 0 ;
void scan_key_matrix(void)
{
switch(row_no) {
case 0: ROW0_Write(1) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
case 1: ROW0_Write(0) ; ROW1_Write(1) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
case 2: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(1) ; ROW3_Write(0) ; break ;
case 3: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(1) ; break ;
default: ROW0_Write(0) ; ROW1_Write(0) ; ROW2_Write(0) ; ROW3_Write(0) ; break ;
}
switch(col_no) {
case 0: key[row_no][col_no] = COL0_Read() ; break ;
case 1: key[row_no][col_no] = COL1_Read() ; break ;
case 2: key[row_no][col_no] = COL2_Read() ; break ;
case 3: key[row_no][col_no] = COL3_Read() ; break ;
}
col_no++ ;
if (col_no >= NUM_COL) {
col_no = 0 ;
row_no = (row_no + 1) % NUM_ROW ;
}
ROW0_Write(0) ;
ROW1_Write(0) ;
ROW2_Write(0) ;
ROW3_Write(0) ;
}
CY_ISR(my_tick_isr)
{
scan_key_matrix() ;
}
#define STR_LEN 64
char str[STR_LEN+1] ;
void print(char *str)
{
UART_PutString(str) ;
}
void printc(char c)
{
UART_PutChar(c) ;
}
void cls(void)
{
print("\033c") ; /* reset */
CyDelay(20) ;
print("\033[2J") ; /* clear screen */
CyDelay(20) ;
}
void splash(void)
{
cls() ;
print("5LP 4x4 Matrix Key Test") ;
snprintf(str, STR_LEN, "(%s %s)\n", __DATE__, __TIME__) ;
print(str) ;
}
void init_hardware(void)
{
CyGlobalIntEnable; /* Enable global interrupts. */
UART_Start() ;
splash() ;
CySysTickStart() ;
CySysTickSetCallback(0, my_tick_isr) ;
}
void dump_key_matrix(void)
{
int col, row ;
for (row = 0 ; row < NUM_ROW ; row++) {
for (col = 0 ; col < NUM_COL ; col++) {
if (key[row][col]) {
snprintf(str, STR_LEN, "%c ", letter[row][col]) ;
print(str) ;
} else {
print("- ") ;
}
}
print("\n\r") ;
}
}
int main(void)
{
init_hardware() ;
for(;;)
{
cls() ;
dump_key_matrix() ;
CyDelay(1000) ;
}
}
========================
And I also compiled the project for CY8CKIT-044
おまけに CY8CKIT-044 でも動かしておきました。
In the main.c, the only difference were
重箱の角ですが、 059版との差は
> UART_PutString(str)
< UART_UartPutString(str)
> print("5LP 4x4 Matrix Key Test") ;
< print("PSoC 4 4x4 Matrix Key Test") ;
moto
Show Less