本文章介紹在Raspberry Pi 3B+上建立Mosquitto MQTT Broker。並在Node-RED 與Pico W上互相Publish 與subscribe message。mosquitto 設定需要user name與password。並啟用TLS 2 way authentication功能。
有關Mosquitto MQTT Broker詳細設定步驟請參閱「MQTT 簡介:使用Mosquitto Broker」文章介紹。
本專案詳細設定步驟請參閱下列影片。
成果影片:
程式碼:
主程式:
| |
| #include "stdio.h" |
| #include "pico/stdlib.h" |
| #include "pico/cyw43_arch.h" |
| #include "picow_tls_mqtt.h" |
| #include "picow_tls_cert.h" |
| #include "lwip/apps/mqtt.h" |
| #include "lwip/altcp.h" |
| |
| #include "pio_rotary_encoder.h" |
| #include "ws2812.h" |
| |
| #define WS2812_TOTAL_PIXELS 12 |
| #define WIFI_SSID "your-ssid" |
| #define WIFI_PASSWORD "your-password" |
| #define SERVER_IP "your-mqtt-broker-ip" |
| |
| static mqtt_client_instance_t mqtt_client; |
| static int8_t rotary_value=0; |
| static uint32_t ws2812_color=0; |
| uint8_t pub_buff[1024]; |
| |
| void update_ws2812_color() { |
| for (int i=0; i < rotary_value; i++) { |
| ws2812_put_pixel_rgb(ws2812_color); |
| } |
| for (int i=rotary_value; i < WS2812_TOTAL_PIXELS; i++) { |
| ws2812_put_pixel_rgb(0x0); |
| } |
| } |
| |
| void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags) |
| { |
| mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg; |
| LWIP_UNUSED_ARG(data); |
| memcpy(client_inst->sub_message+client_inst->sub_message_offset, data, len); |
| client_inst->sub_message_offset += len; |
| if (flags == MQTT_DATA_FLAG_LAST) { |
| client_inst->sub_message_recved = true; |
| client_inst->sub_message[client_inst->sub_message_offset]=0; |
| ws2812_color = strtol(client_inst->sub_message, NULL, 16); |
| update_ws2812_color(); |
| } |
| |
| } |
| |
| void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len) |
| { |
| mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg; |
| client_inst->sub_message_offset=0; |
| client_inst->sub_message_recved=false; |
| strcpy(client_inst->sub_topic, topic); |
| } |
| |
| void mqtt_request_cb(void *arg, err_t err) |
| { |
| mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg; |
| client_inst->connected=true; |
| |
| } |
| |
| void mqtt_pub_request_cb(void *arg, err_t err) |
| { |
| mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg; |
| |
| } |
| |
| void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status) |
| { |
| mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg; |
| if (status == MQTT_CONNECT_ACCEPTED) { |
| mqtt_sub_unsub(client, |
| "ws2812_color", 1, |
| mqtt_request_cb, LWIP_CONST_CAST(void*, client_inst), |
| 1); |
| |
| } |
| } |
| |
| void rotary_encoder_cb(uint8_t action) { |
| if (action == PIO_RE_CLOCKWISE || action == PIO_RE_COUNTERCLOCKWISE) { |
| if (action == PIO_RE_CLOCKWISE) { |
| rotary_value -= 1; |
| if (rotary_value < 0) rotary_value=0; |
| } |
| if (action == PIO_RE_COUNTERCLOCKWISE) { |
| rotary_value += 1; |
| if (rotary_value > 12) rotary_value=12; |
| } |
| } |
| } |
| |
| int main() |
| { |
| int wifi_stat; |
| err_t mqtt_stat; |
| stdio_init_all(); |
| if (cyw43_arch_init()) { |
| printf("cyw43 init error\n"); |
| return 0; |
| } |
| pio_rotary_encoder_default_init(); |
| pio_rotary_encoder_set_callback(rotary_encoder_cb); |
| ws2812_default_pio_init(); |
| |
| for (int i=0; i < WS2812_TOTAL_PIXELS; i++) ws2812_put_pixel(0x0); |
| sleep_ms(50); |
| |
| ws2812_put_pixel_rgb(0xff0000); |
| |
| |
| cyw43_arch_enable_sta_mode(); |
| wifi_stat = cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000); |
| if (wifi_stat) { |
| printf("wifi connect error:%d\n", wifi_stat); |
| return 0; |
| } |
| printf("Wifi connected\n"); |
| |
| ws2812_put_pixel_rgb(0xff); |
| |
| |
| mqtt_client.tls_config = altcp_tls_create_config_client_2wayauth(ca_cert, sizeof(ca_cert), mqtt_client_key, sizeof(mqtt_client_key), NULL, 0, mqtt_client_cert, sizeof(mqtt_client_cert)); |
| assert(mqtt_client.tls_config); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| ipaddr_aton(SERVER_IP, &mqtt_client.ipaddr); |
| |
| |
| mqtt_client.mqtt_connect_client_info = (struct mqtt_connect_client_info_t){ |
| "clientid", |
| "username", |
| "password", |
| 100, |
| NULL, |
| NULL, |
| 0, |
| 0, |
| #if LWIP_ALTCP && LWIP_ALTCP_TLS |
| mqtt_client.tls_config |
| #endif |
| }; |
| |
| mqtt_client.client = mqtt_client_new(); |
| |
| |
| mqtt_set_inpub_callback(mqtt_client.client, |
| mqtt_incoming_publish_cb, |
| mqtt_incoming_data_cb, |
| LWIP_CONST_CAST(void*, &mqtt_client.mqtt_connect_client_info)); |
| mqtt_client.connected=false; |
| cyw43_arch_lwip_begin(); |
| mqtt_stat = mqtt_client_connect(mqtt_client.client, |
| &mqtt_client.ipaddr, LWIP_IANA_PORT_SECURE_MQTT, |
| mqtt_connection_cb, LWIP_CONST_CAST(void*, &mqtt_client), |
| &mqtt_client.mqtt_connect_client_info); |
| cyw43_arch_lwip_end(); |
| |
| absolute_time_t timeout = make_timeout_time_ms(10000); |
| while(!mqtt_client.connected && absolute_time_diff_us(get_absolute_time(), timeout) > 0) { |
| sleep_ms(10); |
| cyw43_arch_poll(); |
| printf("."); |
| } |
| printf("\n"); |
| if (!mqtt_client.connected) { |
| printf("connect mqtt server:timeout\n"); |
| return 0; |
| } |
| |
| printf("mqtt connected\n"); |
| |
| ws2812_put_pixel_rgb(0x00ff00); |
| ws2812_color = 0x00ff00; |
| rotary_value = 1; |
| |
| sprintf(pub_buff, "%02d", rotary_value); |
| mqtt_publish(mqtt_client.client, "ROTARY", pub_buff, 2, 1, 0, mqtt_pub_request_cb, &mqtt_client); |
| |
| uint8_t pre_rotary_value=rotary_value; |
| uint32_t pre_ws2812_color=ws2812_color; |
| |
| while(1) { |
| |
| cyw43_arch_poll(); |
| sleep_ms(10); |
| if (pre_rotary_value != rotary_value) { |
| sprintf(pub_buff, "%02d", rotary_value); |
| mqtt_publish(mqtt_client.client, "ROTARY", pub_buff, 2, 1, 0, mqtt_pub_request_cb, &mqtt_client); |
| update_ws2812_color(); |
| pre_rotary_value = rotary_value; |
| } |
| } |
| cyw43_arch_deinit(); |
| return 0; |
| } |
| |
| |
| #ifndef __PICOW_TLS_CLIENT_H__ |
| #define __PICOW_TLS_CLIENT_H__ |
| #include "lwip/pbuf.h" |
| #include "lwip/altcp_tcp.h" |
| #include "lwip/altcp_tcp.h" |
| #include "lwip/altcp_tls.h" |
| #include "lwip/dns.h" |
| #include "lwip/apps/mqtt.h" |
| #define RECV_BUFF_MAX_LEN 1024 |
| |
| typedef struct { |
| mqtt_client_t *client; |
| struct altcp_tls_config *tls_config; |
| ip_addr_t ipaddr; |
| struct mqtt_connect_client_info_t mqtt_connect_client_info; |
| bool connected; |
| uint8_t sub_topic[256]; |
| uint8_t sub_message[RECV_BUFF_MAX_LEN]; |
| uint16_t sub_message_offset; |
| bool sub_message_recved; |
| } mqtt_client_instance_t; |
| |
| #endif |
| |
| #define ca_cert "-----BEGIN CERTIFICATE-----\n\ |
| ... |
| -----END CERTIFICATE-----\n" |
| |
| #define mqtt_client_cert "-----BEGIN CERTIFICATE-----\n\ |
| ... |
| -----END CERTIFICATE-----" |
| |
| #define mqtt_client_key "-----BEGIN RSA PRIVATE KEY-----\n\ |
| ... |
| -----END RSA PRIVATE KEY-----" |
| |
| |
| # Generated Cmake Pico project file |
| |
| cmake_minimum_required(VERSION 3.13) |
| |
| set(CMAKE_C_STANDARD 11) |
| set(CMAKE_CXX_STANDARD 17) |
| |
| # Initialise pico_sdk from installed location |
| # (note this can come from environment, CMake cache etc) |
| set(PICO_SDK_PATH "/home/duser/pico/pico-sdk") |
| |
| set(PICO_BOARD pico_w CACHE STRING "Board type") |
| |
| # Pull in Raspberry Pi Pico SDK (must be before project) |
| include(pico_sdk_import.cmake) |
| |
| if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0") |
| message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") |
| endif() |
| |
| project(picow_tls_mqtt C CXX ASM) |
| |
| # Initialise the Raspberry Pi Pico SDK |
| pico_sdk_init() |
| |
| # Add executable. Default name is the project name, version 0.1 |
| |
| add_executable(picow_tls_mqtt picow_tls_mqtt.c) |
| |
| pico_set_program_name(picow_tls_mqtt "picow_tls_client") |
| pico_set_program_version(picow_tls_mqtt "0.1") |
| |
| pico_enable_stdio_uart(picow_tls_mqtt 1) |
| pico_enable_stdio_usb(picow_tls_mqtt 0) |
| |
| # Add the standard library to the build |
| target_link_libraries(picow_tls_mqtt |
| pico_stdlib) |
| |
| # Add the standard include files to the build |
| target_include_directories(picow_tls_mqtt PRIVATE |
| ${CMAKE_CURRENT_LIST_DIR} |
| ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required |
| ) |
| |
| # Add any user requested libraries |
| target_link_libraries(picow_tls_mqtt |
| pico_cyw43_arch_lwip_poll |
| pico_lwip_mqtt |
| pico_lwip_mbedtls |
| pico_mbedtls |
| ) |
| add_subdirectory(rotary_encoder) |
| target_link_libraries(picow_tls_mqtt |
| rotary_encoder |
| ) |
| |
| add_subdirectory(ws2812) |
| target_link_libraries(picow_tls_mqtt |
| ws2812 |
| ) |
| |
| pico_add_extra_outputs(picow_tls_mqtt) |
| |
| #ifndef __LWIPOPTS_H__ |
| #define __LWIPOPTS_H__ |
| |
| |
| |
| |
| |
| |
| #ifndef NO_SYS |
| #define NO_SYS 1 |
| #endif |
| |
| #ifndef LWIP_SOCKET |
| #define LWIP_SOCKET 0 |
| #endif |
| #if PICO_CYW43_ARCH_POLL |
| #define MEM_LIBC_MALLOC 1 |
| #else |
| |
| #define MEM_LIBC_MALLOC 0 |
| #endif |
| #define MEM_ALIGNMENT 4 |
| #define MEM_SIZE 4000 |
| #define MEMP_NUM_TCP_SEG 32 |
| #define MEMP_NUM_ARP_QUEUE 10 |
| #define PBUF_POOL_SIZE 24 |
| #define LWIP_ARP 1 |
| #define LWIP_ETHERNET 1 |
| #define LWIP_ICMP 1 |
| #define LWIP_RAW 1 |
| #define TCP_WND (8 * TCP_MSS) |
| #define TCP_MSS 1460 |
| #define TCP_SND_BUF (8 * TCP_MSS) |
| #define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) |
| #define LWIP_NETIF_STATUS_CALLBACK 1 |
| #define LWIP_NETIF_LINK_CALLBACK 1 |
| #define LWIP_NETIF_HOSTNAME 1 |
| #define LWIP_NETCONN 0 |
| #define MEM_STATS 0 |
| #define SYS_STATS 0 |
| #define MEMP_STATS 0 |
| #define LINK_STATS 0 |
| |
| #define LWIP_CHKSUM_ALGORITHM 3 |
| #define LWIP_DHCP 1 |
| #define LWIP_IPV4 1 |
| #define LWIP_TCP 1 |
| #define LWIP_UDP 1 |
| #define LWIP_DNS 1 |
| #define LWIP_TCP_KEEPALIVE 1 |
| #define LWIP_NETIF_TX_SINGLE_PBUF 1 |
| #define DHCP_DOES_ARP_CHECK 0 |
| #define LWIP_DHCP_DOES_ACD_CHECK 0 |
| |
| #ifndef NDEBUG |
| #define LWIP_DEBUG 1 |
| #define LWIP_STATS 1 |
| #define LWIP_STATS_DISPLAY 1 |
| #endif |
| |
| #define ETHARP_DEBUG LWIP_DBG_OFF |
| #define NETIF_DEBUG LWIP_DBG_OFF |
| #define PBUF_DEBUG LWIP_DBG_OFF |
| #define API_LIB_DEBUG LWIP_DBG_OFF |
| #define API_MSG_DEBUG LWIP_DBG_OFF |
| #define SOCKETS_DEBUG LWIP_DBG_OFF |
| #define ICMP_DEBUG LWIP_DBG_OFF |
| #define INET_DEBUG LWIP_DBG_OFF |
| #define IP_DEBUG LWIP_DBG_OFF |
| #define IP_REASS_DEBUG LWIP_DBG_OFF |
| #define RAW_DEBUG LWIP_DBG_OFF |
| #define MEM_DEBUG LWIP_DBG_OFF |
| #define MEMP_DEBUG LWIP_DBG_OFF |
| #define SYS_DEBUG LWIP_DBG_OFF |
| #define TCP_DEBUG LWIP_DBG_OFF |
| #define TCP_INPUT_DEBUG LWIP_DBG_OFF |
| #define TCP_OUTPUT_DEBUG LWIP_DBG_OFF |
| #define TCP_RTO_DEBUG LWIP_DBG_OFF |
| #define TCP_CWND_DEBUG LWIP_DBG_OFF |
| #define TCP_WND_DEBUG LWIP_DBG_OFF |
| #define TCP_FR_DEBUG LWIP_DBG_OFF |
| #define TCP_QLEN_DEBUG LWIP_DBG_OFF |
| #define TCP_RST_DEBUG LWIP_DBG_OFF |
| #define UDP_DEBUG LWIP_DBG_OFF |
| #define TCPIP_DEBUG LWIP_DBG_OFF |
| #define PPP_DEBUG LWIP_DBG_OFF |
| #define SLIP_DEBUG LWIP_DBG_OFF |
| #define DHCP_DEBUG LWIP_DBG_OFF |
| |
| |
| #define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL+1) |
| |
| |
| |
| |
| #undef TCP_WND |
| #define TCP_WND 16384 |
| |
| #define LWIP_ALTCP 1 |
| #define LWIP_ALTCP_TLS 1 |
| #define LWIP_ALTCP_TLS_MBEDTLS 1 |
| |
| #define LWIP_DEBUG 1 |
| #define ALTCP_MBEDTLS_DEBUG LWIP_DBG_ON |
| |
| |
| #define ALTCP_MBEDTLS_AUTHMODE MBEDTLS_SSL_VERIFY_REQUIRED |
| |
| #endif |
| |
| |
| |
| #include < limits.h > |
| |
| #define MBEDTLS_NO_PLATFORM_ENTROPY |
| #define MBEDTLS_ENTROPY_HARDWARE_ALT |
| |
| #define MBEDTLS_SSL_OUT_CONTENT_LEN 2048 |
| |
| #define MBEDTLS_ALLOW_PRIVATE_ACCESS |
| #define MBEDTLS_HAVE_TIME |
| |
| #define MBEDTLS_CIPHER_MODE_CBC |
| #define MBEDTLS_ECP_DP_SECP192R1_ENABLED |
| #define MBEDTLS_ECP_DP_SECP224R1_ENABLED |
| #define MBEDTLS_ECP_DP_SECP256R1_ENABLED |
| #define MBEDTLS_ECP_DP_SECP384R1_ENABLED |
| #define MBEDTLS_ECP_DP_SECP521R1_ENABLED |
| #define MBEDTLS_ECP_DP_SECP192K1_ENABLED |
| #define MBEDTLS_ECP_DP_SECP224K1_ENABLED |
| #define MBEDTLS_ECP_DP_SECP256K1_ENABLED |
| #define MBEDTLS_ECP_DP_BP256R1_ENABLED |
| #define MBEDTLS_ECP_DP_BP384R1_ENABLED |
| #define MBEDTLS_ECP_DP_BP512R1_ENABLED |
| #define MBEDTLS_ECP_DP_CURVE25519_ENABLED |
| #define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED |
| #define MBEDTLS_PKCS1_V15 |
| #define MBEDTLS_SHA256_SMALLER |
| #define MBEDTLS_SSL_SERVER_NAME_INDICATION |
| #define MBEDTLS_AES_C |
| #define MBEDTLS_ASN1_PARSE_C |
| #define MBEDTLS_BIGNUM_C |
| #define MBEDTLS_CIPHER_C |
| #define MBEDTLS_CTR_DRBG_C |
| #define MBEDTLS_ENTROPY_C |
| #define MBEDTLS_ERROR_C |
| #define MBEDTLS_MD_C |
| #define MBEDTLS_MD5_C |
| #define MBEDTLS_OID_C |
| #define MBEDTLS_PKCS5_C |
| #define MBEDTLS_PK_C |
| #define MBEDTLS_PK_PARSE_C |
| #define MBEDTLS_PLATFORM_C |
| #define MBEDTLS_RSA_C |
| #define MBEDTLS_SHA1_C |
| #define MBEDTLS_SHA224_C |
| #define MBEDTLS_SHA256_C |
| #define MBEDTLS_SHA512_C |
| #define MBEDTLS_SSL_CLI_C |
| #define MBEDTLS_SSL_SRV_C |
| #define MBEDTLS_SSL_TLS_C |
| #define MBEDTLS_X509_CRT_PARSE_C |
| #define MBEDTLS_X509_USE_C |
| #define MBEDTLS_AES_FEWER_TABLES |
| |
| |
| #define MBEDTLS_SSL_PROTO_TLS1_2 |
| #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED |
| #define MBEDTLS_GCM_C |
| #define MBEDTLS_ECDH_C |
| #define MBEDTLS_ECP_C |
| #define MBEDTLS_ECDSA_C |
| #define MBEDTLS_ASN1_WRITE_C |
| |
| |
| #define MBEDTLS_PEM_PARSE_C |
| #define MBEDTLS_BASE64_C |
| |
Rotary Encoder:
| |
| #include "pico/stdio.h" |
| #include "pico/stdlib.h" |
| #include "hardware/pio.h" |
| #include "hardware/gpio.h" |
| |
| #include "pio_rotary_encoder.h" |
| #include "rotary_encoder.pio.h" |
| #include "hardware/clocks.h" |
| |
| void (*pio_callback)(uint8_t action); |
| |
| void rotary_encoder_irq_handler() { |
| if (pio_interrupt_get(RE_PIO, ROTARY_INT_NUM)) { |
| pio_interrupt_clear(RE_PIO, ROTARY_INT_NUM); |
| uint32_t act = pio_sm_get_blocking(RE_PIO, RE_SM_ROTARY); |
| if (act == PIO_RE_CLOCKWISE || act == PIO_RE_COUNTERCLOCKWISE) { |
| pio_callback((uint8_t)act); |
| } else { |
| pio_callback(PIO_RE_UNKNOW); |
| } |
| } |
| if (pio_interrupt_get(RE_PIO, SWITCH_INT_NUM)) { |
| pio_interrupt_clear(RE_PIO, SWITCH_INT_NUM); |
| uint8_t action = PIO_RE_SWPRESS; |
| |
| pio_callback(action); |
| } |
| |
| |
| } |
| |
| void pio_rotary_switch_init(PIO pio, uint sm, uint in_base, uint freq) { |
| gpio_pull_up(in_base); |
| |
| pio_sm_config c; |
| uint offset = pio_add_program(pio, &rotary_switch_program); |
| |
| c = rotary_switch_program_get_default_config(offset); |
| |
| pio_gpio_init(pio, in_base); |
| |
| pio_sm_set_consecutive_pindirs(pio, sm, in_base, 1,false); |
| |
| sm_config_set_in_pins(&c, in_base); |
| sm_config_set_in_shift(&c, false, false, 1); |
| |
| float div = clock_get_hz(clk_sys)/freq; |
| sm_config_set_clkdiv(&c, div); |
| |
| uint pio_irq_num = (pio==pio0) ? PIO0_IRQ_0: PIO1_IRQ_0; |
| pio_set_irq0_source_enabled(pio, pis_interrupt0, true); |
| pio_set_irq0_source_enabled(pio, pis_interrupt1, true); |
| irq_add_shared_handler(pio_irq_num, rotary_encoder_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); |
| irq_set_enabled(pio_irq_num, true); |
| pio_sm_init(pio, sm, offset, &c); |
| pio_sm_set_enabled(pio, sm, true); |
| } |
| |
| void pio_rotary_encoder_init(PIO pio, uint sm, uint in_base, uint freq) { |
| pio_sm_config c; |
| uint offset = pio_add_program(pio, &rotary_encoder_program); |
| |
| c = rotary_encoder_program_get_default_config(offset); |
| |
| for (int i=0; i < 2; i++) pio_gpio_init(pio, in_base+i); |
| |
| pio_sm_set_consecutive_pindirs(pio, sm, in_base, 2,false); |
| |
| sm_config_set_in_pins(&c, in_base); |
| sm_config_set_in_shift(&c, false, false, 2); |
| |
| float div = clock_get_hz(clk_sys)/freq; |
| sm_config_set_clkdiv(&c, div); |
| |
| uint pio_irq_num = (pio==pio0) ? PIO0_IRQ_0: PIO1_IRQ_0; |
| pio_set_irq0_source_enabled(pio, pis_interrupt0, true); |
| |
| irq_add_shared_handler(pio_irq_num, rotary_encoder_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); |
| irq_set_enabled(pio_irq_num, true); |
| pio_sm_init(pio, sm, offset, &c); |
| pio_sm_set_enabled(pio, sm, true); |
| } |
| |
| |
| |
| void pio_rotary_encoder_default_init() { |
| pio_rotary_switch_init(RE_PIO, RE_SM_SWITCH, RE_SW_PIN, 2000); |
| pio_rotary_encoder_init(RE_PIO, RE_SM_ROTARY, RE_CLK_DT_PIN, RE_SM_FREQ); |
| } |
| void pio_rotary_encoder_set_callback(void (*cb)(uint8_t action)) { |
| pio_callback=cb; |
| } |
| |
| #ifndef _PIO_ROTARY_ENCODER_H_ |
| #define _PIO_ROTARY_ENCODER_H_ |
| #include "hardware/pio.h" |
| |
| #define RE_PIO pio0 |
| #define RE_CLK_DT_PIN 16 |
| #define RE_SW_PIN 18 |
| #define RE_PIO pio0 |
| #define RE_SM_ROTARY (0) |
| #define RE_SM_SWITCH (1) |
| #define RE_SM_FREQ 100000 |
| enum PIO_RE_ACTION{ |
| PIO_RE_CLOCKWISE=1, |
| PIO_RE_COUNTERCLOCKWISE=2, |
| PIO_RE_SWPRESS=3, |
| PIO_RE_UNKNOW=4 |
| }; |
| |
| void pio_rotary_switch_init(PIO pio, uint sm, uint in_base, uint freq); |
| void pio_rotary_encoder_init(PIO pio, uint sm, uint in_base, uint freq); |
| void pio_rotary_encoder_default_init(); |
| void pio_rotary_encoder_set_callback(void (*cb)(uint8_t action)); |
| |
| |
| #endif |
| |
| .define public ROTARY_INT_NUM 0 |
| .define public SWITCH_INT_NUM 1 |
| .program rotary_encoder |
| start: |
| in pins, 2 [31] |
| mov x, isr ; CLK, DT present state |
| mov y, osr ; CLK, DT previous state |
| jmp x!=y, rotary ; not eaqual, rotary occure |
| jmp saveoldvalue ; clear ISR |
| rotary: |
| set y, 0b01 ; |
| jmp x!=y, pos10 |
| jmp check_dir |
| pos10: |
| set y, 0b10 |
| jmp x!=y, saveoldvalue |
| jmp check_dir |
| saveoldvalue: |
| mov osr, isr |
| in NULL, 32 ;; imporant to clear ISR |
| jmp start |
| |
| check_dir: |
| out y,1 ; previous state DT bit |
| mov osr,x ; save oldvalue |
| in NULL, 32 ; clear ISR |
| in x, 1 ; shift in present state DT bit |
| mov x, isr ; present state DT bit |
| jmp x!=y counterclockwise |
| jmp clockwise |
| |
| clockwise: |
| set y,1 ; clockwise: value = 1 |
| jmp output |
| counterclockwise: |
| set y,2 ; counterclockwise: value = 2 |
| output: |
| mov isr, y |
| push |
| irq wait ROTARY_INT_NUM |
| jmp start |
| |
| .program rotary_switch |
| wait 0 pin 0 [31] ; Switch PIN is initially pulled up |
| irq wait SWITCH_INT_NUM ; [31] wait for button bounce |
| wait 1 pin 0 [31] |
| |
| |
| |
| |
| add_library(rotary_encoder INTERFACE) |
| pico_generate_pio_header(rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/rotary_encoder.pio) |
| target_sources(rotary_encoder INTERFACE |
| ${CMAKE_CURRENT_LIST_DIR}/pio_rotary_encoder.c |
| ) |
| |
| target_include_directories(rotary_encoder INTERFACE |
| ${CMAKE_CURRENT_LIST_DIR} |
| |
| ) |
| |
| target_link_libraries(rotary_encoder INTERFACE |
| hardware_pio |
| ) |
| |
ws2812:
| |
| #include "pico/stdio.h" |
| #include "pico/stdlib.h" |
| #include "hardware/pio.h" |
| #include "hardware/clocks.h" |
| #include "ws2812.pio.h" |
| #include "ws2812.h" |
| |
| void ws2812_pio_init(PIO pio, uint sm, uint pin, float freq) { |
| pio_gpio_init(pio, pin); |
| pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); |
| |
| uint offset = pio_add_program(pio, &ws2812_program); |
| |
| pio_sm_config c = ws2812_program_get_default_config(offset); |
| sm_config_set_sideset_pins(&c, pin); |
| sm_config_set_out_shift(&c, false, true, 24); |
| sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); |
| |
| int cycles_per_bit = 6; |
| float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); |
| sm_config_set_clkdiv(&c, div); |
| |
| pio_sm_init(pio, sm, offset, &c); |
| pio_sm_set_enabled(pio, sm, true); |
| } |
| |
| void ws2812_put_pixel(uint32_t pixel_grb) { |
| pio_sm_put_blocking(WS2812_PIO, WS2812_SM, pixel_grb << 8u); |
| } |
| void ws2812_put_pixel_rgb(uint32_t pixel_rgb) { |
| uint32_t grb = ((pixel_rgb & 0xff00) >> 8) << 16 | ((pixel_rgb & 0xff0000) >> 16) << 8 | pixel_rgb &0xff; |
| ws2812_put_pixel(grb); |
| } |
| void ws2812_default_pio_init() { |
| ws2812_pio_init(WS2812_PIO, WS2812_SM, WS2812_PIN, WS2812_PIO_FREQ); |
| } |
| |
| #ifndef _WS2812_H_ |
| #define _WS2812_H_ |
| |
| #include "hardware/pio.h" |
| |
| #define WS2812_PIO pio1 |
| #define WS2812_SM (1) |
| #define WS2812_PIN (19) |
| #define WS2812_PIO_FREQ (800000) |
| |
| void ws2812_pio_init(PIO pio, uint sm, uint pin, float freq); |
| void ws2812_default_pio_init(); |
| void ws2812_put_pixel(uint32_t pixel_grb); |
| void ws2812_put_pixel_rgb(uint32_t pixel_rgb); |
| #endif |
| |
| .program ws2812 |
| .side_set 1 |
| |
| .wrap_target |
| bitloop: |
| out x, 1 side 0 [1] |
| jmp !x do_zero side 1 [1] |
| do_one: |
| jmp bitloop side 1 [1] |
| do_zero: |
| nop side 0 [1] |
| .wrap |
| |
| add_library(ws2812 INTERFACE) |
| pico_generate_pio_header(ws2812 ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio) |
| target_sources(ws2812 INTERFACE |
| ${CMAKE_CURRENT_LIST_DIR}/ws2812.c |
| ) |
| |
| target_include_directories(ws2812 INTERFACE |
| ${CMAKE_CURRENT_LIST_DIR} |
| |
| ) |
| |
| target_link_libraries(ws2812 INTERFACE |
| hardware_pio |
| ) |
| |
沒有留言:
張貼留言