STM32VLDISCOVERY+mbed NXP LPC1768+RTOS+KEIL



Начало.

Среда разработки: MDK ARM 4.14 + RL-ARM 4.12

Голый проект:

STM32
#include "stm32f10x.h"

int main (void)
{
   return 0;
}


В папку с проектом кладем:
C:\\Keil\\ARM\\INC\\ST\\STM32F10x\\stm32f10x.h (в файле разкоменчиваем одну из строк с 50 по 57)
C:\\Keil\\ARM\\Startup\\ST\\STM32F10x.s

Это пустышка достаточная для начала создания ЛЮБОЙ программы, все что нужно еще, это reference manual.

LPC1768
#include "LPC17xx.h"

int main (void)
{
   return 0;
}


В папку с проектом кладем:
C:\\Keil\\ARM\\Startup\\NXP\\LPC17xx\\startup_LPC17xx.s
C:\\Keil\\ARM\INC\NXP\\LPC17xx\\LPC17xx.h

Опять же пустышка для любой фантазии.

Если не хотим ручками и reference manual-ом устанавливать источник тактирования и частоту работы проца, то добавляем немного файлов в свой проект и одну строчку кода.

LPC1768
#include "LPC17xx.h"

int main (void)
{
   SystemInit ();
   return 0;
}


В папку с проектом добавляем:
C:\\Keil\\ARM\\Startup\\NXP\\LPC17xx\\system_LPC17xx.c
После этого мышкой ставим галочки и меняем делители и умножители для получения желаемой частоты в файле system_LPC17xx.c.

STM32
#include "stm32f10x.h"

int main (void)
{
   SystemInit ();
   return 0;
}


В папку с проектом добавляем:
C:\\Keil\\ARM\\Startup\\ST\\STM32F10x\\system_stm32f10x.c

И все. Частота работы STM будет выбрана в соответствии с раскоментированной строкой в файле stm32f10x.h.
Для переключения источников тактирования и изменения частоты по ходу выполнения программы, можно использовать отдельные функции из stm32f10x.h (SetSysClockToHSE(void), SetSysClockTo24(void) и т.д.) или выдрать куски кода и пользовать их как хочется.

У STM есть классный файлик STM32_Init.c.
Кроме частоты и источника тактирования с помощью него не читая reference manual, можно проинициализировать всю периферию камня.

Добавляем две строчки, или добавляем при желании использовать предыдущий конфиг (system_stm32f10x.c).

STM32
#include "stm32f10x.h"
#include "stm32_init.h"

int main (void)
{
   SystemInit ();
   stm32_Init();
   return 0;
}


В папку с проектом добавляем:
STM32_Init.c
STM32_Init.h
STM32_Reg.h

Откуда они беруться изначально я не знаю, я брал их в папках с примерами к кейловским платам: C:\\Keil\\ARM\\Boards\\Keil\\

После добавления файлов в проект в STM32_Init.c можно мышкой понаставить галочек и запустить нужную периферию.

Еще о тактировании на STM32.

Чуток назад. Выдрано из system_stm32f10x.c и оформлено в виде отдельных функций.

STM32 Clock
#include "stm32f10x.h"

//#define CLOCK_HSI //Внутренний RC 8 Мгц
//#define CLOCK_HSE //Внешний кварц 8 Мгц
#define CLOCK_24MHz //Внешний кварц 8 Мгц и PLL

void ResetClock (void)
{

RCC->CR |= (uint32_t)0x00000001; //HSION = 1
RCC->CFGR &= (uint32_t)0xF8FF0000; //Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits
RCC->CR &= (uint32_t)0xFEF6FFFF; //Reset HSEON, CSSON and PLLON bits
RCC->CR &= (uint32_t)0xFFFBFFFF; //Reset HSEBYP bit
RCC->CFGR &= (uint32_t)0xFF80FFFF; //Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits
RCC->CIR = 0x009F0000; //Disable all interrupts and clear pending bits
RCC->CFGR2 = 0x00000000; //Reset CFGR2 register

}

void SetClockHSI (void)
{

ResetClock();
RCC->CR |= ((uint32_t)RCC_CR_HSION); //Enable HSI
while (!(RCC->CR & RCC_CR_HSIRDY)) {};
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSI;
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x00) {};

}

void SetClockHSE (void)
{

ResetClock();
RCC->CR |= ((uint32_t)RCC_CR_HSEON); //Enable HSE
while (!(RCC->CR & RCC_CR_HSERDY)) {}; //Wait till HSE is ready
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; //SYSCLK not divided
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; //HCLK not divided
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1; //HCLK not divided
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE; //HSE selected as system clock
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04) {}; //Wait till HSE is used as system clock source

}

void SetClockPLL (void)
{

ResetClock();
RCC->CR |= ((uint32_t)RCC_CR_HSEON); //Enable HSE
while (!(RCC->CR & RCC_CR_HSERDY)) {}; //Wait till HSE is ready
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; //SYSCLK not divided
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; //HCLK not divided
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); //PLL configuration: = (HSE / 2) * 6 = 24 MHz
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1_Div2 | RCC_CFGR_PLLMULL6);
RCC->CR |= RCC_CR_PLLON; //Enable PLL
while((RCC->CR & RCC_CR_PLLRDY) == 0) {}; //Wait till PLL is ready
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; //PLL selected as system clock
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) {}; //Wait till PLL is used as system clock source

}

void InitClock (void)
{

RCC->CR |= (uint32_t)0x00000001; //HSION = 1
RCC->CFGR &= (uint32_t)0xF8FF0000; //Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits
RCC->CR &= (uint32_t)0xFEF6FFFF; //Reset HSEON, CSSON and PLLON bits
RCC->CR &= (uint32_t)0xFFFBFFFF; //Reset HSEBYP bit
RCC->CFGR &= (uint32_t)0xFF80FFFF; //Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits
RCC->CIR = 0x009F0000; //Disable all interrupts and clear pending bits
RCC->CFGR2 = 0x00000000; //Reset CFGR2 register

#ifdef CLOCK_HSE

RCC->CR |= ((uint32_t)RCC_CR_HSEON); //Enable HSE
while (!(RCC->CR & RCC_CR_HSERDY)) {}; //Wait till HSE is ready
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; //SYSCLK not divided
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; //HCLK not divided
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1; //HCLK not divided
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE; //HSE selected as system clock
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04) {}; //Wait till HSE is used as system clock source

#elif defined CLOCK_24MHz

RCC->CR |= ((uint32_t)RCC_CR_HSEON); //Enable HSE
while (!(RCC->CR & RCC_CR_HSERDY)) {}; //Wait till HSE is ready
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; //SYSCLK not divided
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; //HCLK not divided
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); //PLL configuration: = (HSE / 2) * 6 = 24 MHz
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1_Div2 | RCC_CFGR_PLLMULL6);
RCC->CR |= RCC_CR_PLLON; //Enable PLL
while((RCC->CR & RCC_CR_PLLRDY) == 0) {}; //Wait till PLL is ready
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; //PLL selected as system clock
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) {}; //Wait till PLL is used as system clock source

#endif

}

int main (void)
{
InitClock ();
return 0;
}


В процессе выполнения кода можно вызывать функции SetClockHSI (void), SetClockHSE (void), SetClockPLL (void) и менять производительность/прожорливость камня. А еще, вроде, можно затактировать от RTC-овского кристала, если он есть и от встроенного LSI на 40 Кгц.

Чтобы из обычной вышеприведенной пустышки сделать RTOS-пустышку, нужно добавить чуток файлов и несколько строчек кода.
Код будет приводится один, изменения для обоих камней будет идентичное.

LPC1768
#include "LPC17xx.h"
#include "RTL.h"

__task void init (void)
{
   os_tsk_prio_self (100);
   os_tsk_delete_self ();
}

int main (void)
{
   SystemInit ();
   os_sys_init(init);
   return 0;
}


В папку с проектом добавляем:
C:\\Keil\\ARM\\Startup\\RTX_Conf_CM.c

В меню кейла: Project->Options for target… в закладке Target меняем Operating system на RTX Kernel.
И все. На камне работает типа ОСь. Осталось на создавать задач и наслаждаться.

Помыргать светодиодиками в RTOS-е.

У моей LPC 4 светодиода (P1.18, P1.20, P1.21, P1.23), у STM-а 2 светодиода, (PC.8, PC.9).

Порядок такой, прописываем функции инициализации светодиодов, функции включить, выключить, создаем задачи под каждый светодиод, запускаем задачи, в главной процедуре инитим светодиоды перед запуском RTOS.

LPC1768
#include "LPC17xx.h"
#include "RTL.h"

const unsigned long led_mask[] = { 1UL<<23, 1UL<<21, 1UL<<20, 1UL<< 18 };

OS_TID t_LED1;
OS_TID t_LED2;
OS_TID t_LED3;
OS_TID t_LED4;

void InitLEDs (void)
{
   LPC_GPIO1->FIODIR = (1<<23) | (1<<21) | (1<<20) | (1<<18);
}

void LEDon (unsigned int num)
{
   LPC_GPIO1->FIOPIN |= led_mask[num];
}

void LEDoff (unsigned int num)
{
   LPC_GPIO1->FIOPIN &= ~led_mask[num];
}

__task void f_LED1 (void)
{
   while (1)
   {
      LEDon (0);
      os_dly_wait (400);
      LEDoff (0);
      os_dly_wait (400);
   }
}

__task void f_LED2 (void)
{
   while (1)
   {
      LEDon (1);
      os_dly_wait (500);
      LEDoff (1);
      os_dly_wait (500);
   }
}

__task void f_LED3 (void)
{
   while (1)
   {
      LEDon (2);
      os_dly_wait (600);
      LEDoff (2);
      os_dly_wait (600);
   }
}

__task void f_LED4 (void)
{
   while (1)
   {
      LEDon (3);
      os_dly_wait (700);
      LEDoff (3);
      os_dly_wait (700);
   }
}

__task void init (void)
{
   os_tsk_prio_self (100);

   t_LED1 = os_tsk_create (f_LED1, 20);
   t_LED2 = os_tsk_create (f_LED2, 20);
   t_LED3 = os_tsk_create (f_LED3, 20);
   t_LED4 = os_tsk_create (f_LED4, 20);

   os_tsk_delete_self ();
}

int main (void)
{
   SystemInit ();
   InitLEDs ();
   os_sys_init(init);
   return 0;
}


Все, 4 задачи, 4 светодиода будут мигать каждый со своей частотой.

STM32
#include "stm32f10x.h"
#include "STM32_Init.h"
#include "rtl.h"

#define Set_PortC_Pin_8_output ((GPIOC->CRH |= 0x2)|(GPIOC->CRH &= ~0xC))
#define Set_PortC_Pin_9_output ((GPIOC->CRH |= 0x20)|(GPIOC->CRH &= ~0xC0))

const unsigned long led_mask[] = { 1UL<<8, 1UL<<9 };

OS_TID t_LED1;
OS_TID t_LED2;

void DevsInit(void)
{
   RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

   Set_PortC_Pin_8_output;
   Set_PortC_Pin_9_output;

}

void LEDOn (uint32_t num)
{
   GPIOC->BSRR = led_mask[num];
}

void LEDOff (uint32_t num)
{
   GPIOC->BRR = led_mask[num];
}

__task void f_LED1 (void)
{
   while (1)
   {
      LEDOn (0);
      os_dly_wait (200);
      LEDOff (0);
      os_dly_wait (200);
   }
}

__task void f_LED2 (void)
{
   while (1)
   {
      LEDOn (1);
      os_dly_wait (400);
      LEDOff (1);
      os_dly_wait (400);
   }
}

__task void init (void)
{
   os_tsk_prio_self (100);

   t_LED1 = os_tsk_create (f_LED1, 20);
   t_LED2 = os_tsk_create (f_LED2, 20);

   os_tsk_delete_self ();
}

int main (void)
{
   SystemInit ();
   stm32_Init();
   DevsInit();
   os_sys_init(init);
   return 0;
}


Все, 2 задачи, 2 светодиода будут мигать каждый со своей частотой.

HTTP server на LPC1768.

Назначаем персональный стек для HTTP Server-a, создаем задачу с минимальным приоритетом, с пользовательским стеком, задержку в задаче не задаем. Перед запуском ОСи не забываем инитить. Добавляем несколько файлов к проекту.

C:\\Keil\\ARM\\RV31\\LIB\\TCP_CM3.lib
C:\\Keil\\ARM\\RV31\\LIB\\TCPD_CM3.lib
C:\\Keil\\ARM\\RL\\TCPnet\\Drivers\\EMAC_LPC17xx.c
C:\\Keil\\ARM\RL\\TCPnet\\Drivers\EMAC_LPC17xx.h
C:\\Keil\\ARM\\RL\\TCPnet\\SRC\\Net_Config.c
C:\\Keil\\ARM\\RL\\TCPnet\\SRC\\Net_Debug.c
C:\\Keil\\ARM\\RL\\TCPnet\\SRC\\Net_Config.h

LPC1768
#include "LPC17xx.h"
#include "RTL.h"

U64 tcp_stack[800/8];

const unsigned long led_mask[] = { 1UL<<23, 1UL<<21, 1UL<<20, 1UL<< 18 };

OS_TID t_LED1;
OS_TID t_LED2;
OS_TID t_LED3;
OS_TID t_LED4;
OS_TID t_TIMER;
OS_TID t_HTTP;

void InitLEDs (void)
{
   LPC_GPIO1->FIODIR = (1<<23) | (1<<21) | (1<<20) | (1<<18);
}

void LEDon (unsigned int num)
{
   LPC_GPIO1->FIOPIN |= led_mask[num];
}

void LEDoff (unsigned int num)
{
   LPC_GPIO1->FIOPIN &= ~led_mask[num];
}

__task void f_LED1 (void)
{
   while (1)
   {
      LEDon (0);
      os_dly_wait (400);
      LEDoff (0);
      os_dly_wait (400);
   }
}

__task void f_LED2 (void)
{
   while (1)
   {
      LEDon (1);
      os_dly_wait (500);
      LEDoff (1);
      os_dly_wait (500);
   }
}

__task void f_LED3 (void)
{
   while (1)
   {
      LEDon (2);
      os_dly_wait (600);
      LEDoff (2);
      os_dly_wait (600);
   }
}

__task void f_LED4 (void)
{
   while (1)
   {
      LEDon (3);
      os_dly_wait (700);
      LEDoff (3);
      os_dly_wait (700);
   }
}

__task void f_TIMER (void) 
{
   os_itv_set (10);
   while (1) 
   {
      os_itv_wait ();
      timer_tick ();
   }
}


__task void f_HTTP (void)
{
   while (1)
   {
      main_TcpNet();
      os_tsk_pass();
   }
}

__task void init (void) 
{ 
   os_tsk_prio_self (100);

   t_LED1 = os_tsk_create (f_LED1, 20);
   t_LED2 = os_tsk_create (f_LED2, 20);
   t_LED3 = os_tsk_create (f_LED3, 20);
   t_LED4 = os_tsk_create (f_LED4, 20);

   t_TIMER = os_tsk_create (f_TIMER, 50);
 
   t_HTTP = os_tsk_create_user (f_HTTP, 1, &tcp_stack, sizeof(tcp_stack));

   os_tsk_delete_self ();
}

int main (void)
{
   SystemInit ();
   InitLEDs ();
   init_TcpNet ();
   os_sys_init(init);
   return 0;
}


После всего этого, контроллер будет пинговаться по адресу, заданному в Net_Config.c.
Там же включаются дополнительные сетевые сервисы, например HTTP Server. Если его включить и зайти после заливки IE по адресу контроллера, получим страницу с ошибкой. Но сервак уже работает, осталось добавить свою страницу.

PS. У меня при пинге и любой попытке достучаться по сети до контроллера RTOS висла, в RTX_Conf_CM.c поставил галочку в Run in privileged mode и все заработало.

Продолжение будет…
  • 0
  • 01 апреля 2011, 09:48
  • lexanet

Комментарии (22)

RSS свернуть / развернуть
Напишите первый вводный абзац перед фотографией. Типа о чем вообще статья.
После этого абзаца поставьте тэг «cut». Чтобы все остальное вместе с фоткой убралось с общей обзорной ленты. Так будет удобней смотреть.
0
Хотя в принципе можно и так оставить. Не обратил внимания что статья уже «обкатана»…
0
Хорошая статья, но я попытался сделать и…
В общем uVision4(я так понимаю в нём делать) выдает вот такое.
Build target 'Target 1'
compiling STM32_Init.c...
compiling system_stm32f10x.c...
stm32f10x.h(205): error:  #67: expected a "}"
stm32f10x.h(455): warning:  #12-D: parsing restarts here after previous syntax error
stm32f10x.h(1079): error:  #101: "CFGR2" has already been declared in the current scope
system_stm32f10x.c(254): error:  #101: "prediv1factor" has already been declared in the current scope
system_stm32f10x.c(250): warning:  #177-D: variable "prediv1factor" was declared but never referenced
compiling stm32f10x.c...
stm32f10x.h(205): error:  #67: expected a "}"
stm32f10x.h(455): warning:  #12-D: parsing restarts here after previous syntax error
stm32f10x.h(1079): error:  #101: "CFGR2" has already been declared in the current scope
stm32f10x.c(71): warning:  #1-D: last line of file ends without a newline
Target not created

Файлы в проекте все.
0
  • avatar
  • qic
  • 23 апреля 2011, 03:00
Пишет что не хватает фигурной скобки, или лишняя где. И такое ощущение, что в stm32f10x.h. Давайте для начала весь код. Можно на мыло, тогда весь проект. alexeyk(DOG)rambler.ru.
0
Дело в том что всё собрано в точности со статьёй, в главном файле последний код засунут. Раскомментированны строки 50-57. Всё. Т.е. каким образом у Вас это получилось?
ЗЫ Может лучше прикрепить к посту архивчик с проектом?
0
Последний код это какой? 2 задачи, 2 светодиода где мигают?
0
Последний код это какой? 2 задачи, 2 светодиода где мигают? Раскомментированы, надеюсь, не все строчки с 50 по 57, а только одна из них? А только оответствующая Вашему камню. У меня камень stm32f100, т.е. STM32 Medium density Value Line devices, потому, раскомментирована только строчка 53.
+1
Вот оно что, недоглядел. Спасибо!
Последний код что тут приведен — мигание двумя светодиодами.
Теперь ошибка другая
Build target 'Target 1'
compiling STM32_Init.c...
compiling system_stm32f10x.c...
compiling stm32f10x.c...
stm32f10x.c(71): warning:  #1-D: last line of file ends without a newline
linking...
STM32Board.axf: Error: L6320W: Ignoring --entry command. Cannot find argument 'Reset_Handler'.
STM32Board.axf: Warning: L6320W: Ignoring --first command. Cannot find argument '__Vectors'.
Target not created
0
Файл STM32F10x.s в проект вставили?
0
Отправил проект на почту.
0
Поправил и вернул.
+1
Большое спасибо!
0
Только вот одна проблемка, оба светодиода просто горят.
Дебаггер висит на строке 256 файла STM32F10x.s
B       .
                ENDP
                ALIGN
0
В файле STM32_Init.c из под кейла снимите галочку напротив RTC Interrupts или со всей группы RTC.
0
(Еще и не сразу заметил где визард)
Наконец заработало! =) Еще раз спасибо!
Всётаки это сложнее AVR.
0
Не, намного проще. Раз попробовал, запомнил и все, дальше только наслаждаешься гибкостью и возможностями :)
0
Значит я пока невкурил. Но начальная пачка (хоть и RTOS) внушает. Однако визарды радуют.
Пока еще не понятно что там выбирать для портов (пулл ап, или пуш пул, и частоты разные) но с визардом интереснее.
0
Не, визарды путают, мне кажется, проще по примерам и доке самому. Там один раз вкурить. Зато будет все на виду и под контролем. А то ведь незамеченная галочка может не только не давать проге работать, но и заблокировать проц, придется COM мастерить, чтобы прошивку обнулить.
0
А каким образом можно залить прогу созданную в Keil в эту MBED плату… Искал, искал в инете, понял что через усб токо .bin можно залить и запустить… Можно из Keil бин сделать? или нужен дополнительный программер?
0
а, попробовал просто хекс залить — все работает, вопрос снят
0
А есть возможность задействовать виртуальный ком порт по УСБ шнурку из Keil не используя онлайн компайлер?
0
KEIL-ом все можно. Online компилером вообще пользоваться не могу, тормозной он. Для связи с компом дрова надо поставить с mbed.org и использовть usart0 как хочется.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.