本帖最后由 仗剑天涯1412 于 2024-12-5 21:16 编辑
首先感谢21ic平台和雅特力的支持,提供了这次测评的机会,让我进一步了解到国产芯片的崛起。 今天测评的这款开发板是AT-START-L021,以AT32L021C8T7芯片为中心,外设配置LED灯,按钮,和ArduinoTM Uno R3扩展接口。此开发板自带嵌入式调试/烧录工具AT-Link-EZ,不需接入额外开发工具即可对芯片调试使用。具有ARM Cortex®-M0+内核的32位微控制器AT32L021的高性能及低功耗特性。话不多说快进到开箱,一整个惊艳:
再来一个高清效果图(图片来源于21ic电子网):
1.生态和工具
雅特力官网提供了丰富的手册资料和开发工具,助力每一个梦想,传送门:雅特力科技 : 32位微控制器的创新领导者!
今天测评的AT-START-L021开发板所有工具资料都可以通过链接雅特力科技 : 32位微控制器的创新领导者!获取下载,划重点:开发板都开源啦!!!
最强大的还得是图形化开发工具AT32 Work Bench,AT32 MCU图形化配置软件,生成初始化C代码。这款软件是免安装的,点击即可使用。下面就一起开启AT32 Work Bench之门: 打开AT32 Work Bench软件首先选择型号,今天的主角--AT32L021C8T7
完成工程创建后就进入到开发界面了,可以自由的定义你想要的功能,完成引脚和外设的初始化。
最让我眼前一亮的还是I2S,小小芯片竟然还支持I2S,在同级别的芯片中很少有支持I2S的芯片。使用起来也相当的容易,因为需要注意待的地方AT32 Work Bench已经全注意到了,直接点点点就完成了初始化。
配置完外设后最重要的灵魂就是时钟,此时如果有一个时钟配置工具就简直不要太开心了,你能想到的,雅特力早就帮你想到了,不仅有专门的时钟配置工具,还集成到了AT32 Work Bench内,一口气完成配置。
duang duang duang一顿输出就完成了初始化配置,还支持代码预览,不用打开层层文件才能看到效果
做痛苦的事莫过于拿到工程文件后,没有固件包,或者固件包版本不匹配,当然这个问题也解决了,可以自由选择导出方式
至此,整个初始化,以及生态和工具都接触到了,生态完善,资料丰富,工具强大,对于开发者来说有很大的支持和帮助。
2.硬件资源:
满满的一页总有一个适合你。雅特力的sLib安全库,硬件CRC校验都很不错,做安全可靠的产品必不可少。
在来看看开发板的整体布局和功能模块:
AT-Link-EZ是开发板自带嵌入式调试/烧录工具,不需接入额外开发工具即可对芯片调试使用(AT-Link-EZ为AT-Link简易版,不支持离线模式)。相当于把调试烧录工具集成到了开发板上,上手更快,无需一堆先缠在一起,无数个条线占用电脑接口。只需要有一根type-C数据线就可以完成烧录和调试。
主控MCU外围电路,实现最小系统。
电源外围电路,提供了LDO电压转换和LED和按键等外设。
排针排母将IO引出方便拓展使用,足够的灵活方便。
雅特力家族有很多款mcu型号,每一款都有属于自己的名字,今天的主角AT32L021C8T7就是其中一员:低功耗,M0+内核,48pin,64K闪存存储器(Flash),8+1KB随机存取存储器(SRAM),LQFP封装,-40℃~105℃。
可以根据封装和Flash大小选择合适的型号:
3.上电运行
按照下面的顺序配置AT-START-L021板,开始启动应用:
1. 检查板上跳线位置: JP1选择GND或OFF(BOOT0为0,BOOT0在AT32L021C8T7内自带下拉电阻);
2. 用一根USB线(Type-A转Type-C)连接AT-Link-EZ到PC,通过USB连接器CN6给板子供电。LED1(红)恒亮,3个LED灯(LED2到LED4)开始轮留闪烁。
3. 按用户键(B2)后,3个LED灯闪烁频率改变。
肉眼更明显直观。再做进一步开发前,需要先了解开发板的跳线帽,0Ω电阻作用,便于更好的去测试性能。
- 电源和电源选择 AT-START-L021的5 V电源可通过USB线接上AT-Link-EZ上的USB连接器(CN6)来提供,或者通过一个外部的5 V电源(E5V)提供所需的5 V电源。这时5 V电源通过板上3.3 V电压调节器(U2),或3.3 V通过板上1.8 V电压调节器(U3)提供微控制器及外设所需的3.3 V或1.8 V电源,3.3 V或1.8 V由JP2作选择。 J4或J7的引脚E5V也可用作输入电源,AT-START-L021板子必须由一个5 V供电单元供电。 J4的引脚VDD_MCU或J1和J2引脚VDD也可用作输入电源直接对AT32L021C8T7及其外设供电。 注意: 除非5 V通过AT-Link-EZ上的USB接口(CN6)提供,否则通过其他供电方式AT-Link-EZ并不会被供电而无法使用。 当另一个应用板连接到J4,引脚E5V和VDD_MCU可用作输出电源;J7引脚E5V可用作5 V输出电源;J1和J2引脚VDD可用作3.3 V或1.8 V输出电源。
- 源和电源选择 AT-START-L021的5 V电源可通过USB线接上AT-Link-EZ上的USB连接器(CN6)来提供,或者通过一个外部的5 V电源(E5V)提供所需的5 V电源。这时5 V电源通过板上3.3 V电压调节器(U2),或3.3 V通过板上1.8 V电压调节器(U3)提供微控制器及外设所需的3.3 V或1.8 V电源,3.3 V或1.8 V由JP2作选择。 J4或J7的引脚E5V也可用作输入电源,AT-START-L021板子必须由一个5 V供电单元供电。 J4的引脚VDD_MCU或J1和J2引脚VDD也可用作输入电源直接对AT32L021C8T7及其外设供电。 注意: 除非5 V通过AT-Link-EZ上的USB接口(CN6)提供,否则通过其他供电方式AT-Link-EZ并不会被供电而无法使用。 当另一个应用板连接到J4,引脚E5V和VDD_MCU可用作输出电源;J7引脚E5V可用作5 V输出电源;J1和J2引脚VDD可用作3.3 V或1.8 V输出电源。
- 编程和调试:嵌入的AT-Link-EZ 开发板上已集合雅特力AT-Link-EZ编程和调试工具,使用者即可对AT-START-L021板上的AT32L021C8T7进行编程和调试。AT-Link-EZ支持SWD接口模式,并支持一组虚拟串口(VCP)与AT32L021C8T7的USART1_TX/USART1_RX(PA9/PA10)对接。透过电平转换芯片(U4),ATLink-EZ的3.3 V信号电平和微控制器的3.3 V或1.8 V的信号电平可以互相转换对接。 关于AT-Link-EZ的操作、固件升级、和注意事项等详细信息,请参考AT-Link连接器用户手册。 若不使用开发板上的AT-Link-EZ,可将RP2和RP3 OFF使其信号与与AT-START-L021断开。此时AT-START-L021仍可通过CN2接口(出厂未上件)与其它Link对接,也可实现对AT32L021C8T7的编程和调试。
- 启动模式选择 在启动时,通过对启动引脚配置可以选择三种启动模式中的一种。
- 外部时钟源
有三种硬件方式设置外部高速时钟来源:
- 板上晶振(出厂默认设置) 板上提供一8 MHz晶振作为HEXT时钟源使用。硬件设置必须为:R26和R27 ON,R28和R29 OFF。
- 来自PF0外灌 外部振荡从J2第5脚灌入。硬件设置必须为:R28和R29 ON,R26和R27 OFF。
- HEXT不使用 PF0和PF1作为GPIO使用。硬件设置必须为:R28和R29 ON,R26和R27 OFF。
有三种硬件方式设置外部低速时钟来源:
- 板上晶振(出厂默认设置) 板上提供一32.768 kHz晶振作为LEXT时钟源使用。硬件设置必须为:R22和R23 ON,R24和R25 OFF。
- 来自PC14外灌 外部振荡从J2第3脚灌入。硬件设置必须为:R24和R25 ON,R22和R23 OFF。
- LEXT不使用 PC14和PC15作为GPIO使用。硬件设置必须为:R24和R25 ON,R22和R23 OFF
0Ω电阻
4.数据备份
养成一个好习惯,在进行新的开发任务前,对原有的数据进行备份。因为没有出厂程序源码(功能很简单,仅作演示示范),所以可以先读取原有程序进行备份。将type-c数据线连接电脑和开发板,电脑的设备管理器中会出现一个名为“USB串行设备”的串口名称,这个就是开发板上的AT-Link-EZ所生成的VCP虚拟串口。
打开Artery_ICP_Programmer工具,点击连接,连接成功后会显示当前连接的芯片型号,AT-Link-EZ固件号等信息,选择合适的读取大小读取存储器数据,当出现一连串的0xFF湿就已经读取完所有字节了。可以导出.bin文件保存。
5.功耗测试5.1 PWC简介
电源控制的功能主要包含以下内容 - 供电方案,包括VDD、VDDA的供电
- 电源域,由VDD/VDDA域,1.2V域组成
- 上电低电压复位,由上电复位和低电压复位组成
- 电压监测器,监测供电电压与设定临界值关系
- 电压调节器,电压调节器的几个工作状态
- 省电模式,包括睡眠模式、深度睡眠模式、待机模式
省电模式下的电流消耗会被明显降低, Datasheet都有经过详细测试后的数据记录。如下表示例记录: (1) 典型值是在TA = 25 °C下测试得到; (3) 睡眠模式下的电流消耗与运行模式间差异不是特别大,本表未做罗列,具体请参考Datasheet; (4) 不同型号产品对应的特性参数存在区别,本表摘自AT32L021xx,其他型号请以实际Datasheet为准。
省电模式下的唤醒均需要等待及稳定时间, Datasheet都有经过详细测试后的数据记录。如下表记录: (1) 不同型号产品对应的特性参数存在区别,本表摘自AT32L021xx,其他型号请以实际Datasheet为准。
MCU的工作不可避免的会产生一定的功耗,对于应用实际而言,降低功耗的考量十分重要。结合MCU特性及应用条件,以下罗列部分典型降低功耗的方法。 - CPU运行状态下,适当降低系统时钟;
- CPU运行状态下,关闭AHB和APB总线上未被使用的外设时钟;
- CPU无需运行时,MCU进入省电模式(睡眠模式、深度睡眠模式、待机模式)。
在睡眠模式下,CPU时钟关闭,其他时钟保持正常工作,电压调节器正常工作,所有的I/O管脚都保持它们在运行模式时的状态,LDO以正常功耗模式提供1.2V电源(CPU内核、内存和内嵌外设)。 Cortex®-M0+内核设计控制位SLEEPONEXIT,其功能如下: 结合SLEEPONEXIT位的设定,MCU支持两种睡眠机制: - SLEEPONEXIT = 0,执行睡眠指令,此时可立即进入睡眠模式;
- SLEEPONEXIT = 1,执行睡眠指令,此时每当系统从最低优先级的中断处理程序中退出时,会立即进入睡眠模式。
睡眠模式进入及退出 WFI 进入条件:SLEEPDEEP = 0,再执行WFI命令行; 唤醒条件:任意外设中断(该外设的中断使能位及NVIC使能位均被使能)的响应;
WFE 进入条件:SLEEPDEEP = 0,再执行WFE命令行; 唤醒条件: - 任意外设中断(该外设的中断使能位及NVIC使能位均被使能)的响应;
- 任意EXINT线(该EXINT线必须配置为事件模式)上产生的唤醒事件;
- SEVONPEND = 1,任意外设中断(该外设的NVIC使能位未使能)的产生。在进入睡眠之前要确保外设中断挂起位和NVIC通道挂起位均未处于置位状态。且此方式唤醒后,软件需清除外设中断挂起位和NVIC通道挂起位。
其中, SLEEPDEEP、SEVONPEND均为Cortex®-M0+内设计核控制位。其功能介绍如下(详细的说明可参考Cortex®-M0+手册): 睡眠模式的进入由独立的软件接口实现,其软件实例如下: <div style="text-align: left;"> /* enter sleep mode */</div><div style="text-align: left;">pwc_sleep_mode_enter(PWC_SLEEP_ENTER_WFI);</div>
1) WFE进入的睡眠模式唤醒所需的时间最短,因为没有时间损失在中断的进入或退出上; 2) SLEEPONEXIT规则可结合WFI或WFE使用,但应用设计时需注意其与唤醒条件的配合; 3) 应用设计时不开PWC接口时钟条件下,执行睡眠模式进入函数同样会实现CPU暂停并等待中断或事件的效果,只是其功耗不会被明显降低。
案例 USART1接收中断唤醒PWC睡眠模式 本例将演示PWC睡眠模式的使用。其中,唤醒源使用USART1的接收中断。 资源准备 1) 硬件环境 对应产品型号的AT-START BOARD 2) 软件环境 project\at_start_l021\examples\pwc\sleep_usart1
1) 配置流程 - 开启PWC时钟
- 初始化USART1并使能接收数据缓冲器满中断
- 使能USART1中断的NVIC中断
- 使能USART1
- 循环执行进睡眠模式命令并等待唤醒
2) 代码介绍 void usart1_config(uint32_t baudrate)
{
gpio_init_type gpio_init_struct;
/* enable the uart1 and gpio clock */
crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_init_struct);
/* configure the uart1 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_pull = GPIO_PULL_NONE;
gpio_init_struct.gpio_pins = GPIO_PINS_9;
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_MUX;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init_struct.gpio_pins = GPIO_PINS_10;
void USART1_IRQHandler(void)
{
if(usart_flag_get(USART1, USART_RDBF_FLAG) != RESET)
{
/* clear rdbf flag */
usart1_index = usart_data_receive(USART1);
/* toggle led */
at32_led_toggle(LED4);
}
}
int main(void)
{
__IO uint32_t index = 0;
__IO uint32_t systick_index = 0;
/* congfig the system clock */
system_clock_config();
/* init at start board */
at32_board_init();
/* turn on the led light */
at32_led_on(LED2);
at32_led_on(LED3);
at32_led_on(LED4);
/* enable pwc clock */
crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
/* config usart1 */
usart1_config(115200);
printf("exit sleep mode by usart1 rdbf interrupt \r\n");
while(1)
{
at32_led_off(LED2);
at32_led_off(LED3);
printf("now enter sleep mode \r\n");
/* save systick register configuration */
systick_index = SysTick->CTRL;
systick_index &= ~((uint32_t)0xFFFFFFFE);
/* disable systick */
SysTick->CTRL &= (uint32_t)0xFFFFFFFE;
/* enter sleep mode */
pwc_sleep_mode_enter(PWC_SLEEP_ENTER_WFI);
/* restore systick register configuration */
SysTick->CTRL |= systick_index;
/* wake up from sleep mode */
printf("now exit sleep mode by usart1 rdbf interrupt \r\n");
at32_led_on(LED2);
delay_ms(300);
}
}
实验效果 可通过AT-START BOARD上的LED翻转查看实现效果。 LED2亮:MCU处于运行模式; LED2灭:MCU处于睡眠模式; LED4状态翻转: USART1接收数据缓冲器满终端发生并唤醒了睡眠模式。 也可通过USART1的串口打印查看实验效果,如下: 下面左侧图片是本例程MCU处于运行模式的电流大小,运行模式为7.2mA。右侧图片是MCU处于睡眠模式的电流大小,睡眠模式为4.3mA。(测试地点为成都,环境室温,VDD=1.8V)
5.3.2 深度睡眠模式 在深度睡眠模式下,所有1.2V时钟关闭,HICK和HEXT振荡器都被关闭,电压调节器以正常工作或低功耗工作状态给1.2V域供电,所有I/O管脚都保持它们在运行模式时的状态,SRAM和寄存器内容保持。 深度睡眠模式可与LDO的正常模式、低功耗模式配合使用以进一步节省功耗。 深度睡眠模式进入及退出 WFI 进入条件:SLEEPDEEP = 1,LPSEL = 0,再执行WFI命令行; 唤醒条件:任意EXINT线(该EXINT线需配置为中断模式且NVIC使能位被使能)上的中断响应。
WFE 进入条件:SLEEPDEEP = 1,LPSEL = 0,再执行WFE命令行; 唤醒条件:任意EXINT线(该EXINT线需配置为事件模式)上产生的唤醒事件。 其中, SLEEPDEEP为Cortex®-M0+内设计核控制位。 系统从深度睡眠模式退出时,HICK RC振荡器被自动开启并在稳定后被选为系统时钟。
/* congfig the voltage regulator mode */
pwc_voltage_regulate_set(PWC_REGULATOR_LOW_POWER);
/* enter deep sleep mode */
pwc_deep_sleep_mode_enter(PWC_DEEP_SLEEP_ENTER_WFI);
注意: 1) 退出深度睡眠模式后,HICK RC振荡器被选为系统时钟,软件需根据需求对系统时钟重新设定; 2) 退出深度睡眠模式时,LDO会保持正常模式,因此若进深睡眠前配置为了低功耗模式的话,LDO的模式切换需要一定耗时,从而会增加额外的唤醒时间。
案例 USART接收数据唤醒PWC深度睡眠模式 本例将演示PWC深度睡眠模式的使用。其中,唤醒源使用USART1接收数据缓冲器满中断。 资源准备 1) 硬件环境: 对应产品型号的AT-START BOARD 2) 软件环境 project\at_start_l021\examples\pwc\deepsleep_usart1
软件设计 1) 配置流程 - 开启PWC时钟
- 执行USART1基础配置并使能接收数据缓冲器满中断
- 开启deepsleep模式下的USART使能位
- 选择低功耗唤醒方式为RDBF
- 使能USART的低功耗唤醒中断
- 配置EXINT线25为中断模式并使能
- 等待USART接收器空闲
- 设定电压调节器输出电压为1.0V
- 执行进深度睡眠模式命令并等待唤醒
- 唤醒后设定电压调节器输出电压为1.2V
- 重新进行系统时钟的恢复设定
- 从“等待USART接收器空闲”开始循环,实现深度睡眠模式的循环唤醒
2) 代码介绍 void usart1_config(uint32_t baudrate)
{
gpio_init_type gpio_init_struct;
/* 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);
crm_usart_clock_select(CRM_USART1, CRM_USART_CLOCK_SOURCE_LEXT);
/* enable the uart1 and gpio clock */
crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_init_struct);
/* configure the uart1 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_pull = GPIO_PULL_UP;//GPIO_PULL_NONE;
gpio_init_struct.gpio_pins = GPIO_PINS_9;
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_MUX;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init_struct.gpio_pins = GPIO_PINS_10;
gpio_init(GPIOA, &gpio_init_struct);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE9, GPIO_MUX_1);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE10, GPIO_MUX_1);
/* configure uart param */
nvic_irq_enable(USART1_IRQn, 0, 0);
usart_init(USART1, baudrate, USART_DATA_8BITS, USART_STOP_1_BIT);
usart_parity_selection_config(USART1, USART_PARITY_NONE);
usart_transmitter_enable(USART1, TRUE);
usart_receiver_enable(USART1, TRUE);
usart_hardware_flow_control_set(USART1,USART_HARDWARE_FLOW_NONE);
usart_interrupt_enable(USART1, USART_RDBF_INT, TRUE);
usart_enable(USART1, TRUE);
/* polling usart initialisation */
while((!(usart_flag_get(USART1, USART_TXON_FLAG))) || (!(usart_flag_get(USART1,
USART_RXON_FLAG))))
{
}
}
void system_clock_recover(void)
{
/* enable external high-speed crystal oscillator - hext */
crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE);
/* wait till hext is ready */
while(crm_hext_stable_wait() == ERROR);
/* enable pll */
crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);
/* wait till pll is ready */
while(crm_flag_get(CRM_PLL_STABLE_FLAG) == RESET);
/* select pll as system clock source */
crm_sysclk_switch(CRM_SCLK_PLL);
/* wait till pll is used as system clock source */
while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL);
}
void USART1_IRQHandler (void)
{
if(usart_flag_get(USART1, USART_RDBF_FLAG) != RESET)
{
/* clear rdbf flag */
usart1_index = usart_data_receive(USART1);
/* toggle led */
at32_led_toggle(LED4);
}
if(usart_flag_get(USART1, USART_LPWUF_FLAG) != RESET)
{
usart_flag_clear(USART1, USART_LPWUF_FLAG);
}
if(exint_flag_get(EXINT_LINE_25) != RESET)
{
exint_flag_clear(EXINT_LINE_25);
}
}
int main(void)
{
__IO uint32_t index = 0;
__IO uint32_t systick_index = 0;
/* enable pwc clock */
crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
/* congfig the voltage regulator mode.only used with deep sleep mode */
pwc_voltage_regulate_set(PWC_REGULATOR_LOW_POWER);
/* congfig the system clock */
system_clock_config();
/* init at start board */
at32_board_init();
/* turn on the led light */
at32_led_on(LED2);
at32_led_on(LED3);
at32_led_on(LED4);
/* config usart1 */
usart1_config(2400);
usart1_wakeup_config();
printf("exit deepsleep mode by usart1 rdbf interrupt \r\n");
while(1)
{
at32_led_off(LED2);
printf("now enter deepsleep mode \r\n");
/* make sure that no usart receiver is ongoing */
while(usart_flag_get(USART1, USART_OCCUPY_FLAG) == SET)
{
}
/* select system clock source as hick before ldo set */
crm_sysclk_switch(CRM_SCLK_HICK);
/* wait till hick is used as system clock source */
while(crm_sysclk_switch_status_get() != CRM_SCLK_HICK)
{
}
/* reduce ldo before enter deepsleep mode */
pwc_ldo_output_voltage_set(PWC_LDO_OUTPUT_1V0);
while(usart_flag_get(USART1, USART_TDC_FLAG) == RESET)
{
}
/* enter deep sleep mode */
pwc_deep_sleep_mode_enter(PWC_DEEP_SLEEP_ENTER_WFI);
/* turn on the led light */
at32_led_on(LED2);
/* resume ldo before system clock source enhance */
pwc_ldo_output_voltage_set(PWC_LDO_OUTPUT_1V2);
/* wake up from deep sleep mode, congfig the system clock */
system_clock_recover();
/* wake up from sleep mode */
printf("\r\nnow exit deepsleep mode by usart1 rdbf interrupt \r\n");
printf("usart1_rdne_data = 0x%x\r\n", usart1_index);
delay_ms(300);
}
}
实验效果 可通过AT-START BOARD上的LED翻转查看实现效果。 LED2亮:MCU处于运行模式; LED2灭:MCU处于深度睡眠模式; LED4状态翻转: USART1接收缓冲器满中断发生并唤醒了深度睡眠模式。 也可通过USART1的串口打印查看实验效果,如下:
下面左侧图片是本例程MCU处于运行模式的电流大小,运行模式为6.6mA。右侧图片是MCU处于深度睡眠模式的电流大小,睡眠模式为1.6mA。(测试地点为成都,环境室温,VDD=1.8V 注:LDO未处于低功耗模式,HICK和HEXT未关闭,WDT已关闭)。所以测试结果会比手册中的典型值高。
5.3.3待机模式 待机模式可最大限度的降低系统功耗,在该模式下,电压调节器关闭,只有电池供电的寄存器和待机电路维持供电,其他的1.2V供电区域,PLL、HICK和HEXT振荡器都被断电。寄存器和SRAM中的内容也会丢失。 在待机模式下,除了复位管脚、被设置为防侵入或校准输出时的TAMPER管脚和被使能的唤醒管脚之外,所有的I/O管脚处于高阻态。 待机模式进入及退出 进入条件:SLEEPDEEP = 1,LPSEL = 1,再执行WFI/WFE命令行; 退出条件: - WKUP管脚的上升沿;发生唤醒时会置位SEF、SWEF标志
- NRST管脚上外部复位;发生复位时会置位SEF、NRSTF标志
- WDT复位;发生复位时会置位SEF、WDTRSTF、NRSTF标志
- 实时时钟事件的上升沿;发生唤醒时会置位SEF、SWEF、及实时时钟事件对应标志
实时时钟事件为ERTC闹钟事件、ERTC入侵事件、ERTC时间戳、ERTC周期性自动唤醒事件。 实时时钟在部分型号为RTC,部分型号为ERTC,部分ERTC型号不支持周期性自动唤醒,部分型号支持双闹钟。且部分型号具备多个WKUP管脚等,这些差异部分请以实际芯片手册为准。 /* enter standby mode */
pwc_standby_mode_enter();
/* enable wakeup pin */
pwc_wakeup_pin_enable(PWC_WAKEUP_PIN_1, TRUE);
注意: 1) SWEF标志为待机唤醒事件标志,其处于置位状态下执行进入待机模式命令,会立即产生复位。故在进入待机模式前,软件需确保SWEF标志已被清除; 2) 部分型号具备多个WKUP管脚,具体请以实际芯片手册为准 3) 实时时钟在部分型号为RTC,部分型号为ERTC,具体请以实际芯片手册为准; 4) 部分ERTC型号不支持周期性自动唤醒,部分型号支持双闹钟,具体请以实际芯片手册为准。
案例 PWC待机模式 本例将演示PWC待机模式的使用。其中,唤醒源使用WKUPx管脚上升沿。 资源准备 1) 硬件环境: 对应产品型号的AT-START BOARD USER_KEY1——PA0 2) 软件环境 project\at_start_l021\examples\ pwc\standby_wakeup_pin
软件设计 1) 配置流程 - 开启PWC时钟
- 检测进入待机模式标志并清除
- 检测待机唤醒事件标志并清除
- 使能WKUP1引脚
- 执行进待机模式命令并等待唤醒
2) 代码介绍 int main(void)
{
__IO uint32_t index = 0;
/* congfig the system clock */
system_clock_config();
/* init at start board */
at32_board_init();
/* turn on the led light */
at32_led_off(LED2);
at32_led_off(LED3);
at32_led_off(LED4);
/* enable pwc clock */
crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
if(pwc_flag_get(PWC_STANDBY_FLAG) != RESET)
{
/* wakeup from standby */
pwc_flag_clear(PWC_STANDBY_FLAG);
at32_led_on(LED2);
}
if(pwc_flag_get(PWC_WAKEUP_FLAG) != RESET)
{
/* wakeup event occurs */
pwc_flag_clear(PWC_WAKEUP_FLAG);
at32_led_on(LED3);
}
at32_led_on(LED4);
/*delay to check led status*/
delay_ms(1000);
delay_ms(1000);
/* enable wakeup pin1 */
pwc_wakeup_pin_enable(PWC_WAKEUP_PIN_1, TRUE);
/* enter standby mode */
pwc_standby_mode_enter();
while(1)
{
}
}
实验效果 可通过AT-START BOARD上的USER_KEY来唤醒,LED翻转查看实现效果。 LED4亮:MCU处于运行模式; LED4灭:MCU处于待机模式; MCU运行状态下的LED2: 亮表示进入待机模式标志置位,反之未置位; MCU运行状态下的LED3: 亮表示待机唤醒事件标志置位,反之未置位。
下面左侧图片是本例程MCU处于运行模式的电流大小,运行模式为7.0mA。右侧图片是MCU处于待机模式的电流大小,睡眠模式为0.000mA。(测试地点为成都,环境室温,VDD=1.8V),因万用表精度影响所以没有显示出有效数值,但是可以判断出待机模式电流小于1uA,与手册中相符。
6.驱动移植
单片机可供移植的协议和驱动非常多,每种协议下又有很多的模块和设备可以移植。这里以最常用的I2C协议为例,使用0.96寸OLED进行驱动移植。0.96寸OLED可以使用I2C进行通信。因为I2C总线可以挂在多设备,通过设备地址识别从机,所以不占用硬件资源,相对于串口调试来说节省硬件资源,特别是在硬件资源没有富余的情况下更有优势,同时也不干扰总线上其他设备正常工作。 使用软件模拟I2C的方式可以不受引脚影响,在非高速情况下,一个好的软件模拟驱动协议比硬件协议更稳定易用,哪怕换了单片机也能很快移植,同时更易排查问题点。这里使用软件模拟I2C的方式移植0.96寸OLED驱动协议,虽然例子很简单,没有复杂的显示。但是驱动部分移植成功了,上层的使用都是不变的,这里就不对复杂的显示展开,仅作驱动移植介绍: 这个是江协科技的0.96寸OLED驱动部分: oled.c文件 #include "stm32f10x.h"
#include "OLED_Font.h"
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_6, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_7, (BitAction)(x))
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] I2C开始
* @param 无
* @retval 无
*/
void OLED_I2C_Start(void)
{
OLED_W_SDA(1);
OLED_W_SCL(1);
OLED_W_SDA(0);
OLED_W_SCL(0);
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] I2C停止
* @param 无
* @retval 无
*/
void OLED_I2C_Stop(void)
{
OLED_W_SDA(0);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_SDA(Byte & (0x80 >> i));
OLED_W_SCL(1);
OLED_W_SCL(0);
}
OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
OLED_W_SCL(0);
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x00); //写命令
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x40); //写数据
OLED_I2C_SendByte(Data);
OLED_I2C_Stop();
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //上电延时
{
for (j = 0; j < 1000; j++);
}
OLED_I2C_Init(); //端口初始化
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
oled.h文件 #ifndef __OLED_H
#define __OLED_H
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
#endif
oled_font.h文件 #ifndef __OLED_FONT_H
#define __OLED_FONT_H
/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 0
0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2
0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,
0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3
0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,
0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4
0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,
0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5
0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,
0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6
0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 7
0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,
0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 8
0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,
0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 9
0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,
0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 10
0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,
0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 11
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 12
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 13
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 14
0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,
0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 15
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 16
0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 17
0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,
0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 18
0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,
0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 19
0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,
0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 20
0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,
0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 21
0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,
0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 22
0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,
0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 23
0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,
0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 24
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 25
0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 26
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 27
0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,
0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 28
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 29
0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 30
0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,
0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 31
0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,
0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//[url=home.php?mod=space&uid=72445]@[/url] 32
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,
0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 33
0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,
0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 34
0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,
0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 35
0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,
0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 36
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 37
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 38
0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,
0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 39
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 40
0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 41
0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,
0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 42
0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,
0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 43
0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,
0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 44
0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,
0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 45
0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,
0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 46
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 47
0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,
0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 48
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 49
0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,
0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 50
0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,
0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 51
0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,
0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 52
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 53
0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,
0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 54
0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,
0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 55
0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,
0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 56
0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,
0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 57
0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,
0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 58
0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,
0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 59
0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 60
0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,
0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 61
0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 62
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 63
0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 64
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 65
0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,
0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 66
0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,
0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 67
0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,
0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 68
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 69
0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 70
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 71
0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,
0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 72
0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 73
0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,
0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 74
0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,
0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 75
0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 76
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 77
0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,
0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 78
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 79
0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,
0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 80
0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,
0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 81
0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 82
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 83
0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,
0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 84
0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,
0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 85
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 86
0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,
0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 87
0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 88
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 89
0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 90
0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,
0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 91
0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 92
0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,
0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 93
0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};
#endif
做移植时这里只用对oled.c文件的引脚号和读写函数进行修改。如果使用同样的引脚模拟I2C的话就只用修改读写函数。一般不同的芯片厂商的库函数不同,当人也可以自己封装一层调用。这里修改后的oled.c文件如下 移植后的oled.c文件 //#include "stm32f10x.h"
#include "at32l021_board.h"
#include "OLED_Font.h"
#include "at32l021_clock.h"
/*引脚配置*/
/*引脚初始化*/
void OLED_I2C_Init(void)
{
(GPIOB->scr = GPIO_PINS_8);
(GPIOB->scr = GPIO_PINS_9);
}
/**
* @brief I2C开始
* @param 无
* @retval 无
*/
void OLED_I2C_Start(void)
{
(GPIOB->scr = GPIO_PINS_9);
(GPIOB->scr = GPIO_PINS_8);
(GPIOB->scr= GPIO_PINS_9);
(GPIOB->clr= GPIO_PINS_9);
}
/**
* @brief I2C停止
* @param 无
* @retval 无
*/
void OLED_I2C_Stop(void)
{
(GPIOB->scr= GPIO_PINS_9);
(GPIOB->scr = GPIO_PINS_8);
(GPIOB->scr = GPIO_PINS_9);
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
//OLED_W_SDA(Byte & (0x80 >> i));
if(Byte & (0x80 >> i)) GPIOB->scr = GPIO_PINS_9;
else GPIOB->clr = GPIO_PINS_9;
(GPIOB->scr = GPIO_PINS_8);
(GPIOB->clr= GPIO_PINS_9);
}
(GPIOB->scr = GPIO_PINS_8); //额外的一个时钟,不处理应答信号
(GPIOB->clr= GPIO_PINS_9);
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x00); //写命令
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x40); //写数据
OLED_I2C_SendByte(Data);
OLED_I2C_Stop();
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //上电延时
{
for (j = 0; j < 1000; j++);
}
OLED_I2C_Init(); //端口初始化
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
下图是移植后的OLED显示结果,只用在main.c中调用驱动即可。 main.c文件 /**
**************************************************************************
* [url=home.php?mod=space&uid=288409]@file[/url] main.c
* @brief main program
**************************************************************************
* Copyright notice & Disclaimer
*
* The software Board Support Package (BSP) that is made available to
* download from Artery official website is the copyrighted work of Artery.
* Artery authorizes customers to use, copy, and distribute the BSP
* software and its related documentation for the purpose of design and
* development in conjunction with Artery microcontrollers. Use of the
* software is governed by this copyright notice and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
* GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
* TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
* STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
* INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
*
**************************************************************************
*/
#include "at32l021_board.h"
#include "at32l021_clock.h"
#include "i2c_application.h"
#include "OLED.h"
/** @addtogroup AT32L021_periph_examples
* @{
*/
void gpio_config(void)
{
gpio_init_type gpio_init_struct;
/* enable the gpioa clock */
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
/* set default parameter */
gpio_default_para_init(&gpio_init_struct);
/* configure the gpio */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_8;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOB, &gpio_init_struct);
/* configure the gpio */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = GPIO_PINS_9;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOB, &gpio_init_struct);
/* configure the 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_11;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(GPIOB, &gpio_init_struct);
}
i2c_handle_type hi2cx;
/**
* @brief error handler program
* @param i2c_status
* @retval none
*/
void error_handler(uint32_t error_code)
{
while(1)
{
}
}
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
/* initial system clock */
system_clock_config();
/* at board initial */
at32_board_init();
gpio_config();
while(1)
{
OLED_Init();
OLED_ShowChar(1, 1, 'A');
OLED_ShowString(1, 3, "HelloWorld!");
OLED_ShowNum(2, 1, 12345, 5);
OLED_ShowSignedNum(2, 7, -66, 2);
OLED_ShowHexNum(3, 1, 0xAA55, 4);
OLED_ShowBinNum(4, 1, 0xAA55, 16);
GPIOB->scr = GPIO_PINS_11;
delay_ms(500);
GPIOB->clr = GPIO_PINS_11;
}
}
/**
* @}
*/
/**
* @}
*/
其余的驱动和协议都是类似的,只要有一个好的驱动,不管换了什么单片机都可以轻松移植。当然发凡事都有例外,有些特殊的问题比如多个外设不能同时使用,某些引脚只能小电流等。比如雅特力的SRAM拓展功能,这款AT32L021C8T7的SRAM是高达9KB的(在同级别芯片中算高的了),9KB的SRAM采用8+1模式,1KB是需要拓展的,虽然两块SRAM地址是连续的,但是需要通过ICP软件或者程序中进行设置实现。具体可以参考官方文档。除了官方文档非常齐全外,个人最满意的就是雅特力的技术支持群,有问必有答,很晚都在回答问题。对小微企业和个人开发者特别友好。所以在手册中那个没找到答案,可以加群:1121757020友好提问。
7.项目移植
因为本次测评开发板的另一个目的是为产品国产化选型做性能测试,所以在开发板上进行了项目的移植,并做了相关的测试。因为属于公司项目所以这里不做展开。在开发板上移植取得了成功,各参数均符合要求,特别是功耗方面。后续将做一致性测试,同时打板后再次测试性能。
8.感想与总结 因为我司一直使用的雅特力的MCU,给我最深的印象就是:M4的核只卖M3的价。这次体验了M0+的低功耗的性能,真真正正的感觉当初没有选错,多款MCU性能都很不错,这样在软件上的时间就会省下很多,硬件上就会少踩很多坑,因为换新品牌的MCU会遇到此前不了解的问题,花费的时间相对较多。特别期待雅特力近期会发售的电机MCU,也感谢雅特力提供的机会体验这次AT32L021低功耗开发板。
|