diff --git a/firmware/application/Makefile b/firmware/application/Makefile index c0dc11e..40f5b84 100644 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -15,7 +15,7 @@ $(OUTPUT_DIRECTORY)/application.out: \ CHAMELEON_ULTRA := ultra CHAMELEON_LITE := lite # What device is it? -CURRENT_DEVICE_TYPE := ${CHAMELEON_LITE} +CURRENT_DEVICE_TYPE := ${CHAMELEON_ULTRA} # Source files common to all targets @@ -26,6 +26,7 @@ SRC_FILES += \ $(PROJ_DIR)/ble_main.c \ $(PROJ_DIR)/rfid_main.c \ $(PROJ_DIR)/usb_main.c \ + $(PROJ_DIR)/rgb_marquee.c \ $(PROJ_DIR)/bsp/bsp_delay.c \ $(PROJ_DIR)/bsp/bsp_time.c \ $(PROJ_DIR)/rfid/crc_utils.c \ diff --git a/firmware/application/src/app_main.c b/firmware/application/src/app_main.c index 3537722..c0591b4 100644 --- a/firmware/application/src/app_main.c +++ b/firmware/application/src/app_main.c @@ -33,6 +33,8 @@ NRF_LOG_MODULE_REGISTER(); #include "syssleep.h" #include "tag_emulation.h" #include "usb_main.h" +#include "rgb_marquee.h" + // 定义软定时器 APP_TIMER_DEF(m_button_check_timer); // 用于按钮防抖的定时器 @@ -207,6 +209,20 @@ static void system_off_enter(void) { ret = sd_power_ram_power_set(8, ram8_retention); APP_ERROR_CHECK(ret); + + // 关机动画 + uint8_t slot = tag_emulation_get_slot(); + for (uint8_t i = 0; i < RGB_LIST_NUM; i++) { + nrf_gpio_pin_clear(hw_get_led_array()[i]); + } + uint8_t dir = slot > 3 ? 1 : 0; + // TODO 实现从指定卡槽的位置跑到头的灯效 + ledblink4(0,dir,7,99,75); + ledblink4(0,!dir,7,75,50); + ledblink4(0,dir,7,50,25); + ledblink4(0,!dir,7,25,0); + + // 需要配置为浮空模拟输入且不上下拉的IO uint32_t gpio_cfg_default_nopull[] = { #if defined(PROJECT_CHAMELEON_ULTRA) @@ -331,6 +347,7 @@ static void check_wakeup_src(void) { /**@brief button press event process */ +extern bool g_usb_led_marquee_enable; static void button_press_process(void) { // 确保AB按钮其中一个发生了点击事件 if (m_is_read_btn_press || m_is_write_btn_press) { @@ -351,6 +368,8 @@ static void button_press_process(void) { // 仅在新卡槽切换有效的情况下更新状态 if (slot_new != slot_now) { tag_emulation_change_slot(slot_new, true); // 告诉模拟卡模块我们需要切换卡槽 + g_usb_led_marquee_enable = false; + ledblink3(slot_now, 0, slot_new, 0); // 灯效 light_up_by_slot(); // 切换了卡槽,我们需要重新亮灯 set_slot_light_color(0); // 然后重新切换灯的颜色 } @@ -364,7 +383,6 @@ static void button_press_process(void) { */ int main(void) { hw_connect_init(); // 记得先把引脚初始化一下 - log_init(); // 日志初始化 gpio_te_init(); // 初始化GPIO矩阵库 app_timers_init(); // 初始化软定时器 @@ -395,6 +413,9 @@ int main(void) { while (1) { // Button event process button_press_process(); + + ledblink1(0, 0); + // Data pack process data_frame_process(); // Log print process diff --git a/firmware/application/src/rgb_marquee.c b/firmware/application/src/rgb_marquee.c new file mode 100644 index 0000000..c4606e1 --- /dev/null +++ b/firmware/application/src/rgb_marquee.c @@ -0,0 +1,340 @@ +#include "math.h" +#include "nrf_gpio.h" +#include "hw_connect.h" +#include "bsp_delay.h" +#include "rgb_marquee.h" +#include "bsp_time.h" + + +#define PWM_MAX 1000 // PWM 最大值 +#define LIGHT_LEVEL_MAX 99 // 亮度级别最大值 +static nrf_drv_pwm_t pwm0_ins = NRF_DRV_PWM_INSTANCE(1); +nrf_pwm_values_individual_t pwm_sequ_val;//独立模式下的PWM控制4通道的占空比 +nrf_pwm_sequence_t const seq = //配置pwm输出用的结构体 +{ + .values.p_individual = &pwm_sequ_val, + .length = 4, + .repeats = 0, + .end_delay = 0 +}; +nrf_drv_pwm_config_t pwm_config = //PWM配置结构体 +{ + .irq_priority = APP_IRQ_PRIORITY_LOWEST, + .base_clock = NRF_PWM_CLK_1MHz, + .count_mode = NRF_PWM_MODE_UP, + .top_value = PWM_MAX, + .load_mode = NRF_PWM_LOAD_INDIVIDUAL,//4个通道用四个值 + .step_mode = NRF_PWM_STEP_AUTO +}; +static autotimer *timer; + + +void pwmout_init(void){ + timer = bsp_obtain_timer(0); + nrf_drv_pwm_init(&pwm0_ins, &pwm_config, NULL); +} + +void pwmout_uninit(void) { + nrfx_pwm_stop(&pwm0_ins,1); + nrfx_pwm_uninit(&pwm0_ins); + bsp_return_timer(timer); +} + +// 亮度转PWM值 +uint16_t get_pwmduty(uint8_t light_level){ + return PWM_MAX-(PWM_MAX*pow(((double)light_level/LIGHT_LEVEL_MAX),2.2)); +} + +//4灯同亮度水平移动循环(不返回) +//COLOR 0-R,1-G,2-B +extern bool g_usb_connected; +extern bool g_usb_port_opened; +extern bool g_usb_led_marquee_enable; +extern void light_up_by_slot(void); +void ledblink1(uint8_t color, uint8_t dir){ + static uint8_t startled = 0; + static uint8_t setled = 0; + uint32_t* led_pins_arr; + static bool is_uninited = true; + static bool wait_next = false; + + //处理方向 + if(dir == 0){ + led_pins_arr = hw_get_led_array(); + } else{ + led_pins_arr = hw_get_led_reversal_array(); + } + + if (g_usb_connected && !g_usb_port_opened && g_usb_led_marquee_enable) { + if (is_uninited) { + //调整颜色 + set_slot_light_color(color); + pwm_sequ_val.channel_0 = 1; + pwm_sequ_val.channel_1 = 1; + pwm_sequ_val.channel_2 = 1; + pwm_sequ_val.channel_3 = 1; + pwmout_init(); + is_uninited = false; + wait_next = false; + bsp_set_timer(timer, 0); + } + + if (wait_next) { + if (!(NO_TIMEOUT_1MS(timer, 80))) { + wait_next = false; + } + } else { + setled = startled; + for(uint8_t i=0;i<4;i++){ + pwm_config.output_pins[i] = led_pins_arr[setled]; + setled++; + if(setled>7)setled=0; + } + startled++; + if(startled>7)startled = 0; + nrfx_pwm_uninit(&pwm0_ins); + nrf_drv_pwm_init(&pwm0_ins, &pwm_config, NULL); + nrf_drv_pwm_simple_playback(&pwm0_ins, &seq, 1,NRF_DRV_PWM_FLAG_LOOP); + + wait_next = true; + bsp_set_timer(timer, 0); + } + + + } else { + if (!is_uninited) { + pwmout_uninit(); + light_up_by_slot(); + } + is_uninited = true; + startled = 0; + setled = 0; + wait_next = false; + } +} + +//4灯拖尾水平移动循环(不返回),包含尾部消失和头部缓入 +//dir 0-从1卡槽到8卡槽,1-从8卡槽到1卡槽 (方向,结束点由end参数决定) +//end 要扫描的灯数量,和方向一起决定最终动画区域 +void ledblink2(uint8_t color,uint8_t dir, uint8_t end){ + uint8_t startled = 0; + uint8_t setled = 0; + uint8_t leds2turnon = 0; + uint8_t i = 0; + uint32_t * led_pins_arr; + //处理方向 + if(dir == 0){ + led_pins_arr = hw_get_led_array(); + } + else{ + led_pins_arr = hw_get_led_reversal_array(); + } + + //调整颜色 + set_slot_light_color(color); + pwm_sequ_val.channel_3 = 1; //最亮的 + pwm_sequ_val.channel_2 = 600; + pwm_sequ_val.channel_1 = 880; + pwm_sequ_val.channel_0 = 980;//最暗的 + + pwmout_init(); + while(1) { + //关闭所有通道 + pwm_config.output_pins[0] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[2] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[3] = NRF_DRV_PWM_PIN_NOT_USED; + + setled = startled; + if(setled<3){//正向期间,0,1,2的时候只能点亮前几个led + //首先确定能点亮几个灯 + leds2turnon = setled+1;//1,2,3 + //然后设置pwm输出通道 + for(i=0;i7&&setled<=10){//正向期间,8.9.10的时候只能点亮后几个led + //首先确定能点亮几个灯 + leds2turnon = 11-setled; + //然后设置pwm输出通道 + for(i=0;i=end){ + //计算需要隐藏几个灯 + leds2turnon = startled-end; + //把超出去的都隐藏了 + for(i=0;i=4)break; + if(startled>11)break; + } + pwmout_uninit(); +} + +//切换卡槽动画 +//led_up 要点亮的led +//color_led_up 要点亮的led的颜色 0-R,1-G,2-B +//led_down 要熄灭的led +//color_led_down 要熄灭的led的颜色 0-R,1-G,2-B +volatile bool callback_waiting = 0; +static void ledblink3_pwm_callback(nrfx_pwm_evt_type_t event_type){ + if (event_type == NRF_DRV_PWM_EVT_FINISHED) { + callback_waiting = 1; + } +} +void ledblink3(uint8_t led_down,uint8_t color_led_up,uint8_t led_up,uint8_t color_led_down){ + uint16_t light_level= 99;//led亮度值 + uint32_t* led_pins = hw_get_led_array(); + //先处理要熄灭的 + pwm_config.output_pins[0] = led_pins[led_down]; + pwm_config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[2] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[3] = NRF_DRV_PWM_PIN_NOT_USED; + + pwmout_init(); + while(light_level > 0){ + //处理亮度 + pwm_sequ_val.channel_0 = get_pwmduty(light_level); + + nrfx_pwm_uninit(&pwm0_ins); //关闭pwm输出 + + nrf_gpio_pin_clear(led_pins[led_up]); + + set_slot_light_color(color_led_down); + + nrf_drv_pwm_init(&pwm0_ins, &pwm_config, ledblink3_pwm_callback); + nrf_drv_pwm_simple_playback(&pwm0_ins, &seq, 1,NRF_DRV_PWM_FLAG_LOOP); + + while(callback_waiting == 0);//等待pwm模块输出完成 + bsp_delay_us(1234); + callback_waiting = 0; + light_level --; + } + + //处理要点亮的 + pwm_config.output_pins[0] = led_pins[led_up]; + pwm_config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[2] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[3] = NRF_DRV_PWM_PIN_NOT_USED; + + while(light_level < 99){ + //处理亮度 + pwm_sequ_val.channel_0 = get_pwmduty(light_level); + + nrfx_pwm_uninit(&pwm0_ins); //关闭pwm输出 + + nrf_gpio_pin_clear(led_pins[led_down]); + + set_slot_light_color(color_led_up); + + nrf_drv_pwm_init(&pwm0_ins, &pwm_config, ledblink3_pwm_callback); + nrf_drv_pwm_simple_playback(&pwm0_ins, &seq, 1,NRF_DRV_PWM_FLAG_LOOP); + + while(callback_waiting == 0);//等待pwm模块输出完成 + bsp_delay_us(1234); + callback_waiting = 0; + light_level ++; + } + pwmout_uninit(); +} + +//4灯拖尾水平移动循环(不返回),不包含尾部消失但是包含头部缓入(用于弹球类型动画) +//dir 0-从1卡槽到8卡槽,1-从8卡槽到1卡槽 (方向,结束点由end参数决定) +//end 要扫描的灯数量,和方向一起决定最终动画区域 +//start_light stop_light 0-99 表示渐变亮度 +void ledblink4(uint8_t color,uint8_t dir, uint8_t end,uint8_t start_light,uint8_t stop_light){ + uint8_t startled = 0; + uint8_t setled = 0; + uint8_t leds2turnon = 0; + uint8_t i = 0; + uint32_t * led_pins_arr; + volatile double light_cnd; + //处理方向 + if(dir == 0){ + led_pins_arr = hw_get_led_array(); + } + else{ + led_pins_arr = hw_get_led_reversal_array(); + } + + //调整颜色 + set_slot_light_color(color); + pwmout_init(); + while(1) { + //设置亮度 + //当前亮度系数 + //start经过end次数达到stop + light_cnd = (((double)stop_light-(double)start_light)/end)*startled+start_light; + pwm_sequ_val.channel_3 = get_pwmduty((uint8_t)(0.99*light_cnd));//1; //最亮的 + pwm_sequ_val.channel_2 = get_pwmduty((uint8_t)(0.60*light_cnd));//600; + pwm_sequ_val.channel_1 = get_pwmduty((uint8_t)(0.30*light_cnd));//880; + pwm_sequ_val.channel_0 = get_pwmduty((uint8_t)(0.01*light_cnd));//980;//最暗的 + //关闭所有通道 + pwm_config.output_pins[0] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[2] = NRF_DRV_PWM_PIN_NOT_USED; + pwm_config.output_pins[3] = NRF_DRV_PWM_PIN_NOT_USED; + + setled = startled; + if(setled<3){//正向期间,0,1,2的时候只能点亮前几个led + //首先确定能点亮几个灯 + leds2turnon = setled+1;//1,2,3 + //然后设置pwm输出通道 + for(i=0;i7&&setled<=10){//正向期间,8.9.10的时候只能点亮后几个led + //首先确定能点亮几个灯 + leds2turnon = 11-setled; + //然后设置pwm输出通道 + for(i=0;i=4)break; + if(startled>11)break; + } + pwmout_uninit(); +} diff --git a/firmware/application/src/rgb_marquee.h b/firmware/application/src/rgb_marquee.h new file mode 100644 index 0000000..b7689d8 --- /dev/null +++ b/firmware/application/src/rgb_marquee.h @@ -0,0 +1,13 @@ +#ifndef RGB_MARQUEE_H +#define RGB_MARQUEE_H + +#include +#include "nrf_drv_pwm.h" + + +void ledblink1(uint8_t color,uint8_t dir); +void ledblink2(uint8_t color,uint8_t dir, uint8_t end); +void ledblink3(uint8_t led_down,uint8_t color_led_up,uint8_t led_up,uint8_t color_led_down); +void ledblink4(uint8_t color,uint8_t dir, uint8_t end,uint8_t start_light,uint8_t stop_light); + +#endif diff --git a/firmware/application/src/sdk_config.h b/firmware/application/src/sdk_config.h index 4fc1783..26f13c6 100644 --- a/firmware/application/src/sdk_config.h +++ b/firmware/application/src/sdk_config.h @@ -4981,7 +4981,7 @@ #ifndef PWM1_ENABLED -#define PWM1_ENABLED 0 +#define PWM1_ENABLED 1 #endif // PWM2_ENABLED - Enable PWM2 instance diff --git a/firmware/application/src/usb_main.c b/firmware/application/src/usb_main.c index ccf71a2..8fa4a39 100644 --- a/firmware/application/src/usb_main.c +++ b/firmware/application/src/usb_main.c @@ -37,8 +37,9 @@ APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm, // USB DEFINES END // USB CODE START -static bool m_usb_connected = false; -static bool m_usb_port_opened = false; +volatile bool g_usb_connected = false; +volatile bool g_usb_port_opened = false; +volatile bool g_usb_led_marquee_enable = false; /** @brief User event handler @ref app_usbd_cdc_acm_user_ev_handler_t */ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const *p_inst, app_usbd_cdc_acm_user_event_t event) { @@ -56,15 +57,14 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const *p_inst, app_usb ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm, cdc_data_buffer, 1); UNUSED_VARIABLE(ret); NRF_LOG_INFO("CDC ACM port opened"); - m_usb_port_opened = true; + g_usb_port_opened = true; break; } case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE: NRF_LOG_INFO("CDC ACM port closed"); - m_usb_port_opened = false; - if (m_usb_connected) { - } + g_usb_port_opened = false; + g_usb_led_marquee_enable = true; break; case APP_USBD_CDC_ACM_USER_EVT_TX_DONE: @@ -91,15 +91,19 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const *p_inst, app_usb static void usbd_user_ev_handler(app_usbd_event_type_t event) { switch (event) { case APP_USBD_EVT_DRV_SUSPEND: + NRF_LOG_INFO("USB SUSPEND"); break; case APP_USBD_EVT_DRV_RESUME: + NRF_LOG_INFO("USB RESUME"); break; case APP_USBD_EVT_STARTED: + NRF_LOG_INFO("USB STARTED"); break; case APP_USBD_EVT_STOPPED: + NRF_LOG_INFO("USB STOPPED"); app_usbd_disable(); break; @@ -109,22 +113,25 @@ static void usbd_user_ev_handler(app_usbd_event_type_t event) { if (!nrf_drv_usbd_is_enabled()) { app_usbd_enable(); } + g_usb_led_marquee_enable = true; break; case APP_USBD_EVT_POWER_REMOVED: sleep_timer_start(SLEEP_DELAY_MS_USB_POWER_DISCONNECTED); NRF_LOG_INFO("USB power removed"); - m_usb_connected = false; + g_usb_connected = false; + g_usb_led_marquee_enable = false; app_usbd_stop(); break; case APP_USBD_EVT_POWER_READY: NRF_LOG_INFO("USB ready"); - m_usb_connected = true; + g_usb_connected = true; app_usbd_start(); break; default: + // NRF_LOG_INFO("Other usb event: %d", event); break; } } @@ -158,7 +165,7 @@ int fputc(int ch, FILE *f){ ch_static = ch; // must cdc is available - if (m_usb_port_opened && m_usb_connected) { + if (g_usb_port_opened && g_usb_connected) { // send and wait done. ret_code_t ret; do { diff --git a/firmware/bootloader/Makefile b/firmware/bootloader/Makefile index 227a23e..7d82760 100644 --- a/firmware/bootloader/Makefile +++ b/firmware/bootloader/Makefile @@ -16,7 +16,7 @@ $(OUTPUT_DIRECTORY)/bootloader.out: \ CHAMELEON_ULTRA := ultra CHAMELEON_LITE := lite # What device is it? -CURRENT_DEVICE_TYPE := ${CHAMELEON_LITE} +CURRENT_DEVICE_TYPE := ${CHAMELEON_ULTRA} # Source files common to all targets diff --git a/resource/public_src/hw_connect.c b/resource/public_src/hw_connect.c index 43f76a9..29c0fc8 100644 --- a/resource/public_src/hw_connect.c +++ b/resource/public_src/hw_connect.c @@ -49,10 +49,12 @@ uint32_t g_reader_power; uint32_t m_led_array[MAX_LED_NUM]; -#define INIT_LED_ARRAY(num, led) \ - if (RGB_LIST_NUM >= num) { \ - m_led_array[num - 1] = led; \ - } \ +uint32_t m_led_reversal_array[MAX_LED_NUM]; +#define INIT_LED_ARRAY(num, led) \ + if (RGB_LIST_NUM >= num) { \ + m_led_array[num - 1] = led; \ + m_led_reversal_array[RGB_LIST_NUM - num] = led; \ + } \ uint32_t m_rgb_array[MAX_RGB_NUM]; @@ -220,6 +222,10 @@ uint32_t* hw_get_led_array(void) { return m_led_array; } +uint32_t* hw_get_led_reversal_array(void) { + return m_led_reversal_array; +} + uint32_t* hw_get_rgb_array(void) { return m_rgb_array; } diff --git a/resource/public_src/hw_connect.h b/resource/public_src/hw_connect.h index f73fea1..00af634 100644 --- a/resource/public_src/hw_connect.h +++ b/resource/public_src/hw_connect.h @@ -7,7 +7,6 @@ #include "nrf_lpcomp.h" #include "device_info.h" - typedef enum { CHAMELEON_ULTRA, CHAMELEON_LITE, @@ -92,6 +91,7 @@ extern uint32_t g_reader_power; void hw_connect_init(void); void init_leds(void); uint32_t* hw_get_led_array(void); +uint32_t* hw_get_led_reversal_array(void); uint32_t* hw_get_rgb_array(void); chameleon_device_type_t hw_get_device_type(void); uint8_t hw_get_version_code(void);