xld0932 发表于 2022-6-26 11:41

【国民技术低功耗系列N32L43x测评】02.创建模版工程

本帖最后由 xld0932 于 2022-6-26 12:14 编辑

<p>本文章将通过KEIL MDK集成开发环境一步步创建基于N32L43XRL-STB开发板的模版工程,通过原理图设计、实现板载资源LED、KEY、USART的基础功能,并通过USART移植我常用的Letter-shell,下面将详述其过程,文末有调试问题记录,供参考。</p>

<p style="">&nbsp;</p>

<p style=""><span style="color:#e74c3c;"><strong>创建工程</strong></span></p>

<p style="">STEP1.打开KEIL MDK集成开发环境软件</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP2.点击菜单栏Project-&gt;New uVision Project...</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP3.在弹出的Create New Project窗口中输入工程文件名,选择工程文件需要保存的路径后,点击保存按键</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP4.在弹出的Select Device fro Target窗口中选择开发板上对应的芯片型号:N32L436RB,然后点击OK</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP5.在弹出的Manage Run-Time Environment窗口中暂时不需要操作,直接点击OK即可</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP6.此时我们一个基于N32L436RB芯片的空的KEIL工程就创建完成了,接下来我们需要来添加工程文件和配置工程</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP7.点击工具栏上的Manage Project Items按键,我们来添加工程文件</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP8.在弹出的Manage Project Items窗口中的Project Items中,我们可以修改Project Targets名称,在Groups中添加文件分组,在Files中添加对应分组中的源代码,操作完成后,我们点击OK</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP9.添加好后的工程分组和文件信息如下图所示</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP10.接下来我们继续点击工具栏上的Options for Target按钮,来对工程进行配置</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP11.在Target中将Code Generation区域的ARM Compiler选择Use default compiler version5、勾选Use MicroLIB</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP12.在Output中勾选Create HEX File,这样在我们编译完工程后就可以自动生成HEX烧录文件了</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP13.在C/C++中,将Preprocessor Symbols中的Define填写N32L43X和USE_STDPERIPH_DRIVER这两个宏,这是作用于芯片底层驱动库程序的、接着勾选C99 Mode,这是个人编译习惯,根据需要来选择、然后在Include Paths栏添加头文件的包含路径</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP14.在Debug中选择调试下载器:CMSIS-DAP Debugger,然后点击Settings按键,这时我们会看到SW Device区域中已经检测到了MCU(需要将开发板通过USB连接到电脑上)</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP15.在Utilities中保持默认选项设置,然后点击Settings按键,在弹出窗口的Flash Download中勾选Reset and Run,这样我们在通过KEIL下载完程序后,会自动复位芯片然后运行程序,在确认了Programming Algorithm中程序下载算法是对应的之后,我们点击OK</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP16.此时我们完整的KEIL工程就创建、配置完成了,接下来我们需要编写代码,然后编译、下载程序、运行调试了</p>

<p style="">&nbsp;</p>

<p style=""><span style="color:#e74c3c;"><strong>编写驱动</strong></span></p>

<p style="">STEP1.开发板板载了3个彩色LED灯,分别由PA8、PB4和PB5这三个GPIO端口引脚来控制,通过原理图,我们可以看出当GPIO端口引脚输出高电平时,对应的LED灯处于点亮的状态,而当GPIO端口引脚输出底电平时,对应的LED灯处于熄灭的状态;驱动程序如下所示:</p>

<p style=""></p>

<pre>
<code class="language-cpp">/*******************************************************************************
* <a href="home.php?mod=space&amp;uid=159083" target="_blank">@brief</a> * @param      
* @retval      
* <a href="home.php?mod=space&amp;uid=1020061" target="_blank">@attention</a> *******************************************************************************/
void LED_Init(void)
{
    GPIO_InitType GPIO_InitStructure;

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);

    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin          = GPIO_PIN_8;
    GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
    GPIO_InitStructure.GPIO_Pull    = GPIO_No_Pull;
    GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(GPIOA, &amp;GPIO_InitStructure);

    GPIO_WriteBit(GPIOA, GPIO_PIN_8, Bit_RESET);

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);

    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin          = GPIO_PIN_4;
    GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
    GPIO_InitStructure.GPIO_Pull    = GPIO_No_Pull;
    GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(GPIOB, &amp;GPIO_InitStructure);

    GPIO_WriteBit(GPIOB, GPIO_PIN_4, Bit_RESET);

    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin          = GPIO_PIN_5;
    GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
    GPIO_InitStructure.GPIO_Pull    = GPIO_No_Pull;
    GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(GPIOB, &amp;GPIO_InitStructure);

    GPIO_WriteBit(GPIOB, GPIO_PIN_5, Bit_RESET);

    TASK_Append(TASK_ID_LED, LED_Toggle, 250);
}


/*******************************************************************************
* <a href="home.php?mod=space&amp;uid=159083" target="_blank">@brief</a>      
* @param      
* @retval      
* <a href="home.php?mod=space&amp;uid=1020061" target="_blank">@attention</a>   
*******************************************************************************/
void LED_Toggle(void)
{
#if 0
    if(!GPIO_ReadOutputDataBit(GPIOA, GPIO_PIN_8))
    {
      GPIO_WriteBit(GPIOA, GPIO_PIN_8, Bit_SET);
    }
    else
    {
      GPIO_WriteBit(GPIOA, GPIO_PIN_8, Bit_RESET);
    }
#else
    static uint8_t Index = 0;

    switch(Index)
    {
      case 0:
            GPIO_WriteBit(GPIOA, GPIO_PIN_8, Bit_SET);
            GPIO_WriteBit(GPIOB, GPIO_PIN_4, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_PIN_5, Bit_RESET);
            break;

      case 1:
            GPIO_WriteBit(GPIOA, GPIO_PIN_8, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_PIN_4, Bit_SET);
            GPIO_WriteBit(GPIOB, GPIO_PIN_5, Bit_RESET);
            break;

      case 2:
            GPIO_WriteBit(GPIOA, GPIO_PIN_8, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_PIN_4, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_PIN_5, Bit_SET);
            break;

      default:
            break;
    }

    Index = (Index + 1) % 3;
#endif
}</code></pre>

<p style="">&nbsp;</p>

<p style="">STEP2.开发板板载了3个用户按键和一个WAKEUP按键,3个用户按键分别由PA4、PA5和PA6这三个GPIO端口引脚来控制,通过原理图,我们可以看出当按键按下后,对应的按键状态是处于与GND连通的状态,处于低电平,所以我们需要将这三个按键的常态(按键不处于被按下的状态)配置为上拉输入,这样我们就可以通过检测按键的电平状态来判断按键是否按下了;而另外一个按键是WAKEUP按键,连接在PA0这个端口引脚上,它可以作为普通按键功能使用,也可以作为MCU在低功耗模式下的唤醒按键;当作为唤醒按键功能时,需要串联一个电阻到地,通过按键连接到MCU_VDD,以上升沿的方式唤醒处于低功耗模式下的MCU;而作为普通按键功能时,我只需要将这个引脚配置成浮空输入即可,因为常态下这个商品引脚已经被一个串联的电阻拉到了GND上,而当按下的时候会被按键短路到MCU_VCC上,由此来判断按键的状态;这边的3个用户按键和WAKEUP按键的识别电平正好是相反的,所以在编程的时候需要注意一下哈;驱动程序如下所示:</p>

<p style=""></p>

<pre>
<code class="language-cpp">/*******************************************************************************
* <a href="home.php?mod=space&amp;uid=159083" target="_blank">@brief</a>      
* @param      
* @retval      
* <a href="home.php?mod=space&amp;uid=1020061" target="_blank">@attention</a>   
*******************************************************************************/
void KEY_Init(void)
{
    GPIO_InitType GPIO_InitStructure;

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);

    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin       = GPIO_PIN_0;
    GPIO_InitStructure.GPIO_Pull = GPIO_No_Pull;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Input;
    GPIO_InitPeripheral(GPIOA, &amp;GPIO_InitStructure);

    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin       = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6;
    GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Input;
    GPIO_InitPeripheral(GPIOA, &amp;GPIO_InitStructure);

    TASK_Append(TASK_ID_KEY, KEY_Scan, 10);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void KEY_SubScan(uint8_t *State, uint8_t *Count, uint8_t Value, uint8_t Active, char *Name)
{
    if(*State == 0)
    {
      if(Value == Active) *Count += 1;
      else                *Count= 0;

      if(*Count &gt; 5)
      {
            *Count = 0; *State = 1;
            printf(&quot;\r\n%s Pressed&quot;, Name);
      }
    }
    else
    {
      if(Value != Active) *Count += 1;
      else                *Count= 0;

      if(*Count &gt; 5)
      {
            *Count = 0; *State = 0;
            printf(&quot;\r\n%s Release&quot;, Name);
      }
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void KEY_Scan(void)
{
    static uint8_t KeyState = {0, 0, 0, 0};
    static uint8_t KeyCount = {0, 0, 0, 0};

    KEY_SubScan(&amp;KeyState, &amp;KeyCount, GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_0), Bit_SET,   &quot;WAKEUP&quot;);
    KEY_SubScan(&amp;KeyState, &amp;KeyCount, GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_4), Bit_RESET, &quot;KEY1&quot;);
    KEY_SubScan(&amp;KeyState, &amp;KeyCount, GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_5), Bit_RESET, &quot;KEY2&quot;);
    KEY_SubScan(&amp;KeyState, &amp;KeyCount, GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_6), Bit_RESET, &quot;KEY3&quot;);
}</code></pre>

<p style="">&nbsp;</p>

<p style="">STEP3.板载的NS-LINK除了有调试、下载程序的功能,还集成了一个虚拟串口,将它连接到MCU的USART上我们可以打印出一些需要的信息到PC端;通过原理图,我们可以看到通过跳帽的方式可以将MCU的USART1与NS-LINK的TTL串口进行连接;另外基于USART我们还移植了Letter-shell这个开源代码,驱动程序如下所示:</p>

<p style=""></p>

<pre>
<code class="language-cpp">/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void shellPortWrite(const char ch)
{
    USART_SendData(USART1, (uint8_t)ch);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXDE) == RESET);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void shellPortInit(void)
{
    GPIO_InitType   GPIO_InitStructure;
    NVIC_InitType   NVIC_InitStructure;
    USART_InitTypeUSART_InitStructure;

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_USART1, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&amp;NVIC_InitStructure);

    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin            = GPIO_PIN_9;
    GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Alternate   = GPIO_AF4_USART1;
    GPIO_InitPeripheral(GPIOA, &amp;GPIO_InitStructure);

    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin            = GPIO_PIN_10;
    GPIO_InitStructure.GPIO_Pull      = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Alternate   = GPIO_AF4_USART1;
    GPIO_InitPeripheral(GPIOA, &amp;GPIO_InitStructure);

    USART_StructInit(&amp;USART_InitStructure);
    USART_InitStructure.BaudRate            = 115200;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity            = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;
    USART_Init(USART1, &amp;USART_InitStructure);

    USART_ClrFlag(USART1, USART_FLAG_RXDNE);
    USART_ConfigInt(USART1, USART_INT_RXDNE, ENABLE);

    USART_Enable(USART1, ENABLE);

    shell.write = shellPortWrite;
    shellInit(&amp;shell);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void USART1_IRQHandler(void)
{
    if(USART_GetIntStatus(USART1, USART_INT_RXDNE) != RESET)
    {
      shellHandler(&amp;shell, USART_ReceiveData(USART1));
      USART_ClrIntPendingBit(USART1, USART_INT_RXDNE);
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
int fputc(int ch, FILE *f)
{
    USART_SendData(USART1, (uint8_t)ch);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXDE) == RESET);

    return ch;
}</code></pre>

<p style="">&nbsp;</p>

<p style=""><span style="color:#e74c3c;"><strong>编译、下载、调试运行</strong></span></p>

<p style="">STEP1.在我们完成驱动程序编写完成后,点击工具栏的Built或者Rebuild按钮编译工程源代码,确认无误</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP2.点击工具栏的Download按钮或者按下F8快捷按键,将程序烧到到芯片</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style="">STEP3.打开PC端的串口调试软件MobaXterm进行监测,在代码开始运行后,我们看到开发板上的LED灯开始闪烁,串口软件有消息打印输出,同时我们每按下一个按键,在串口终端软件上都会看到对应的打印消息,如下所示:</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style=""><span style="color:#e74c3c;"><strong>调试问题</strong></span></p>

<p style="">在开发板拿到手后,J5接口中的RESET是没有跳帽短接的,就算在KEIL中配置了Reset and Run这个功能,也仅仅是通过软件复位的方式来复位芯片的,并没有实际去操作芯片的RESET过程,所以导致在J5 RESET没有连接的情况下,芯片上电后USART打印不正常,如下图所示;曾一度以为是NS-LINK或者是芯片复位上电时序的问题呢,担独立按下板载的RESET按键,程序又是正常的&hellip;&hellip;所以在用示波器连接到RESET查看波形后,竟然没有复位电平时序,所以才定位到了J5的RESET跳帽上来&hellip;&hellip;在将J5的RESET跳帽连接上后,程序就运行正常啦!!!</p>

<p style="">正常上电打印:</p>

<p style=""></p>

<p style="">异常上电打印:</p>

<p style=""></p>

<p style="">&nbsp;</p>

<p style=""><span style="color:#e74c3c;"><strong>附件:</strong></span></p>

<p style="">软件工程源代码:</p>

<p style="">&nbsp;</p>

<p style=""><span style="color:#e74c3c;"><strong>运行效果:</strong></span></p>

<p style=""><iframe allowfullscreen="true" frameborder="0" height="510" src="https://training.eeworld.com.cn/shareOpenCourseAPI?isauto=true&amp;lessonid=33800" style="background:#eee;margin-bottom:10px;" width="700"></iframe><br />
&nbsp;</p>

<p style="">&nbsp;</p>

lugl4313820 发表于 2022-6-26 12:48

本帖最后由 lugl4313820 于 2022-6-26 16:57 编辑

<p>帖子写得很好,特别是按键的检测,辛苦了,期待更加优秀的作品!</p>

xld0932 发表于 2022-6-26 12:52

lugl4313820 发表于 2022-6-26 12:48
帖子写得很好,特别是按键的检测不,辛苦了,期待更加优秀的作品!

<p><img height="50" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan21.gif" width="63" /></p>

秦天qintian0303 发表于 2022-7-7 21:41

<p>坛友们都是用keil吗?有和我用IAR的吗?</p>
页: [1]
查看完整版本: 【国民技术低功耗系列N32L43x测评】02.创建模版工程