17.12.2013, 19:24
|
|
Заблокирован
Регистрация: 25.04.2013
Сообщений: 1,431
Сказал спасибо: 0
Сказали Спасибо 385 раз(а) в 254 сообщении(ях)
|
STM32 - Tips 'N Tricks
Предлагаю выкладывать здесь полезные мелочи (и не только) при работе с STM32, которые могут запросто потеряться в других ветках или на просторах инета.
|
|
|
Эти 9 пользователя(ей) сказали Спасибо nahimovv за это сообщение:
|
|
|
17.12.2013, 19:35
|
|
Заблокирован
Регистрация: 25.04.2013
Сообщений: 1,431
Сказал спасибо: 0
Сказали Спасибо 385 раз(а) в 254 сообщении(ях)
|
Бага TIM1
Бага TIM1 во многих линейках STM32.
При cоединении в цепочку двух таймеров, когда TIM1 настроен как slave и Gated Mode, происходит самопроизвольный запуск TIM1 при попытке Update generation регистра TIM1-›EGR.
|
|
|
Эти 3 пользователя(ей) сказали Спасибо nahimovv за это сообщение:
|
|
|
17.12.2013, 20:18
|
|
Гуру портала
Регистрация: 20.11.2004
Сообщений: 10,015
Сказал спасибо: 936
Сказали Спасибо 2,269 раз(а) в 1,563 сообщении(ях)
|
Re: STM32 - Tips 'N Tricks
Расчет BRR USART в противовес простыням SPL.
Код:
|
#define USARTCLK 48000000UL
#define BAUDRATE 115200UL |
где USARTCLK частота тактирования USART в Гц.
Тактирование может быть и от APB и от HSI
Код:
|
USARTx-›BRR =(USARTCLK+BAUDRATE/2)/BAUDRATE; |
__________________
Осторожно , злой кот
|
|
|
Сказали "Спасибо" dosikus
|
|
|
17.12.2013, 20:35
|
|
Заблокирован
Регистрация: 25.04.2013
Сообщений: 1,431
Сказал спасибо: 0
Сказали Спасибо 385 раз(а) в 254 сообщении(ях)
|
Злокопипаст
Злокопипаст документации.
В инете наблюдались жалобы на "невозможность" каскадировать некоторые таймера в один 32-ух разрядный, хотя в таблицах UM данное каскадирование описано как допустимое. Это типа TIM16--›TIM15 в STM32F100, TIM10--›TIM9 в STM32F20X и т.д.. Так как все таймера STM32, по сути, одинаковы, то налицо злокопипаст документации. На самом деле там всё просто, в качестве TRGO выступает OCx. Т.е. нужно настроить ведущий таймер в режиме PWM1, разрешить выход, альтернативную функцию пина можно не включать, если это не нужно.
|
|
|
Эти 5 пользователя(ей) сказали Спасибо nahimovv за это сообщение:
|
|
|
17.12.2013, 20:38
|
|
Гуру портала
Регистрация: 20.11.2004
Сообщений: 10,015
Сказал спасибо: 936
Сказали Спасибо 2,269 раз(а) в 1,563 сообщении(ях)
|
Re: STM32 - Tips 'N Tricks
swo.h
Код:
|
#ifndef _SWO_H
#define _SWO_H
void SWO_PrintChar (char c);
void SWO_PrintString(const char *s);
#endif |
swo.c
Код:
|
/************************************************** *********************************
*
*
* Defines for Cortex-M debug unit
*/
#define ITM_STIM_U32 (*(volatile unsigned int*)0xE0000000) // STIM word acces
#define ITM_STIM_U8 (*(volatile char*)0xE0000000) // STIM byte acces
#define ITM_ENA (*(volatile unsigned int*)0xE0000E00) // ITM Enable
#define ITM_TCR (*(volatile unsigned int*)0xE0000E80) // ITM Trace Control Reg.
#define DHCSR (*(volatile unsigned int*)0xE000EDF0) // Debug register
#define DEMCR (*(volatile unsigned int*)0xE000EDFC) // Debug register
/************************************************** **********************************
*
* Function description
* Prints a character to the ITM_STIM register in order to provide data for SWO
*/
void SWO_PrintChar(char c) {
//
// Check if SWO is set up. If it is not, return to avoid that a program
// hangs if no debugger is connected.
//
//
// Check if DEBUGEN in DHCSR is set
//
if ((DHCSR & 1)!= 1) {
return;
}
//
// Check if TRACENA in DEMCR is set
//
if ((DEMCR & (1 ‹‹ 24)) == 0) {
return;
}
//
// Check if ITM_TRC is enabled
//
if ((ITM_TCR & (1 ‹‹ 22)) == 1) {
return;
}
//
// Check if stimulus port 0 is enabled
//
if ((ITM_ENA & 1) == 0) {
return;
}
//
// Wait until STIMx is ready to accept at least 1 word
//
while ((ITM_STIM_U8 & 1) == 0);
ITM_STIM_U8 = c;
}
/************************************************** **********************************
*
* SWO_PrintString
*
* Function description
* Prints a string via SWO
*
*/
void SWO_PrintString(const char *s) {
//
// Print out character per character
//
while (*s) {
SWO_PrintChar(*s++);
}
} |
Вывод строк через swo .
Выдрано из мануала JLink .Проверено в IAR, Keil, Eclipse
Подключаем файлы к проекту . Подключаем PB3 (конфигурация по умолчанию) к SWO Jlink .
В IAR отметить General options , вкладка Library Configuration -› Library low-level interface implementation -› Via SWO
В Keil в настройках дебуггера разрешить trace.
Выхлоп наблюдаем в терминалах дебуггеров.
__________________
Осторожно , злой кот
Последний раз редактировалось dosikus; 17.12.2013 в 20:40.
|
|
|
Эти 6 пользователя(ей) сказали Спасибо dosikus за это сообщение:
|
|
|
18.12.2013, 00:15
|
|
Заблокирован
Регистрация: 25.04.2013
Сообщений: 1,431
Сказал спасибо: 0
Сказали Спасибо 385 раз(а) в 254 сообщении(ях)
|
Обращение к порту
Многие знают, а многие и не догадываются, что к многим ресурсам STM32 можно обратиться как к слову (32бит), полуслову (16бит) и как к байту (8бит).
Например, такое:
Код:
|
#define GPIOB_Low (*(__IO uint8_t *)((uint32_t)&(GPIOB-›ODR)))
#define GPIOB_High (*(__IO uint8_t *)((uint32_t)&(GPIOB-›ODR) + 1)) |
позволяет получить доступ к младшему байту порта, не затрагивая старший, и наоборот. Доступно как для чтения, так и для записи. На стандартных методах обращения к порту это никак не отражается, они абсолютно ликвидны.
Запись
Код:
|
#define LCD_DATA (*(__IO uint8_t *)((uint32_t)&(GPIOB-›ODR))) |
позволяет оперировать шиной LCD 8бит, подключённой к PB0-PB7, без дополнительных телодвижений.
|
|
|
Эти 13 пользователя(ей) сказали Спасибо nahimovv за это сообщение:
|
-jonns- (20.12.2013), -shiva- (28.12.2013), dadigor (18.12.2013), danilych2 (10.08.2016), dosikus (18.12.2013), Easyrider83 (18.12.2013), kot-69 (01.01.2014), MaxiMuz79 (20.12.2013), neyvert (18.12.2013), Romsb (30.01.2016), Sl_ (23.01.2016), Unknown (26.03.2015), Uvavan (13.06.2014) |
|
18.12.2013, 13:08
|
|
Заблокирован
Регистрация: 25.04.2013
Сообщений: 1,431
Сказал спасибо: 0
Сказали Спасибо 385 раз(а) в 254 сообщении(ях)
|
SPI 8bit STM32F0XX
При освоении STM32F0XX новички часто испытывают трудности запуска SPI в режиме 8бит и ниже. Причиной этому является значительное расширение возможностей модуля SPI, по сравнению с предшественниками, и некоторые изменения в работе модуля.
Что делать:
1. По умолчанию доступ к SPIx-›DR 16бит. Чтобы получить доступ к SPIx-›DR как к регистру 8бит пишем:
Код:
|
#define SPI1_DR_8bit (*(__IO uint8_t *)((uint32_t)&(SPI1-›DR)))
#define SPI2_DR_8bit (*(__IO uint8_t *)((uint32_t)&(SPI2-›DR))) |
Теперь обращаемся к регистру данных так:
Код:
|
SPI1_DR_8bit = 0x34;
SPI2_DR_8bit = 0x78; |
2. По умолчанию и при запрещённых установках DS [3:0]: Data size регистра SPIx_CR2 устанавливается длина данных 16бит (1111: 16-bit). Поэтому при настройке длины данных логичнее сбрасывать, а не устанавливать определённые биты. Иначе при любой попытке записи некорректного значения, регистр SPIx_CR2 автоматически восстановит длину данных 16бит.
Т.е. для того чтобы установить длину 8бит нужно просто сбросить старший бит DS [3:0] регистра SPIx_CR2.
3. Также встречались жалобы на непонятное поведение бита RXNE. Всё дело в бите FRXTH регистра SPIx_CR2, при работе с данными 8бит требуется его установить .
|
|
|
Эти 11 пользователя(ей) сказали Спасибо nahimovv за это сообщение:
|
|
|
19.12.2013, 21:23
|
|
Заблокирован
Регистрация: 25.04.2013
Сообщений: 1,431
Сказал спасибо: 0
Сказали Спасибо 385 раз(а) в 254 сообщении(ях)
|
TIM + DMA
Изюминкой STM32, ИМХО, является возможность связывать таймера между собой и широкие возможности DMA. Здесь возможности практически безграничны, что позволяет делать многие вещи по сути хардварными.
Процесс этот, порой, мозговыносящий и без хороших знаний работы модулей МК, логического анализатора и других средств отладки не сулит ничего хорошего. Однако результаты того стоят.
Самый простой пример - подключение 4-х разрядного LED-семисегментника с общим анодом (максимум до 8 разрядов). Сегменты подключены к PA0-PA7 через резисторы 200 Ом, аноды - к PA8-PA11 непосредственно, без ключей.
Всё это подключалось к STM32F0Discovery, МК STM32F051. Особенностью такого подключения является разнояркость разрядов, для компенсации которой был тоже задействован DMA. Итого задействовано - 1 таймер и 2 канала DMA (при подключении ключей в цепях анодов достаточно 1 таймера и 1 канала DMA). Результат - нет прерываний, обновление информации и коррекция разнояркости по сути хардварное.
Как это работает:
В общем случае - таймер периодически пинает DMA, DMA загружает данные из буфера в порт, в данном случае это GPIOA. Т.к. сегменты и разряды подключены к одному порту и их коммутация происходит одновременно, то никакого гашения/включения разрядов на время смены информации на сегментах не требуется, подсветка нерабочих сегментов отсутствует.
В данном случае, с коррекцией разнояркости - сначала таймер пинает один канал DMA для загрузки TIM_ARR, чем и определяется яркость разряда, потом пинает другой канал DMA, который и загружает порт данными о текущем разряде и состоянии сегментов.
Примерный код:
Здесь всё стандартно и ничего интересного.
Код:
|
#define LOW_BYTE(var) (*(__IO uint8_t *)((uint32_t)&(var)))
#define DIGITS 0x04 // количество разрядов LED
#define LED_PORT GPIOA
#define DIGIT_1 GPIO_ODR_11
#define DIGIT_2 GPIO_ODR_8
#define DIGIT_3 GPIO_ODR_9
#define DIGIT_4 GPIO_ODR_10
#define DIGIT_5 GPIO_ODR_12
#define DIGIT_6 GPIO_ODR_13
#define DIGIT_7 GPIO_ODR_14
#define DIGIT_8 GPIO_ODR_15
#define SEG_A GPIO_ODR_0
#define SEG_B GPIO_ODR_2
#define SEG_C GPIO_ODR_6
#define SEG_D GPIO_ODR_4
#define SEG_E GPIO_ODR_3
#define SEG_F GPIO_ODR_1
#define SEG_G GPIO_ODR_7
#define SEG_H GPIO_ODR_5
const uint8_t ten2led[] =
{
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // "0"
SEG_B | SEG_C, // "1"
SEG_A | SEG_B | SEG_G | SEG_E | SEG_D, // "2"
SEG_A | SEG_B | SEG_G | SEG_C | SEG_D, // "3"
SEG_F | SEG_G | SEG_B | SEG_C, // "4"
SEG_A | SEG_F | SEG_G | SEG_D | SEG_C, // "5"
SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G, // "6"
SEG_A | SEG_B | SEG_C, // "7"
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G, // "8"
SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G, // "9"
SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G, // "A"
SEG_C | SEG_D | SEG_E | SEG_F | SEG_G, // "b"
SEG_A | SEG_D | SEG_E | SEG_F, // "C"
SEG_B | SEG_C | SEG_D | SEG_E | SEG_G, // "d"
SEG_A | SEG_D | SEG_E | SEG_F | SEG_G, // "E"
SEG_A | SEG_E | SEG_F | SEG_G, // "F"
0x00 // " "
};
uint32_t tmp = 0;
uint32_t ttmp = 0;
uint8_t Ress[11]; |
Здесь содержится информация о разрядах и сегментах, которые DMA загружает в порт. Старшие байты DIGIT_Х содержат информацию о включённом разряде (в данном случае "1" соответствует включённому разряду) и при выполнении программы меняться не должны!
Младшие байты содержат информацию о включённых сегментах. Изначально содержат 0xFF, т.е. все сегменты выключены.
Код:
|
uint16_t Data_Buffer[DIGITS]=
{
DIGIT_1 | 0xFF, DIGIT_2 | 0xFF, DIGIT_3 | 0xFF, DIGIT_4 | 0xFF
}; |
Здесь содержатся значения яркости для каждого разряда которые DMA заносит в таймер. Изначально содержатся базовые значения яркости.
Код:
|
uint16_t TIM_Buffer[DIGITS]=
{
1000, 1000, 1000, 1000
}; |
Здесь содержатся значения яркости для различных высвечиваемых символов. 1000 - наибольшая яркость, 770 - наименьшая.
В данном случае значений всего три, что вполне достаточно. В других случаях можно задать для каждого символа свои значения.
Код:
|
const uint16_t TIM_ARR_val[] =
{
1000, // "0"
770, // "1"
920, // "2"
920, // "3"
920, // "4"
920, // "5"
1000, // "6"
770, // "7"
1000, // "8"
1000, // "9"
1000, // "A"
920, // "b"
920, // "C"
920, // "d"
920, // "E"
920, // "F"
770, // " "
}; |
Настройки DMA, как по учебнику, ничего особенного.
Код:
|
void Init_DMA(void)
{
RCC-›AHBENR |= RCC_AHBENR_DMA1EN;
DMA1_Channel3-›CPAR = (uint32_t)&GPIOA-›ODR; // DMA channel x peripheral address register
DMA1_Channel3-›CMAR = (uint32_t)Data_Buffer; // DMA channel x memory address register
DMA1_Channel3-›CNDTR = DIGITS; // DMA channel x number of data register
DMA1_Channel3-›CCR |= DMA_CCR_MSIZE_0; // Memory size 16 bit
DMA1_Channel3-›CCR |= DMA_CCR_PSIZE_0; // Peripheral size 16 bit
DMA1_Channel3-›CCR |= DMA_CCR_PL_1; // Channel Priority level High
DMA1_Channel3-›CCR |= DMA_CCR_MINC; // Memory increment mode
DMA1_Channel3-›CCR |= DMA_CCR_CIRC; // Circular mode
DMA1_Channel3-›CCR |= DMA_CCR_DIR; // Data transfer direction Memory -› Peripheral
DMA1_Channel3-›CCR |= DMA_CCR_EN; // Channel enable
//---------------------------
DMA1_Channel4-›CPAR = (uint32_t)&TIM3-›ARR; // DMA channel x peripheral address register
DMA1_Channel4-›CMAR = (uint32_t)TIM_Buffer; // DMA channel x memory address register
DMA1_Channel4-›CNDTR = DIGITS; // DMA channel x number of data register
DMA1_Channel4-›CCR |= DMA_CCR_MSIZE_0; // Memory size 16 bit
DMA1_Channel4-›CCR |= DMA_CCR_PSIZE_0; // Peripheral size 16 bit
DMA1_Channel4-›CCR |= DMA_CCR_PL; // Channel Priority level Very High
DMA1_Channel4-›CCR |= DMA_CCR_MINC; // Memory increment mode
DMA1_Channel4-›CCR |= DMA_CCR_CIRC; // Circular mode
DMA1_Channel4-›CCR |= DMA_CCR_DIR; // Data transfer direction Memory -› Peripheral
DMA1_Channel4-›CCR |= DMA_CCR_EN; // Channel enable
} |
Настройка таймера.
Настройка предделителя: (SystemCoreClock/(DIGITS * 50 * 1000)) - 1, где 50 значение в герцах на один разряд, 1000 - базовая константа яркости.
TIM3-›ARR = 1000 - 1, где 1000 - базовая константа яркости на время настройки таймера. При работе изменяется через DMA.
TIM3-›CCR1 = 25, где 25 - абстрактная константа, определяет промежуток времени между загрузкой значения яркости для разряда и последующим обновлением GPIOA.
Код:
|
void Init_TIM3(void)
{
RCC-›APB1ENR |= RCC_APB1ENR_TIM3EN; // TIM3 clock enable
TIM3-›PSC = (SystemCoreClock/(DIGITS * 50 * 1000)) - 1; //240
TIM3-›ARR = 1000 - 1;
TIM3-›CCR1 = 25;
TIM3-›DIER |= TIM_DIER_UDE; // Upload DMA Enable
TIM3-›DIER |= TIM_DIER_CC1DE;
TIM3-›CR1 |= TIM_CR1_CEN | TIM_CR1_ARPE; // Counter Enable
} |
Bin2BCD, в коментах не нуждается.
Код:
|
uint32_t Bin2BCD(uint32_t value, uint8_t *Res)
{
uint8_t *n = Res;
while (value › 0)
{
*Res++ = value % 10;
value /= 10;
}
return (uint32_t)(Res - n);
} |
Примерный майн - инкрементальный счётчик с инкрементом значения через ~100 миллисекунд.
Код:
|
int main(void)
{
Init_GPIO();
Init_DMA();
Init_TIM3();
while(1)
{
Bin2BCD(tmp, Ress);
ttmp = DIGITS;
while(ttmp--)
{
TIM_Buffer[ttmp] = TIM_ARR_val[Ress[ttmp]]; // Загружается значение яркости в соответствии с высвечиваемым
// символом для соответствующего разряда
LOW_BYTE(Data_Buffer[ttmp]) = ~ten2led[Ress[ttmp]]; // Загружаются данные по сегментам для соответствующего разряда
}
TIM_Buffer[1] += 100; // Для децимальной точки увеличиваем яркость
LOW_BYTE(Data_Buffer[1]) -= SEG_H; // Засвечиваем точку в соответствующем разряде
tmp++;
Delay_mS(100); // delay 100ms
}
while(1);
} |
Последний раз редактировалось nahimovv; 20.12.2013 в 23:44.
|
|
|
Эти 11 пользователя(ей) сказали Спасибо nahimovv за это сообщение:
|
-jonns- (21.03.2017), -shiva- (28.12.2013), dadigor (05.09.2016), dosikus (19.12.2013), kot-69 (20.12.2013), krug_vv (21.12.2013), MaxiMuz79 (20.12.2013), neyvert (28.12.2013), Nick19 (26.04.2015), sun4in (21.12.2013), ut1wpr (20.12.2013) |
|
08.08.2016, 14:35
|
|
Частый гость
Регистрация: 31.03.2006
Сообщений: 40
Сказал спасибо: 2
Сказали Спасибо 7 раз(а) в 7 сообщении(ях)
|
Быстый вывод отладочной инфармации Real Time Transfer
Обнаружил для себя новый быстрый вывод отладочный информации - Real Time Transfer.
Работает только с отладчиком J-Link. Подробно на сайте https://www.segger.com/jlink-rtt.html
Там же можно бесплатно скачать. Пример работы с RTT в моем архиве.
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 01:26.
|
|