本帖最后由 qintian0303 于 2022-9-14 15:15 编辑
概述 非常感谢21ic论坛和雅特力组织这次活动,让我们又一次认识了以为新朋友,很快就收到了开发板,不过只有一个裸板,还因为运输问题出现了部分引脚的的损坏,不过不影响本次测试,大力的将它复原就可以了。 开发板收到后就对上电欣赏了一下内置的程序,简单的LED操作,这些不重要,重要的还是要先了解一下开发板的资源,开展系列测试计划。
资源分析 开发板板载AT-Link下载工具; 使用的是AT32F403CCT6作为Link的主控芯片,M4核,最高150 MHz工作频率,不过只能分频到144; 芯片默认是FLASH启动; 射频天线器件包含一个板载PCB天线和进行专业性能测试的SMA接口; 板载的LED分别是红、黄、绿各一个,供电指示灯个人建议用绿色的更好; 用户按键使用了不同颜色的按键作为区分,不过真正可以进行功能按键的只有一个; SPI,IIC,串口等接口都有,种类齐全不过接口数量少;
需求分析
由以上资源的所支撑的,我们初步对测试内容和资源进行分析和分配,基本原则如下: SPI接口的常用器件有存储器(例如W25Q系列),支持SPI的TFT等,本次测试使用支持SPI通信的TFT,有利于整个测试过程的状态展示; IIC接口一般是各种传感器使用,本次选用AHT20温湿度传感器进行展示; 串口可以通过PC测试,同时该开发板所使用的的芯片中的内置蓝牙同样是使用串口通信; 确定这些基本测试后并结合板载的LED和按键,基本就能满足本次测试需要。 初步的需求分析思维导图:
具体实现 根据思维导图我们对于具体的需求进行实际的分析。 首先作为这里的唯一输入对象,按键的识别其实是最主要的,其次是显示功能,然后根据各个界面去实现对应的功能,当然在这些操作之前,给单片机上一下发条还是非常必须的(时钟配置); 1)时钟配置 时钟配置离不开芯片的时钟树,可以清晰直观的进行各部时钟配置的选择;
其实在头文件中就有头文件中 EXTERN SystemInit就是执行的的时钟配置,在system_at32wb415.c的SystemInit如下:
由上图可以看到是直接进行的寄存器赋值,这样其实是不适合我们直接修改操作的,不易理解还容易出错,所以在这之外雅特力在main中又进行了一遍时钟配置system_clock_config();可以通过修改里面的寄存器配置。AT32WB415CCU7-7目前不考虑功耗,由于USB的存在必须能够通过PLLCLK分频得到48M,所以PLLCLK在150M的限值下最大可以设计成144,HEXT通过18倍频后得到PLLCLK。
2)按键配置 按键作为目前开发板的唯一输入,准确快速获取其状态是非常有必要的,本次采用外部中断+定时器的方式来进行按键状态的判断,注意所有引脚都可以被配置为外部中断。 定时器配置:基本定时器是用定时器10,注意分频的计算,Period对应定时ms; void Tmr10Base_init(uint16_t Period)
{
/* enable tmr10 clock */
crm_periph_clock_enable(CRM_TMR10_PERIPH_CLOCK, TRUE);
/* tmr1 configuration */
/* time base configuration */
/* systemclock/14400/10000 = 1hz */
tmr_base_init(TMR10, Period * 10 - 1, 14400 - 1);
tmr_cnt_dir_set(TMR10, TMR_COUNT_UP);
/* overflow interrupt enable */
tmr_interrupt_enable(TMR10, TMR_OVF_INT, TRUE);
/* tmr10 overflow interrupt nvic init */
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(TMR1_OVF_TMR10_IRQn, 0, 2);
/* enable tmr10 */
tmr_counter_enable(TMR10, TRUE);
}
外部中断配置:按键对应的IO为PA0void port_EXIT_init(void)
{
exint_init_type exint_init_struct;
crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOA, GPIO_PINS_SOURCE0);
exint_default_para_init(&exint_init_struct);
exint_init_struct.line_enable = TRUE;
exint_init_struct.line_mode = EXINT_LINE_INTERRUPUT;
exint_init_struct.line_select = EXINT_LINE_0;
exint_init_struct.line_polarity = EXINT_TRIGGER_RISING_EDGE;
exint_init(&exint_init_struct);
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(EXINT0_IRQn, 1, 0);
}
到这里只是一个基本功能的配置,为了实现不阻塞,所有的操作都是对标志位的修改,为此建立一个结构体;typedef struct
{
uint8_t EXIT_flag; //中断触发标志
uint8_t EXIT_Data; //中断触发防抖计时
uint8_t Press_flag; //按键触发标志
uint8_t Press_Data; //按键类型判断计时
uint8_t ShortPress_flag; //短按触发标志
uint8_t LongPress_flag; //长按触发标志
}key;
key key_Ok;
在外部中断中只进行EXIT_flag的复位,最主要是通过定时器中进行防抖和按键识别: if(key_Ok.EXIT_flag == 1)//防抖判断
3)tft驱动 首先了解一下本次使用的tft,1.54吋,IPS高清显示,分辨率240*240,驱动芯片为ST7789,支持MCU和SPI两种通信方式,本次通过SPI方式驱动液晶屏,相对来说分辨率不低,液晶屏SPI通信时只能作为从机接收数据,所以只用到了MOSI和SCK,CS和RS通过软件控制,复位和背光控制通过软件控制。 初始化代码: #define CS_OUT0 gpio_bits_reset(GPIOA,GPIO_PINS_4)
#define CS_OUT1 gpio_bits_set(GPIOA,GPIO_PINS_4)
#define TFT_RS_reset gpio_bits_reset(GPIOA,GPIO_PINS_5)
#define TFT_RS_set gpio_bits_set(GPIOA,GPIO_PINS_5)
#define TFT_RESET_reset gpio_bits_reset(GPIOA,GPIO_PINS_3)
#define TFT_RESET_set gpio_bits_set(GPIOA,GPIO_PINS_3)
#define TFT_BL_SET gpio_bits_set(GPIOA,GPIO_PINS_2)
#define TFT_BL_RESET gpio_bits_reset(GPIOA,GPIO_PINS_2)
void SPI_IOInit(void)
{
gpio_init_type gpio_initstructure;
spi_init_type spi_init_struct;
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
/* software cs, pa4 as a general io to control flash cs */
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_4|GPIO_PINS_5|GPIO_PINS_3;
gpio_init(GPIOA, &gpio_initstructure);
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pull = GPIO_PULL_DOWN;
gpio_initstructure.gpio_pins = GPIO_PINS_2;
gpio_init(GPIOA, &gpio_initstructure);
CS_OUT0;
TFT_RS_reset;
TFT_RESET_reset;
TFT_BL_RESET;
/* sck */
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_pins = GPIO_PINS_13;
gpio_init(GPIOB, &gpio_initstructure);
/* mosi */
gpio_initstructure.gpio_pull = GPIO_PULL_DOWN;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_pins = GPIO_PINS_15;
gpio_init(GPIOB, &gpio_initstructure);
crm_periph_clock_enable(CRM_SPI2_PERIPH_CLOCK, TRUE);
spi_default_para_init(&spi_init_struct);
spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_8;
spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;
spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;
spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;
spi_init(SPI2, &spi_init_struct);
spi_enable(SPI2, TRUE);
}
void SPI_Send_Data(uint8_t Data)
{
CS_OUT0; //选中该从机
delay_us(1);
spi_i2s_data_transmit(SPI2, Data);
while(spi_i2s_flag_get(SPI2, SPI_I2S_TDBE_FLAG) == RESET);
delay_us(1);
CS_OUT1;
}
注意CS_OUT1;的前面加的delay_us(1);是不可少的,可能上升太快导致通信异常。
4)界面设计 界面的控制通过按键的短按和长按进行来实现的,例如界面菜单的切换通过短按实现,具体进入和退出界面通过长按来实现,这时候就需要对按键状态进行判断处理和执行界面切换和刷新操作; 界面的分配: #define Holle_Interface 1 //开机欢迎界面
#define Menu_Interface 2 //菜单界面
#define LEDTest_Interface 3 //LED测试界面
#define THP_Interface 4 //温湿度大气压力显示界面
#define RTCMenu_Interface 5 //RTC菜单测试界面
#define RTCshow_Interface 6 //RTC实时显示界面
#define UART_Interface 7 //串口测试界面
#define InterfaceOpen 1 //其他参数显示界面
#define InterfaceClose 0 //其他参数阈值设置界面
typedef struct
{
uint8_t ing; //正在显示的界面
uint8_t Request; //要求显示的界面
}show;
extern show Menu,Menu_unit,LEDTest_unit,RTCMenu_unit;
按键状态判断及处理:void App_KEY_Judge(void)
{
if(key_Ok.ShortPress_flag == 1)//短按OK键
{
switch(Menu.ing)
{
case Holle_Interface:
Menu.Request = Menu_Interface;
Menu_unit.Request = 1;
break;
case Menu_Interface:
Menu_unit.Request = PARA_Cyclic(1,5,Menu_unit.Request,1);
break;
case LEDTest_Interface:
LEDTest_unit.Request = PARA_Cyclic(1,3,LEDTest_unit.Request,1);
LED.Mode_Dis = LEDTest_unit.Request;
break;
case RTCMenu_Interface:
RTCMenu_unit.Request = PARA_Cyclic(1,4,RTCMenu_unit.Request,1);
break;
case UART_Interface:
USART2_sendbits("AT32WB415 USART Test \r\n");
break;
default:
break;
}
key_Ok.ShortPress_flag = 0;
}
if(key_Ok.LongPress_flag == 1)//长按OK键
{
switch(Menu.ing)
{
case Holle_Interface:
Menu.Request = Menu_Interface;
Menu_unit.Request = 1;
break;
case Menu_Interface:
if(Menu_unit.Request == 1)
{
Menu.Request = LEDTest_Interface;
LEDTest_unit.Request = 1;
LED.Mode_Dis = LEDTest_unit.Request;
}
else if(Menu_unit.Request == 2)
{
Menu.Request = THP_Interface;
}
else if(Menu_unit.Request == 3)
{
Menu.Request = UART_Interface;
}
else if(Menu_unit.Request == 5)
{
Menu.Request = RTCMenu_Interface;
RTCMenu_unit.Request = 1;
}
Menu_unit.Request = 0;
MenuInterface_unit_cut(Menu_unit.ing,Menu_unit.Request);
Menu_unit.ing = 0;
break;
case LEDTest_Interface:
Menu.Request = Menu_Interface;
Menu_unit.Request = 1;
LEDTest_unit.Request = 0;
LEDTestface_unit_cut(LEDTest_unit.ing,LEDTest_unit.Request);
LEDTest_unit.ing = 0;
LED.Mode_Dis = 0;
break;
case THP_Interface:
Menu.Request = Menu_Interface;
Menu_unit.Request = 2;
break;
case RTCMenu_Interface:
if(RTCMenu_unit.Request == 1)
{
Menu.Request = RTCshow_Interface;
RTCMenu_unit.ing = 0;
}
else if(RTCMenu_unit.Request == 2)
{
// Menu.Request = THP_Interface;
}
else if(RTCMenu_unit.Request == 4)//返回上一级界面
{
Menu.Request = Menu_Interface;;
Menu_unit.Request = 5;
RTCMenu_unit.Request = 0;
RTCMenuface_unit_cut(RTCMenu_unit.ing,RTCMenu_unit.Request);
RTCMenu_unit.ing = 0;
}
break;
case RTCshow_Interface:
Menu.Request = RTCMenu_Interface;
RTCMenu_unit.ing = 0;
RTCMenu_unit.Request = 1;
break;
case UART_Interface:
Menu.Request = Menu_Interface;
Menu_unit.Request = 3;
break;
default:
break;
}
key_Ok.LongPress_flag = 0;
}
}
界面静态切换:if(Menu.ing != Menu.Request) //静态界面切换
{
setup(Menu.Request);
}
void setup(uint8_t FaceNum)
{
switch(Menu.ing)
{
case Holle_Interface:
HolleInterface(InterfaceClose);
break;
case Menu_Interface:
MenuInterface(InterfaceClose);
break;
case LEDTest_Interface:
LEDTestface(InterfaceClose);
break;
case THP_Interface:
THPInterface(InterfaceClose);
break;
case RTCMenu_Interface:
RTCMenuface(InterfaceClose);
break;
case RTCshow_Interface:
RTCShowface(InterfaceClose);
break;
case UART_Interface:
USARTInterface(InterfaceClose);
break;
default:
break;
}
switch(FaceNum)
{
case Holle_Interface:
HolleInterface(InterfaceOpen);
break;
case Menu_Interface:
MenuInterface(InterfaceOpen);
break;
case LEDTest_Interface:
LEDTestface(InterfaceOpen);
break;
case THP_Interface:
THPInterface(InterfaceOpen);
break;
case RTCMenu_Interface:
RTCMenuface(InterfaceOpen);
break;
case RTCshow_Interface:
RTCShowface(InterfaceOpen);
break;
case UART_Interface:
USARTInterface(InterfaceOpen);
break;
default:
break;
}
Menu.ing = FaceNum;
}
5)接下来就是各个分界面功能的实现,其一:LED操作 根据需求分析中的解析,其涉及基本定时器、PWM操作,模式切换可以在App_KEY_Judge(void)中进行。 LED对应IO口初始化: void LED_GPIO_init(void)
{
gpio_init_type gpio_init_struct;
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);/* enable the led clock */
gpio_default_para_init(&gpio_init_struct);/* set default parameter */
/* configure the led gpio */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_7|GPIO_PINS_8|GPIO_PINS_9;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOB, &gpio_init_struct);
}
IO口操作宏定义:/************************************宏定义************************************/
#define LEDRed_on gpio_bits_reset(GPIOB,GPIO_PINS_7);
#define LEDRed_off gpio_bits_set(GPIOB,GPIO_PINS_7);
#define LEDRed_Toggle gpio_bits_TogglePin(GPIOB,GPIO_PINS_7);
#define LEDYellow_on gpio_bits_reset(GPIOB,GPIO_PINS_8);
#define LEDYellow_off gpio_bits_set(GPIOB,GPIO_PINS_8);
#define LEDYellow_Toggle gpio_bits_TogglePin(GPIOB,GPIO_PINS_8);
#define LEDGreen_on gpio_bits_reset(GPIOB,GPIO_PINS_9);
#define LEDGreen_off gpio_bits_set(GPIOB,GPIO_PINS_9);
#define LEDGreen_Toggle gpio_bits_TogglePin(GPIOB,GPIO_PINS_9);
定时器4PWM输出配置:void Tmr4PWM_init(uint16_t Period)
{
/* tmr4 clock enable */
crm_periph_clock_enable(CRM_TMR4_PERIPH_CLOCK, TRUE);
/* gpioa gpiob clock enable */
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
gpio_init_type gpio_init_struct;
tmr_output_config_type tmr_oc_init_structure;
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_pins = GPIO_PINS_7 | GPIO_PINS_8 | GPIO_PINS_9;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(GPIOB, &gpio_init_struct);
/* tmr4 configuration */
/* time base configuration */
/* systemclock/14400/10000 = 1hz */
tmr_base_init(TMR4, 1000000/Period - 1, 144 - 1);
tmr_cnt_dir_set(TMR4, TMR_COUNT_UP);
tmr_clock_source_div_set(TMR4, TMR_CLOCK_DIV1);
tmr_output_default_para_init(&tmr_oc_init_structure);
tmr_oc_init_structure.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
tmr_oc_init_structure.oc_idle_state = TRUE;
tmr_oc_init_structure.oc_polarity = TMR_OUTPUT_ACTIVE_LOW;
tmr_oc_init_structure.oc_output_state = TRUE;
tmr_output_channel_config(TMR4, TMR_SELECT_CHANNEL_2, &tmr_oc_init_structure);
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_2, 0);
tmr_output_channel_buffer_enable(TMR4, TMR_SELECT_CHANNEL_2, TRUE);
tmr_output_channel_config(TMR4, TMR_SELECT_CHANNEL_3, &tmr_oc_init_structure);
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_3, 0);
tmr_output_channel_buffer_enable(TMR4, TMR_SELECT_CHANNEL_3, TRUE);
tmr_output_channel_config(TMR4, TMR_SELECT_CHANNEL_4, &tmr_oc_init_structure);
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_4, 0);
tmr_output_channel_buffer_enable(TMR4, TMR_SELECT_CHANNEL_4, TRUE);
tmr_period_buffer_enable(TMR4, TRUE);
/* tmr enable counter */
tmr_counter_enable(TMR4, TRUE);
}
定义吸灯的频率设置为2s,并定义了LED控制结构体: typedef struct
{
uint8_t Mode_Dis; //LED显示模式
uint8_t Mode_OidDis; //LED上一次显示模式
uint8_t PWMcnt; //LED控制模式
uint8_t state; //LED过程
uint8_t UpFlag; //更新标志
uint8_t Upcnt; //更新计时计数
}LED_states;
if(LED.UpFlag == 0)
{
if(LED.Mode_Dis == 0 || LED.Mode_Dis == 1)
{
LED.Upcnt++;
if(LED.Upcnt%100 == 0)
{
LED.state++;
LED.state %= 3;
LED.Upcnt = 0;
LED.UpFlag = 1;
}
}
else if(LED.Mode_Dis == 2)
{
// PWMcnt = Tmr4PWM/10*2;
LED.Upcnt++;
if(LED.Upcnt%200 == 0)
{
LED.state++;
LED.state %= 3;
LED.Upcnt = 0;
LED.UpFlag = 1;
}
if(LED.Upcnt <= 100)
{
LED.PWMcnt = LED.Upcnt;
}
if(LED.Upcnt <= 200 && LED.Upcnt > 100)
{
LED.PWMcnt = 200 - LED.Upcnt;
}
}
}
LED执行函数:void App_LED(void)
{
if(LED.Mode_OidDis != LED.Mode_Dis)
{
LED.Upcnt = 0;
LED.state = 0;
LED.UpFlag = 1;
if(LED.Mode_OidDis == 2)
{
tmr_counter_enable(TMR4, FALSE);
LED_GPIO_init();
}
else
{
LEDRed_off;
LEDYellow_off;
LEDGreen_off;
}
switch(LED.Mode_Dis)
{
case 0:
LEDRed_on;
LEDYellow_on;
LEDGreen_on;
break;
case 1:
LEDRed_on;
break;
case 2:
Tmr4PWM_init(Tmr4PWM);
LED.Upcnt = 0;
break;
default:
break;
}
LED.Mode_OidDis = LED.Mode_Dis;
}
if(LED.UpFlag == 1)
{
if(LED.Mode_Dis == 0)
{
LEDRed_Toggle;
LEDYellow_Toggle;
LEDGreen_Toggle;
}
else if(LED.Mode_Dis == 1)
{
switch(LED.state)
{
case 0:
LEDRed_on;
LEDGreen_off;
break;
case 1:
LEDRed_Toggle;
LEDYellow_Toggle;
break;
case 2:
LEDYellow_Toggle;
LEDGreen_Toggle;
break;
default:
break;
}
}
else if(LED.Mode_Dis == 2)
{
switch(LED.state)
{
case 0:
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_2, LED.PWMcnt*50);
break;
case 1:
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_3, LED.PWMcnt*50);
break;
case 2:
tmr_channel_value_set(TMR4, TMR_SELECT_CHANNEL_4, LED.PWMcnt*50);
break;
default:
break;
}
}
LED.UpFlag = 0;
}
}
6)AHT20数据获取接AHT界面设计 其实模拟IIC就是两个引脚的IO状态来模拟时序,通过IO口的高低电平及上升下降沿来完美模拟时序,本开发板对应的IIC接口是PB7和PB6,注意PB7同时是红灯的控制引脚,通过IO口模拟正确读取了数据,不过使用硬件IIC是并没有成功,一直得不到从机的回复,考虑到速度问题,就没有继续进行硬件IIC的实现,也希望大佬可以指导一下雅特力的硬件IIC。 模拟IIC配置: #define IIC1_RCU_GPIOB_clock crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE)
#define IIC1_SDA_INITOut IIC1_SDA_GPIO_OutConfig()
#define IIC1_SDA_INITIn IIC1_SDA_GPIO_InConfig()
#define IIC1_SDA_SET gpio_bits_set(GPIOB,GPIO_PINS_7)
#define IIC1_SDA_RESET gpio_bits_reset(GPIOB,GPIO_PINS_7)
#define IIC1_ReadSDA gpio_input_data_bit_read(GPIOB, GPIO_PINS_7)
#define IIC1_SCL_INITOut IIC1_SCL_GPIO_OutConfig()
#define IIC1_SCL_SET gpio_bits_set(GPIOB,GPIO_PINS_6)
#define IIC1_SCL_RESET gpio_bits_reset(GPIOB,GPIO_PINS_6)
void IIC1_IOInit(void)
{
gpio_init_type gpio_init_struct;
IIC1_RCU_GPIOB_clock;
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_6|GPIO_PINS_7;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOB, &gpio_init_struct);
IIC1_SDA_SET;
IIC1_SCL_SET;
}
void IIC1_SDA_GPIO_OutConfig(void)
{
gpio_init_type gpio_init_struct;
IIC1_RCU_GPIOB_clock;
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_7;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOB, &gpio_init_struct);
}
void IIC1_SDA_GPIO_InConfig(void)
{
gpio_init_type gpio_init_struct;
IIC1_RCU_GPIOB_clock;
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_7;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOB, &gpio_init_struct);
}
void IIC1_SCL_GPIO_OutConfig(void)
{
gpio_init_type gpio_init_struct;
IIC1_RCU_GPIOB_clock;
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_6;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOB, &gpio_init_struct);
}
void IIC1_IIC_Init(void)
{
IIC1_SDA_INITOut;
// IIC1_IIC_Stop();
IIC1_SDA_RESET;
IIC1_SCL_RESET;
}
void IIC1_IIC_Start(void)
{
IIC1_SDA_SET;
delay_us(8);
IIC1_SCL_SET;
delay_us(14);
IIC1_SDA_RESET;
delay_us(14);
IIC1_SCL_RESET;
delay_us(14);
}
void IIC1_IIC_Stop(void)
{
IIC1_SDA_RESET;
delay_us(14);
IIC1_SCL_SET;
delay_us(8);
IIC1_SDA_SET;
delay_us(14);
}
void IIC1_SendACK(uint8_t ack)
{
IIC1_SDA_INITOut;
if(ack == 0)
IIC1_SDA_RESET; //写应答信号
else
IIC1_SDA_SET;
delay_us(14);
IIC1_SCL_SET; //拉高时钟线
delay_us(14); //延时
IIC1_SCL_RESET; //拉低时钟线
delay_us(14); //延时
}
uint8_t IIC1_RecvACK(void)
{
uint8_t RecvACK;
IIC1_SDA_INITIn; //SDA接口为输入
delay_us(8);
IIC1_SCL_SET; //拉高时钟线
delay_us(8); //延时
RecvACK = IIC1_ReadSDA; //读应答信号
IIC1_SCL_RESET; //拉低时钟线
delay_us(8); //延时
IIC1_SDA_INITOut;
return RecvACK;
}
uint8_t IIC1_SendByte(uint8_t dat)
{
uint8_t datsendbit;
uint8_t RecvACK;
for (uint8_t i=0; i<8; i++) //8位计数器
{
datsendbit = ((dat & 0x80) >> 7);
dat <<= 1; //移出数据的最高位
if(datsendbit == 1)
{
IIC1_SDA_SET;
}
if(datsendbit == 0)
{
IIC1_SDA_RESET;
} //送数据口
delay_us(8);
IIC1_SCL_SET; //拉高时钟线
delay_us(8); //延时
IIC1_SCL_RESET; //拉低时钟线
delay_us(8);
}
RecvACK = IIC1_RecvACK();
return RecvACK;
}
uint8_t IIC1_RecvByte(void)
{
uint8_t RecvDat = 0;
IIC1_SDA_INITIn;
for (uint8_t i=0; i<8; i++) //8位计数器
{
RecvDat <<= 1;
IIC1_SCL_SET; //拉高时钟线
delay_us(10); //延时
RecvDat |= (IIC1_ReadSDA);
IIC1_SCL_RESET; //拉低时钟线
delay_us(10); //延时
}
IIC1_SDA_INITOut;
return RecvDat;
}
void IIC1_SendBytes(uint8_t len,uint8_t* dat,uint8_t dev_addr)
{
uint8_t ack = 0;
IIC1_IIC_Start();
IIC1_SendByte(dev_addr<<1);
for(uint8_t i = 0; i < len; i++)
{
ack = IIC1_SendByte(*(dat+i));
}
IIC1_IIC_Stop();
}
void IIC1_RecvBytes(uint8_t len,uint8_t* dat,uint8_t dev_addr)
{
IIC1_IIC_Start();
IIC1_SendByte((dev_addr<<1)+1);
for(uint8_t i = 0; i < len; i++)
{
*(dat+i) = IIC1_RecvByte();
if(i == (len - 1))
{
IIC1_SendACK(1);
}
else
{
IIC1_SendACK(0);
}
}
IIC1_IIC_Stop();
}
AHT20的驱动函数在文章末尾,具体的驱动就不赘述了,定时采集传感器数据并刷新显示;7)ERTC的实现 雅特力开发板的RTC又名ERTC,其作用是提供日历管理,因为其计数逻辑作用在电池供电域,因此只要电池供电域有电,ERTC 就不会受到系统复位以及VDD掉电影响。不过开发板是不带电池域单独供电的。ERTC的ck_b用于更新日历,开发板包含外部32.768kHz时钟,首选通过LEXT经过分频器A 和分频器B获得,ck_b=LEXT/(divA+1)/(divB+1),例如32.768K的时钟通过A分频器127、B分频器255,获得1Hz频率。上电复位后所有ERTC 寄存器都处于写保护状态,在进行擦写前一定要先解除写保护,这部分和多数MCU的配置过程基本是相同的,解除写保护——写配置——写保护,部分寄存器需要进入初始化模式才能更改。ERTC的时间寄存器和日期寄存器就是需要经常访问的寄存器,更新日历就是直接更新这两个寄存器,这一点与GD32F303的单片机略有不同,GD实际上是一个64位的寄存器,只存一个自增数,具体的时间设定还需要自己去进行设计,这里可以直接访问时间相关寄存器,更像一个外部的实时时钟模式,直接获取年月日时分秒,不过ERTC的可统计时间范围没有给出,润年机制方面也没有什么介绍,寄存器中的年份只有十位和各位。 ERTC初始化: void ertc_config(void)
{
/* allow access to ertc */
pwc_battery_powered_domain_access(TRUE);
/* reset ertc domain */
crm_battery_powered_domain_reset(TRUE);
crm_battery_powered_domain_reset(FALSE);
/* enable the lext osc */
crm_clock_source_enable(CRM_CLOCK_SOURCE_LEXT, TRUE);
/* wait till lext is ready */
while(crm_flag_get(CRM_LEXT_STABLE_FLAG) == RESET)
{
}
/* select the ertc clock source */
crm_ertc_clock_select(CRM_ERTC_CLOCK_LEXT);
/* enable the ertc clock */
crm_ertc_clock_enable(TRUE);
/* deinitializes the ertc registers */
ertc_reset();
/* wait for ertc apb registers update */
ertc_wait_update();
/* configure the ertc divider */
/* ertc second(1hz) = ertc_clk / (div_a + 1) * (div_b + 1) */
ertc_divider_set(127, 255);
/* configure the ertc hour mode */
ertc_hour_mode_set(ERTC_HOUR_MODE_24);
eRTC_set.year = 22;
eRTC_set.month = 8;
eRTC_set.day = 18;
eRTC_set.hour = 8;
eRTC_set.min = 0;
eRTC_set.sec = 0;
eRTC_set.week = 4;
Set_Time(&eRTC_set);
}
应用函数:void Set_Time(ertc_time_type* time)
{
/* set date */
ertc_date_set(time->year,time->month,time->day,time->week);
/* set time*/
ertc_time_set(time->hour,time->min,time->sec,ERTC_AM);
}
uint32_t bpr_reg_get(uint8_t index)
{
if(index >= ERTC_BPR_DT_NUMBER)
{
index = 0;
}
return ertc_bpr_data_read(bpr_addr_tab[index]);
}
void bpr_reg_write(uint8_t index,uint32_t DT_data)
{
ertc_bpr_data_write(bpr_addr_tab[index],DT_data);
}
这里比较遗憾的是按键的限制导致不太好设计设置时间的部分,不过这部分在串口通信中得到了解决。8)串口通信 串口1对应的引脚与IIC冲突,所以对PC的通信只能使用串口2了,串口3是对蓝牙的通信,固定死的。 串口2配置: void USART2_init(void)
{
gpio_init_type gpio_init_struct;
/* enable the usart2 and gpio clock */
crm_periph_clock_enable(CRM_USART2_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
/* enable iomux clock */
crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_init_struct);
/* configure the usart2 tx pin */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_pins = GPIO_PINS_2;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOA, &gpio_init_struct);
/* configure the usart2 rx pin */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_3;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOA, &gpio_init_struct);
/* config usart nvic interrupt */
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(USART2_IRQn, 0, 0);
/* configure usart2 param */
usart_init(USART2, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
usart_transmitter_enable(USART2, TRUE);
usart_receiver_enable(USART2, TRUE);
/* enable usart2 interrupt */
usart_interrupt_enable(USART2, USART_RDBF_INT, TRUE);
usart_enable(USART2, TRUE);
}
采用的方式是中断接受数据,非中断方式发送数据,发送和接收处理函数如下://******************************************************************************
//* 函数名称 : USART_sendbit(TxBuffer)
//* 函数描述 : 串口发送一个字节
//* 输入参数 :
//* 参数描述 : TxBuffer:存储发送的一个字节
//* 输出参数 : 无
//* 返回值 : 无
//******************************************************************************
void USART2_sendbit(uint8_t aaTx)
{
usart_data_transmit(USART2,aaTx);
while((usart_flag_get(USART2, USART_TDBE_FLAG) == RESET));
}
//******************************************************************************
//* 函数名称 : USART_sendbit(TxBuffer)
//* 函数描述 : 串口发送多个字节
//* 输入参数 :
//* 参数描述 : TxBuffer:存储发送的一个字节
//* 输出参数 : 无
//* 返回值 : 无
//******************************************************************************
void USART2_sendbits(uint8_t *HzpAscii)
{
while(*HzpAscii != 0)
{
usart_data_transmit(USART2,*HzpAscii);
while((usart_flag_get(USART2, USART_TDBE_FLAG) == RESET));
HzpAscii += 1;
}
}
//******************************************************************************
// 函数名称 : USART2_deal
// 函数描述 : 串口中断处理
// 输入参数 :
// 参数描述 : 无
// 输出参数 : 无
// 返回值 : 无
//******************************************************************************
void USART2_deal(uint8_t USART_data)
{
uint8_t check = 0;
if(USART_data == 0xA5 && Com_Data.ing_step == 0)
{
Com_Data.STX_1 = 0xA5;
Com_Data.ing_step = 1;
}
else if(USART_data == 0x5A && Com_Data.ing_step == 1)
{
Com_Data.STX_2 = 0x5A;
Com_Data.ing_step = 2;
}
else if(Com_Data.ing_step == 2)
{
Com_Data.Length = USART_data;
Com_Data.ing_step = 3;
}
else if(Com_Data.ing_step == 3)
{
RecePackBuf[Com_Data.ReturnCnt] = USART_data;
Com_Data.ReturnCnt ++;
if(Com_Data.Length == Com_Data.ReturnCnt)
{
Com_Data.CHECKSUM_1 = RecePackBuf[Com_Data.ReturnCnt-1];
for(uint8_t i=0;i<Com_Data.Length-1;i++)
{
check += RecePackBuf[i];
}
if(Com_Data.CHECKSUM_1 = check)//校验成功
{
line_** = 2;
Com_Data.CMD = RecePackBuf[0];
}
else
{
memset(RecePackBuf, 0,ARRAYNUM(RecePackBuf));//清空接收包
memset(&Com_Data,0,sizeof(Com_Data));//清空结构体
}
}
}
else
{
memset(RecePackBuf, 0,ARRAYNUM(RecePackBuf));//清空接收包
memset(&Com_Data,0,sizeof(Com_Data));//清空结构体
}
}
对于接收具体处理,这边采用的是通信协议的方式进行甄别,实现方式千千万,只取适合自己的那一瓢,这时候就可以把设置时间放到了这里,同时也可以控制LED的显示,只要正确解析就可以。9)蓝牙通信 其实所谓蓝牙通信实现的功能就是串口通信的无线化,其透传模式功能实现的就是串口通信的延伸,命令模式就是对蓝牙模块的配置。本开发板通过串口3和蓝牙模块的耦合实现芯片自带的蓝牙功能。最开始测试想把开发板作为主机进行扫描的,不过通过对资料的分析,这个工程有点大,本次测试提供的资料里只有简单的AT指令,所以改变目标,实现基本透传功能。 串口3使用的是空闲中断+DMA的方式进行的,这样可以减少串口中断的次数,蓝牙切换为透传模式也是非常方便的,发送"AT+TPMODE1\r\n"就可以。 串口三初始化: void USART3_init(void)
{
gpio_init_type gpio_init_struct;
crm_periph_clock_enable(CRM_USART3_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
gpio_pin_remap_config(USART3_GMUX_0010, TRUE);
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_pins = GPIO_PINS_7;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOA, &gpio_init_struct);
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_6;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOA, &gpio_init_struct);
usart_init(USART3, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
usart_transmitter_enable(USART3, TRUE);
usart_receiver_enable(USART3, TRUE);
usart_dma_receiver_enable(USART3, TRUE);
USART3_DMA1_config();
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(USART3_IRQn, 0, 0);
/* Enable the USARTx Interrupt */
usart_interrupt_enable(USART3, USART_IDLE_INT, TRUE);
// usart_interrupt_enable(USART3, USART_RDBF_INT, TRUE);
usart_enable(USART3, TRUE);
}
DMA初始化: void USART3_DMA1_config(void)
{
dma_init_type dma_init_struct;
/* enable dma1 clock */
crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
/* dma1 channel3 for usart1 tx configuration */
dma_reset(DMA1_CHANNEL3);
dma_default_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;//外设到内存
dma_init_struct.memory_base_addr = (uint32_t)USART3RecePackBuf;//内存接收基地址
dma_init_struct.memory_inc_enable = TRUE;//内存地址递增
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;//8位数据
dma_init_struct.buffer_size = ARRAYNUM(USART3RecePackBuf);
dma_init_struct.peripheral_base_addr = (uint32_t)&USART3->dt;//外设地址
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;//外设数据长度
dma_init_struct.peripheral_inc_enable = FALSE;//外设地址不增加
dma_init_struct.priority = DMA_PRIORITY_MEDIUM;//最高DMA通道
dma_init_struct.loop_mode_enable = TRUE;
dma_init(DMA1_CHANNEL3, &dma_init_struct);
/* config flexible dma for usart3 Rx */
dma_flexible_config(DMA1, FLEX_CHANNEL3, DMA_FLEXIBLE_UART3_RX);
dma_channel_enable(DMA1_CHANNEL3, TRUE); /* usart1 tx begin dma transmitting */
}
这里我们将DMA初始化单独拎出来,是为了实现串口处理函数中对DMA重新进行配置。 串口处理函数: void USART3_DMA_deal(void)
{
uint8_t ReturnCnt;
uint8_t check = 0;
dma_channel_enable(DMA1_CHANNEL3, FALSE); /* 关闭DMA传输 */
ReturnCnt = ARRAYNUM(USART3RecePackBuf)-dma_data_number_get(DMA1_CHANNEL3);
memcpy(BLEPackBuf,USART3RecePackBuf,ReturnCnt);
memset(USART3RecePackBuf, 0,ReturnCnt);//清空接收包
// usart_data_transmit(USART0, RecePackBuf[0]);
// while((usart_flag_get(USART0, USART_FLAG_TBE) == RESET));
if(BLEPackBuf[0] == 0xA5 && BLEPackBuf[1] == 0x5A)
{
Com_Data.Length = BLEPackBuf[2];
Com_Data.CHECKSUM_1 = BLEPackBuf[ReturnCnt-1];
for(uint8_t i=0;i<Com_Data.Length-1;i++)
{
check += BLEPackBuf[i+3];
}
if(Com_Data.CHECKSUM_1 = check)//校验成功
{
line_** = 3;
memcpy(RecePackBuf,BLEPackBuf+3,ReturnCnt-4);
Com_Data.CMD = RecePackBuf[0];
memset(BLEPackBuf, 0,Com_Data.Length+3);//清空接收包
}
else
{
memset(BLEPackBuf, 0,ARRAYNUM(BLEPackBuf));//清空接收包
memset(&Com_Data,0,sizeof(Com_Data));//清空结构体
}
}
USART3_DMA1_config();
}
这里一共用到了三个数组,实现对接收数据的隔离及缓存,我们在进入空闲中断是需要关闭DMA通道,避免这个是后继续接收数据,重新调用 USART3_DMA1_config();而不是简单的开启DMA通道的目的是使下一组数据依然从0开始,而不是接着上一个数据存储。通过memcpy(RecePackBuf,BLEPackBuf+3,ReturnCnt-4);使数据与串口解析数据进行融合,共用同一个解析程序。10)总结 AT32WB415是一个非常优秀全面的芯片,是一个面向低功耗消费电子的一款产品,基本可以满足一个产品从控制到显示及通信等多方面要求。不过也有一些不足,例如内部RTC的润年识别机制应该是没有,蓝牙方面控制资料略有不足,建议添加全面的蓝牙设计指南,当然我们对于蓝牙的深层理解还是不够的,还需要继续努力,全面的开发设计资料也是我们所需要的,希望可以补足全面蓝牙设计资料。 再次感谢论坛和雅特力的这次活动,希望有机会能实际使用该产品进行设计,也希望国产芯片能大踏步发展!
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
打赏榜单
ArterySW 打赏了 20.00 元 2022-09-14 理由:评测很详细,支持一下。
共1人点赞
|