PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : SMT32 - Ports konfigurieren, Frage zur C-Syntax



White_Fox
29.05.2016, 16:03
Hallo allerseits

Ich knabbere grad an der Konfiguration der Portpins eines STM32-F446RE (Nucleoboard).

Ich will PA5 als Ausgang (Push-Pull) und PC13 als Eingang (Floating).

So wie ich das sehe muß ich bloß eine 1 ins MODER-Register schreiben (für PA5) und kann den Rest so lassen. (Stimmt das überhaupt?)

Jetzt bin ich aber etwas über die Syntax verwirrt. Ich kenne C eher von der Windows-Programmierung her und hab C vllt eine Woche lang auf nem ATMega verwendet. Wenn ich da ein Bit in einem Register setzen will, tu ich das für gewöhnlich so:

Register |= ~(1<<Bit);

Jetzt hab ich mir, um die Registerbenennung für den STM32 mal in Code zu sehen, mal von STM32Cube was ausspucken lassen. Da sieht die Syntax jedoch gänzlich anders aus:

void MX_GPIO_Init(void)
{

GPIO_InitTypeDef GPIO_InitStruct;

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_EVT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

/*Configure GPIO pins : USART_TX_Pin USART_RX_Pin */
GPIO_InitStruct.Pin = USART_TX_Pin|USART_RX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/*Configure GPIO pin : LD2_Pin */
GPIO_InitStruct.Pin = LD2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

}

Ich bin jetzt etwas ratlos wegen z.B. der Registerbezeichnung

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
Ich hätte zuerst gedacht das sei C++, objektorientiert, aber soweit ich das bisher beurteilen kann scheint das wirklich C zu sein. Mein zweiter Einfall wäre, daß sich ST dafür eine Struktur ausgedacht hat. Eine Struktur wird doch aber üblicherweise so deklariert:

typedef struct
int a;
char b;
}Tstructname
Ein solches Codekonstrukt finde ich aber nirgends. Was hat es also damit auf sich?

Unregistriert
29.05.2016, 17:38
Hi,

das


Register |= ~(1<<Bit);

löscht ein Bit und setzt es nicht! Der |-Strich ist ein logisches Oder, die Tilde ~ eine logische Negation.
Mit diesem Konstrukt kannst du alle anderen Bits setzen (sofern sie das noch nicht sind), genau das benötigte Bit wird aber nicht gesetzt.
Um ein Bit zu setzen musst du schreiben:


Register |= (1<<Bit);

also obiges, nur ohne die Tilde.

Dinge wie


GPIO_MODE_AF_PP

sind sog. Enums (Enumeration), also chronologische Gebilde, die irgendwo in irgendeiner Datei definiert werden. Wo genau und wie genau das funktioniert kann ich dir jetzt nicht sagen, sorry.

White_Fox
29.05.2016, 18:06
löscht ein Bit und setzt es nicht!
Richtig...ich meinte auch das Gebilde ohne Tilde. Aber gut, darum gehts mir grad weniger.

Aber Enums sagen mir schon eher was...danke für den Tip. Ich vermute mal ich werd mich wohl mal durch die Standard-Bibliotheken qälen müssen.
Oder sehen wo ich eine vernünftige Doku dazu finde...

Sisor
29.05.2016, 18:14
Die Datei, in der die meisten Register-Konstanten definiert werden, heisst normalerweise stm32f4xx.h

Unregistriert
30.05.2016, 10:34
Hallo,

Du wirfst da ein bisschen was durcheinander.
Dieser ganze HAL-Kram kommt von dieser unerträglich ST-Library. Meine Empfehlung: Tu Dir den Mist erst gar nicht an. Schneller bist Du damit nicht, hast aber dafür umso mehr Ärger wenn mal was nicht geht.

Was Du suchst, steht in der stm32f446xx.h:



/**
* @brief General Purpose I/O
*/

typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;


Das ist die Grund(C-)struktur für die GPIOs.
Dazu kommen dann die Bit-Definitionen:




/************************************************** ****************************/
/* */
/* General Purpose I/O */
/* */
/************************************************** ****************************/
/****************** Bits definition for GPIO_MODER register *****************/
#define GPIO_MODER_MODER0 ((uint32_t)0x00000003)
#define GPIO_MODER_MODER0_0 ((uint32_t)0x00000001)
#define GPIO_MODER_MODER0_1 ((uint32_t)0x00000002)

#define GPIO_MODER_MODER1 ((uint32_t)0x0000000C)
#define GPIO_MODER_MODER1_0 ((uint32_t)0x00000004)
#define GPIO_MODER_MODER1_1 ((uint32_t)0x00000008)

#define GPIO_MODER_MODER2 ((uint32_t)0x00000030)
#define GPIO_MODER_MODER2_0 ((uint32_t)0x00000010)
#define GPIO_MODER_MODER2_1 ((uint32_t)0x00000020)

#define GPIO_MODER_MODER3 ((uint32_t)0x000000C0)
#define GPIO_MODER_MODER3_0 ((uint32_t)0x00000040)
#define GPIO_MODER_MODER3_1 ((uint32_t)0x00000080)

#define GPIO_MODER_MODER4 ((uint32_t)0x00000300)
#define GPIO_MODER_MODER4_0 ((uint32_t)0x00000100)
#define GPIO_MODER_MODER4_1 ((uint32_t)0x00000200)

#define GPIO_MODER_MODER5 ((uint32_t)0x00000C00)
#define GPIO_MODER_MODER5_0 ((uint32_t)0x00000400)
#define GPIO_MODER_MODER5_1 ((uint32_t)0x00000800)

#define GPIO_MODER_MODER6 ((uint32_t)0x00003000)
#define GPIO_MODER_MODER6_0 ((uint32_t)0x00001000)
#define GPIO_MODER_MODER6_1 ((uint32_t)0x00002000)

#define GPIO_MODER_MODER7 ((uint32_t)0x0000C000)
#define GPIO_MODER_MODER7_0 ((uint32_t)0x00004000)
#define GPIO_MODER_MODER7_1 ((uint32_t)0x00008000)

#define GPIO_MODER_MODER8 ((uint32_t)0x00030000)
#define GPIO_MODER_MODER8_0 ((uint32_t)0x00010000)
#define GPIO_MODER_MODER8_1 ((uint32_t)0x00020000)

#define GPIO_MODER_MODER9 ((uint32_t)0x000C0000)
#define GPIO_MODER_MODER9_0 ((uint32_t)0x00040000)
#define GPIO_MODER_MODER9_1 ((uint32_t)0x00080000)

#define GPIO_MODER_MODER10 ((uint32_t)0x00300000)
#define GPIO_MODER_MODER10_0 ((uint32_t)0x00100000)
#define GPIO_MODER_MODER10_1 ((uint32_t)0x00200000)

#define GPIO_MODER_MODER11 ((uint32_t)0x00C00000)
#define GPIO_MODER_MODER11_0 ((uint32_t)0x00400000)
#define GPIO_MODER_MODER11_1 ((uint32_t)0x00800000)

#define GPIO_MODER_MODER12 ((uint32_t)0x03000000)
#define GPIO_MODER_MODER12_0 ((uint32_t)0x01000000)
#define GPIO_MODER_MODER12_1 ((uint32_t)0x02000000)

#define GPIO_MODER_MODER13 ((uint32_t)0x0C000000)
#define GPIO_MODER_MODER13_0 ((uint32_t)0x04000000)
#define GPIO_MODER_MODER13_1 ((uint32_t)0x08000000)

#define GPIO_MODER_MODER14 ((uint32_t)0x30000000)
#define GPIO_MODER_MODER14_0 ((uint32_t)0x10000000)
#define GPIO_MODER_MODER14_1 ((uint32_t)0x20000000)

#define GPIO_MODER_MODER15 ((uint32_t)0xC0000000)
#define GPIO_MODER_MODER15_0 ((uint32_t)0x40000000)
#define GPIO_MODER_MODER15_1 ((uint32_t)0x80000000)

/****************** Bits definition for GPIO_OTYPER register ****************/
#define GPIO_OTYPER_OT_0 ((uint32_t)0x00000001)
#define GPIO_OTYPER_OT_1 ((uint32_t)0x00000002)
#define GPIO_OTYPER_OT_2 ((uint32_t)0x00000004)
#define GPIO_OTYPER_OT_3 ((uint32_t)0x00000008)
#define GPIO_OTYPER_OT_4 ((uint32_t)0x00000010)
#define GPIO_OTYPER_OT_5 ((uint32_t)0x00000020)
#define GPIO_OTYPER_OT_6 ((uint32_t)0x00000040)
#define GPIO_OTYPER_OT_7 ((uint32_t)0x00000080)
#define GPIO_OTYPER_OT_8 ((uint32_t)0x00000100)
#define GPIO_OTYPER_OT_9 ((uint32_t)0x00000200)
#define GPIO_OTYPER_OT_10 ((uint32_t)0x00000400)
#define GPIO_OTYPER_OT_11 ((uint32_t)0x00000800)
#define GPIO_OTYPER_OT_12 ((uint32_t)0x00001000)
#define GPIO_OTYPER_OT_13 ((uint32_t)0x00002000)
#define GPIO_OTYPER_OT_14 ((uint32_t)0x00004000)
#define GPIO_OTYPER_OT_15 ((uint32_t)0x00008000)

/****************** Bits definition for GPIO_OSPEEDR register ***************/
#define GPIO_OSPEEDER_OSPEEDR0 ((uint32_t)0x00000003)
#define GPIO_OSPEEDER_OSPEEDR0_0 ((uint32_t)0x00000001)
#define GPIO_OSPEEDER_OSPEEDR0_1 ((uint32_t)0x00000002)

#define GPIO_OSPEEDER_OSPEEDR1 ((uint32_t)0x0000000C)
#define GPIO_OSPEEDER_OSPEEDR1_0 ((uint32_t)0x00000004)
#define GPIO_OSPEEDER_OSPEEDR1_1 ((uint32_t)0x00000008)

#define GPIO_OSPEEDER_OSPEEDR2 ((uint32_t)0x00000030)
#define GPIO_OSPEEDER_OSPEEDR2_0 ((uint32_t)0x00000010)
#define GPIO_OSPEEDER_OSPEEDR2_1 ((uint32_t)0x00000020)

#define GPIO_OSPEEDER_OSPEEDR3 ((uint32_t)0x000000C0)
#define GPIO_OSPEEDER_OSPEEDR3_0 ((uint32_t)0x00000040)
#define GPIO_OSPEEDER_OSPEEDR3_1 ((uint32_t)0x00000080)

#define GPIO_OSPEEDER_OSPEEDR4 ((uint32_t)0x00000300)
#define GPIO_OSPEEDER_OSPEEDR4_0 ((uint32_t)0x00000100)
#define GPIO_OSPEEDER_OSPEEDR4_1 ((uint32_t)0x00000200)

#define GPIO_OSPEEDER_OSPEEDR5 ((uint32_t)0x00000C00)
#define GPIO_OSPEEDER_OSPEEDR5_0 ((uint32_t)0x00000400)
#define GPIO_OSPEEDER_OSPEEDR5_1 ((uint32_t)0x00000800)

#define GPIO_OSPEEDER_OSPEEDR6 ((uint32_t)0x00003000)
#define GPIO_OSPEEDER_OSPEEDR6_0 ((uint32_t)0x00001000)
#define GPIO_OSPEEDER_OSPEEDR6_1 ((uint32_t)0x00002000)

#define GPIO_OSPEEDER_OSPEEDR7 ((uint32_t)0x0000C000)
#define GPIO_OSPEEDER_OSPEEDR7_0 ((uint32_t)0x00004000)
#define GPIO_OSPEEDER_OSPEEDR7_1 ((uint32_t)0x00008000)

#define GPIO_OSPEEDER_OSPEEDR8 ((uint32_t)0x00030000)
#define GPIO_OSPEEDER_OSPEEDR8_0 ((uint32_t)0x00010000)
#define GPIO_OSPEEDER_OSPEEDR8_1 ((uint32_t)0x00020000)

#define GPIO_OSPEEDER_OSPEEDR9 ((uint32_t)0x000C0000)
#define GPIO_OSPEEDER_OSPEEDR9_0 ((uint32_t)0x00040000)
#define GPIO_OSPEEDER_OSPEEDR9_1 ((uint32_t)0x00080000)

#define GPIO_OSPEEDER_OSPEEDR10 ((uint32_t)0x00300000)
#define GPIO_OSPEEDER_OSPEEDR10_0 ((uint32_t)0x00100000)
#define GPIO_OSPEEDER_OSPEEDR10_1 ((uint32_t)0x00200000)

#define GPIO_OSPEEDER_OSPEEDR11 ((uint32_t)0x00C00000)
#define GPIO_OSPEEDER_OSPEEDR11_0 ((uint32_t)0x00400000)
#define GPIO_OSPEEDER_OSPEEDR11_1 ((uint32_t)0x00800000)

#define GPIO_OSPEEDER_OSPEEDR12 ((uint32_t)0x03000000)
#define GPIO_OSPEEDER_OSPEEDR12_0 ((uint32_t)0x01000000)
#define GPIO_OSPEEDER_OSPEEDR12_1 ((uint32_t)0x02000000)

#define GPIO_OSPEEDER_OSPEEDR13 ((uint32_t)0x0C000000)
#define GPIO_OSPEEDER_OSPEEDR13_0 ((uint32_t)0x04000000)
#define GPIO_OSPEEDER_OSPEEDR13_1 ((uint32_t)0x08000000)

#define GPIO_OSPEEDER_OSPEEDR14 ((uint32_t)0x30000000)
#define GPIO_OSPEEDER_OSPEEDR14_0 ((uint32_t)0x10000000)
#define GPIO_OSPEEDER_OSPEEDR14_1 ((uint32_t)0x20000000)

#define GPIO_OSPEEDER_OSPEEDR15 ((uint32_t)0xC0000000)
#define GPIO_OSPEEDER_OSPEEDR15_0 ((uint32_t)0x40000000)
#define GPIO_OSPEEDER_OSPEEDR15_1 ((uint32_t)0x80000000)

/****************** Bits definition for GPIO_PUPDR register *****************/
#define GPIO_PUPDR_PUPDR0 ((uint32_t)0x00000003)
#define GPIO_PUPDR_PUPDR0_0 ((uint32_t)0x00000001)
#define GPIO_PUPDR_PUPDR0_1 ((uint32_t)0x00000002)

#define GPIO_PUPDR_PUPDR1 ((uint32_t)0x0000000C)
#define GPIO_PUPDR_PUPDR1_0 ((uint32_t)0x00000004)
#define GPIO_PUPDR_PUPDR1_1 ((uint32_t)0x00000008)

#define GPIO_PUPDR_PUPDR2 ((uint32_t)0x00000030)
#define GPIO_PUPDR_PUPDR2_0 ((uint32_t)0x00000010)
#define GPIO_PUPDR_PUPDR2_1 ((uint32_t)0x00000020)

#define GPIO_PUPDR_PUPDR3 ((uint32_t)0x000000C0)
#define GPIO_PUPDR_PUPDR3_0 ((uint32_t)0x00000040)
#define GPIO_PUPDR_PUPDR3_1 ((uint32_t)0x00000080)

#define GPIO_PUPDR_PUPDR4 ((uint32_t)0x00000300)
#define GPIO_PUPDR_PUPDR4_0 ((uint32_t)0x00000100)
#define GPIO_PUPDR_PUPDR4_1 ((uint32_t)0x00000200)

#define GPIO_PUPDR_PUPDR5 ((uint32_t)0x00000C00)
#define GPIO_PUPDR_PUPDR5_0 ((uint32_t)0x00000400)
#define GPIO_PUPDR_PUPDR5_1 ((uint32_t)0x00000800)

#define GPIO_PUPDR_PUPDR6 ((uint32_t)0x00003000)
#define GPIO_PUPDR_PUPDR6_0 ((uint32_t)0x00001000)
#define GPIO_PUPDR_PUPDR6_1 ((uint32_t)0x00002000)

#define GPIO_PUPDR_PUPDR7 ((uint32_t)0x0000C000)
#define GPIO_PUPDR_PUPDR7_0 ((uint32_t)0x00004000)
#define GPIO_PUPDR_PUPDR7_1 ((uint32_t)0x00008000)

#define GPIO_PUPDR_PUPDR8 ((uint32_t)0x00030000)
#define GPIO_PUPDR_PUPDR8_0 ((uint32_t)0x00010000)
#define GPIO_PUPDR_PUPDR8_1 ((uint32_t)0x00020000)

#define GPIO_PUPDR_PUPDR9 ((uint32_t)0x000C0000)
#define GPIO_PUPDR_PUPDR9_0 ((uint32_t)0x00040000)
#define GPIO_PUPDR_PUPDR9_1 ((uint32_t)0x00080000)

#define GPIO_PUPDR_PUPDR10 ((uint32_t)0x00300000)
#define GPIO_PUPDR_PUPDR10_0 ((uint32_t)0x00100000)
#define GPIO_PUPDR_PUPDR10_1 ((uint32_t)0x00200000)

#define GPIO_PUPDR_PUPDR11 ((uint32_t)0x00C00000)
#define GPIO_PUPDR_PUPDR11_0 ((uint32_t)0x00400000)
#define GPIO_PUPDR_PUPDR11_1 ((uint32_t)0x00800000)

#define GPIO_PUPDR_PUPDR12 ((uint32_t)0x03000000)
#define GPIO_PUPDR_PUPDR12_0 ((uint32_t)0x01000000)
#define GPIO_PUPDR_PUPDR12_1 ((uint32_t)0x02000000)

#define GPIO_PUPDR_PUPDR13 ((uint32_t)0x0C000000)
#define GPIO_PUPDR_PUPDR13_0 ((uint32_t)0x04000000)
#define GPIO_PUPDR_PUPDR13_1 ((uint32_t)0x08000000)

#define GPIO_PUPDR_PUPDR14 ((uint32_t)0x30000000)
#define GPIO_PUPDR_PUPDR14_0 ((uint32_t)0x10000000)
#define GPIO_PUPDR_PUPDR14_1 ((uint32_t)0x20000000)

#define GPIO_PUPDR_PUPDR15 ((uint32_t)0xC0000000)
#define GPIO_PUPDR_PUPDR15_0 ((uint32_t)0x40000000)
#define GPIO_PUPDR_PUPDR15_1 ((uint32_t)0x80000000)

/****************** Bits definition for GPIO_IDR register *******************/
#define GPIO_IDR_IDR_0 ((uint32_t)0x00000001)
#define GPIO_IDR_IDR_1 ((uint32_t)0x00000002)
#define GPIO_IDR_IDR_2 ((uint32_t)0x00000004)
#define GPIO_IDR_IDR_3 ((uint32_t)0x00000008)
#define GPIO_IDR_IDR_4 ((uint32_t)0x00000010)
#define GPIO_IDR_IDR_5 ((uint32_t)0x00000020)
#define GPIO_IDR_IDR_6 ((uint32_t)0x00000040)
#define GPIO_IDR_IDR_7 ((uint32_t)0x00000080)
#define GPIO_IDR_IDR_8 ((uint32_t)0x00000100)
#define GPIO_IDR_IDR_9 ((uint32_t)0x00000200)
#define GPIO_IDR_IDR_10 ((uint32_t)0x00000400)
#define GPIO_IDR_IDR_11 ((uint32_t)0x00000800)
#define GPIO_IDR_IDR_12 ((uint32_t)0x00001000)
#define GPIO_IDR_IDR_13 ((uint32_t)0x00002000)
#define GPIO_IDR_IDR_14 ((uint32_t)0x00004000)
#define GPIO_IDR_IDR_15 ((uint32_t)0x00008000)
/* Old GPIO_IDR register bits definition, maintained for legacy purpose */
#define GPIO_OTYPER_IDR_0 GPIO_IDR_IDR_0
#define GPIO_OTYPER_IDR_1 GPIO_IDR_IDR_1
#define GPIO_OTYPER_IDR_2 GPIO_IDR_IDR_2
#define GPIO_OTYPER_IDR_3 GPIO_IDR_IDR_3
#define GPIO_OTYPER_IDR_4 GPIO_IDR_IDR_4
#define GPIO_OTYPER_IDR_5 GPIO_IDR_IDR_5
#define GPIO_OTYPER_IDR_6 GPIO_IDR_IDR_6
#define GPIO_OTYPER_IDR_7 GPIO_IDR_IDR_7
#define GPIO_OTYPER_IDR_8 GPIO_IDR_IDR_8
#define GPIO_OTYPER_IDR_9 GPIO_IDR_IDR_9
#define GPIO_OTYPER_IDR_10 GPIO_IDR_IDR_10
#define GPIO_OTYPER_IDR_11 GPIO_IDR_IDR_11
#define GPIO_OTYPER_IDR_12 GPIO_IDR_IDR_12
#define GPIO_OTYPER_IDR_13 GPIO_IDR_IDR_13
#define GPIO_OTYPER_IDR_14 GPIO_IDR_IDR_14
#define GPIO_OTYPER_IDR_15 GPIO_IDR_IDR_15

/****************** Bits definition for GPIO_ODR register *******************/
#define GPIO_ODR_ODR_0 ((uint32_t)0x00000001)
#define GPIO_ODR_ODR_1 ((uint32_t)0x00000002)
#define GPIO_ODR_ODR_2 ((uint32_t)0x00000004)
#define GPIO_ODR_ODR_3 ((uint32_t)0x00000008)
#define GPIO_ODR_ODR_4 ((uint32_t)0x00000010)
#define GPIO_ODR_ODR_5 ((uint32_t)0x00000020)
#define GPIO_ODR_ODR_6 ((uint32_t)0x00000040)
#define GPIO_ODR_ODR_7 ((uint32_t)0x00000080)
#define GPIO_ODR_ODR_8 ((uint32_t)0x00000100)
#define GPIO_ODR_ODR_9 ((uint32_t)0x00000200)
#define GPIO_ODR_ODR_10 ((uint32_t)0x00000400)
#define GPIO_ODR_ODR_11 ((uint32_t)0x00000800)
#define GPIO_ODR_ODR_12 ((uint32_t)0x00001000)
#define GPIO_ODR_ODR_13 ((uint32_t)0x00002000)
#define GPIO_ODR_ODR_14 ((uint32_t)0x00004000)
#define GPIO_ODR_ODR_15 ((uint32_t)0x00008000)
/* Old GPIO_ODR register bits definition, maintained for legacy purpose */
#define GPIO_OTYPER_ODR_0 GPIO_ODR_ODR_0
#define GPIO_OTYPER_ODR_1 GPIO_ODR_ODR_1
#define GPIO_OTYPER_ODR_2 GPIO_ODR_ODR_2
#define GPIO_OTYPER_ODR_3 GPIO_ODR_ODR_3
#define GPIO_OTYPER_ODR_4 GPIO_ODR_ODR_4
#define GPIO_OTYPER_ODR_5 GPIO_ODR_ODR_5
#define GPIO_OTYPER_ODR_6 GPIO_ODR_ODR_6
#define GPIO_OTYPER_ODR_7 GPIO_ODR_ODR_7
#define GPIO_OTYPER_ODR_8 GPIO_ODR_ODR_8
#define GPIO_OTYPER_ODR_9 GPIO_ODR_ODR_9
#define GPIO_OTYPER_ODR_10 GPIO_ODR_ODR_10
#define GPIO_OTYPER_ODR_11 GPIO_ODR_ODR_11
#define GPIO_OTYPER_ODR_12 GPIO_ODR_ODR_12
#define GPIO_OTYPER_ODR_13 GPIO_ODR_ODR_13
#define GPIO_OTYPER_ODR_14 GPIO_ODR_ODR_14
#define GPIO_OTYPER_ODR_15 GPIO_ODR_ODR_15

/****************** Bits definition for GPIO_BSRR register ******************/
#define GPIO_BSRR_BS_0 ((uint32_t)0x00000001)
#define GPIO_BSRR_BS_1 ((uint32_t)0x00000002)
#define GPIO_BSRR_BS_2 ((uint32_t)0x00000004)
#define GPIO_BSRR_BS_3 ((uint32_t)0x00000008)
#define GPIO_BSRR_BS_4 ((uint32_t)0x00000010)
#define GPIO_BSRR_BS_5 ((uint32_t)0x00000020)
#define GPIO_BSRR_BS_6 ((uint32_t)0x00000040)
#define GPIO_BSRR_BS_7 ((uint32_t)0x00000080)
#define GPIO_BSRR_BS_8 ((uint32_t)0x00000100)
#define GPIO_BSRR_BS_9 ((uint32_t)0x00000200)
#define GPIO_BSRR_BS_10 ((uint32_t)0x00000400)
#define GPIO_BSRR_BS_11 ((uint32_t)0x00000800)
#define GPIO_BSRR_BS_12 ((uint32_t)0x00001000)
#define GPIO_BSRR_BS_13 ((uint32_t)0x00002000)
#define GPIO_BSRR_BS_14 ((uint32_t)0x00004000)
#define GPIO_BSRR_BS_15 ((uint32_t)0x00008000)
#define GPIO_BSRR_BR_0 ((uint32_t)0x00010000)
#define GPIO_BSRR_BR_1 ((uint32_t)0x00020000)
#define GPIO_BSRR_BR_2 ((uint32_t)0x00040000)
#define GPIO_BSRR_BR_3 ((uint32_t)0x00080000)
#define GPIO_BSRR_BR_4 ((uint32_t)0x00100000)
#define GPIO_BSRR_BR_5 ((uint32_t)0x00200000)
#define GPIO_BSRR_BR_6 ((uint32_t)0x00400000)
#define GPIO_BSRR_BR_7 ((uint32_t)0x00800000)
#define GPIO_BSRR_BR_8 ((uint32_t)0x01000000)
#define GPIO_BSRR_BR_9 ((uint32_t)0x02000000)
#define GPIO_BSRR_BR_10 ((uint32_t)0x04000000)
#define GPIO_BSRR_BR_11 ((uint32_t)0x08000000)
#define GPIO_BSRR_BR_12 ((uint32_t)0x10000000)
#define GPIO_BSRR_BR_13 ((uint32_t)0x20000000)
#define GPIO_BSRR_BR_14 ((uint32_t)0x40000000)
#define GPIO_BSRR_BR_15 ((uint32_t)0x80000000)

/****************** Bit definition for GPIO_LCKR register *********************/
#define GPIO_LCKR_LCK0 ((uint32_t)0x00000001)
#define GPIO_LCKR_LCK1 ((uint32_t)0x00000002)
#define GPIO_LCKR_LCK2 ((uint32_t)0x00000004)
#define GPIO_LCKR_LCK3 ((uint32_t)0x00000008)
#define GPIO_LCKR_LCK4 ((uint32_t)0x00000010)
#define GPIO_LCKR_LCK5 ((uint32_t)0x00000020)
#define GPIO_LCKR_LCK6 ((uint32_t)0x00000040)
#define GPIO_LCKR_LCK7 ((uint32_t)0x00000080)
#define GPIO_LCKR_LCK8 ((uint32_t)0x00000100)
#define GPIO_LCKR_LCK9 ((uint32_t)0x00000200)
#define GPIO_LCKR_LCK10 ((uint32_t)0x00000400)
#define GPIO_LCKR_LCK11 ((uint32_t)0x00000800)
#define GPIO_LCKR_LCK12 ((uint32_t)0x00001000)
#define GPIO_LCKR_LCK13 ((uint32_t)0x00002000)
#define GPIO_LCKR_LCK14 ((uint32_t)0x00004000)
#define GPIO_LCKR_LCK15 ((uint32_t)0x00008000)
#define GPIO_LCKR_LCKK ((uint32_t)0x00010000)



Konfigurieren tust Du die IOs dann z.B. so:



GPIOA->MODER |= ( GPIO_MODER_MODER5_0 );
GPIOA->OSPEEDR |= ( GPIO_OSPEEDER_OSPEEDR5_1 );



Fertig ist die Laube.

White_Fox
10.06.2016, 16:22
Vielen Dank für eure Hilfe, entschuldigt bitte daß ich mich so lange nicht mehr gemeldet habe. HW-Entwicklung ging vor.
Ich denk ich hab den Code verstanden.
Und ich hab jetzt die richtigen Dateien...und noch nen Riesenhaufen Bibliotheken für die Peripherie.

Ich kriegs nur nicht hin, die Bibliotheken korrekt ins Projekt einzubinden. Ich will mit meinem Programm ja bloß eine LED blinken lassen (und als erste Vorstufe die LED erstmal nur einschalten). Dazu wollte ich die GPIO-Bibliothek einbinden, aber das funktioniert nicht. Die include-Anweisung meckert der Kompiler jedesmal an.

Jetzt hab ich mal aus Spaß einfach die letzten beiden Zeilen meines Vorposters stumpf in die main-Funktion kopiert. Wider Erwarten kommt keine Fehlermeldung was mich jetzt wiederum zu der Frage führt: Brauch ich die GPIO-Dateien um die Pins wackeln zu lassen? (Offenbar ist dem ja aber so.) Wenn ich die GPIO-Bibliothek dafür nicht brauche, wofür sind die dann gut?

Edit:
Gemeint ist natürlich die stm32f4xx_gpio.h.

Unregistriert
12.06.2016, 06:38
Jetzt hab ich mal aus Spaß einfach die letzten beiden Zeilen meines Vorposters stumpf in die main-Funktion kopiert. Wider Erwarten kommt keine Fehlermeldung was mich jetzt wiederum zu der Frage führt: Brauch ich die GPIO-Dateien um die Pins wackeln zu lassen? (Offenbar ist dem ja aber so.) Wenn ich die GPIO-Bibliothek dafür nicht brauche, wofür sind die dann gut?

Edit:
Gemeint ist natürlich die stm32f4xx_gpio.h.


Habe mich vielleicht ein bisschen unklar ausgedrückt:

Die stm32f4xx_gpio.h brauchst Du nur, wenn Du die - meiner Meinung nach abgrundtief schlechte - ST-Library nutzt. Dann musst Du diesen ganzen Unsinn mit dem Strukturen-Füllen machen.

Wenn Du aber die Register direkt beschreibst, dann kannst Du die ganzen anderen Includes weglassen. Ausschließlich die Definitionen aus der stm32f446xx.h sind nötig, damit Du die Register und Bits mit Namen ansprechen kannst.
Für den reinen Cortex-Kern kannst Du noch die CMSIS-Header einbinden.

Bei meinen Projekten mache ich es grundsätzlich so:
Von ST nutze ich:
- stm32f446xx.h
- Die CMSIS-Includes (also die core_cm4.h includen)
- startup_stm32f446xx.s (Hier stehen die Deklarationen für die Interrupthandler drin sowie der Assemblercode für die Initialisierung der statischen Variable, etc.)

Das war es auch schon.
Die Initialisierung der MCU mache ich komplett selber.
In der startup_stm32f446xx.s wird zuerst die Funktion SystemInit aufgerufen, danach dann die Funktion main.

In der von mir erstellten Funktion SystemInit initialisiere ich die MCU. Also zuerst aktiviere ich die Caches, konfiguriere den Prefetch und die Waitstates.
Anschließend konfiguriere ich die PLLs und schalte auf die entsprechende Taktquelle (meistens HSE, entweder extern -> Nucleo oder Quarz) um.

Ob man dann die Peripherie wie GPIOs, Timer, Systick, etc. in der SystemInit konfiguriert oder erst später, ist dann Geschmackssache.

White_Fox
12.06.2016, 16:00
Ah ja...vielen Dank, jetzt ist mir Einiges schon viel klarer, ein paar Feagen hab ich aber noch.

Was ist eigentlich die genaue Funktion des Output-Speed-Registers? Ich werd da aus dem Datenblatt nicht schlau, kann aber auch daran liegen daß ich immer noch mehr oder weniger in meiner AVR-Denke drinhänge und imemr wieder staune, um welchen Mist man sich beim ARM kümmern muß.
Muß ich das Speed-Register eigentlich beschreiben oder wird sich auch so etwas tun?

Ich muß ja, soweit ich das mitbekommen habe, den Port noch irgendwie an einen Bus anbinden. Nach was richtet man sich da, woher weiß ich wie ich da den Port am besten wo anbinde? Wie geht man da vor allem vor, wenn man etwas mehr Peripherie verwendet?

Kann meinen Code leider nicht testen, weil ich Embedded Studio nicht mit dem J-Link zusammenkriege. :mad:
J-Link Commander hat aber seltsamerweise keine Probleme mit dem J-Link zu quatschen.



#include <stdio.h>
#include <stdlib.h>
#include <stm32f446xx.h>

/************************************************** *******************
*
* main()
*
* Function description
* Application entry point.
*/
void main(void) {
GPIOA->MODER |= (GPIO_MODER_MODER5_0); //A5 => Ausgang
GPIOA->OTYPER |= (GPIO_OTYPER_OT_5); //A5 0> OpenDrain
GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR5); //A5 ohne Pullup-Pulldown
}

Unregistriert
13.06.2016, 18:17
Der Output-Speed ist für die Schaltflanken der IOs zuständig.
Er kontrolliert also die Slew-Rate (also die Anstiegs- und Abfallzeit der Flanken).
Je größer die Anstiegszeit, desto größer sind die abgestrahlten Störsignale. Daher sollte man den Output Speed nur so schnell wie nötig konfigurieren.

Das Speed-Register musst Du übrigens nicht beschreiben, nach dem Reset ist die Default-Einstellung 00 (also Low speed). Nur Port B ist da eine Ausnahme.

Die Komplexität hat übrigens nichts mit dem ARM-Kern zu tun. ST hätte auch Simpel-Peripherie wie beim AVR an den Cortex anbinden können. Aber das wäre doch Verschwendung ;)

Was Du mit "Bus-Anbinden" meinst, verstehe ich nicht. Der Port läuft nach dem Reset sofort (bis auf die paar Ausnahmen wie JTAG, etc.). Du musst nur den Takt für den entsprechenden Port einschalten, wie auch für jede andere Peripherie.
Also z.B. für Port A:

RCC->AHB1ENR |= ( RCC_AHB1ENR_GPIOAEN );
Danach kannst Du dann ganz normal Bits setzen oder zurücksetzen:

GPIOA->BSRR |= GPIO_BSRR_BS_0;
oder

GPIOA->BSRR |= GPIO_BSRR_BR_0;

Oder meinst Du das Umlegen der Peripherie-Funktionen auf andere Pins über das AFR-Register?




Ah ja...vielen Dank, jetzt ist mir Einiges schon viel klarer, ein paar Feagen hab ich aber noch.

Was ist eigentlich die genaue Funktion des Output-Speed-Registers? Ich werd da aus dem Datenblatt nicht schlau, kann aber auch daran liegen daß ich immer noch mehr oder weniger in meiner AVR-Denke drinhänge und imemr wieder staune, um welchen Mist man sich beim ARM kümmern muß.
Muß ich das Speed-Register eigentlich beschreiben oder wird sich auch so etwas tun?

Ich muß ja, soweit ich das mitbekommen habe, den Port noch irgendwie an einen Bus anbinden. Nach was richtet man sich da, woher weiß ich wie ich da den Port am besten wo anbinde? Wie geht man da vor allem vor, wenn man etwas mehr Peripherie verwendet?

Kann meinen Code leider nicht testen, weil ich Embedded Studio nicht mit dem J-Link zusammenkriege. :mad:
J-Link Commander hat aber seltsamerweise keine Probleme mit dem J-Link zu quatschen.



#include <stdio.h>
#include <stdlib.h>
#include <stm32f446xx.h>

/************************************************** *******************
*
* main()
*
* Function description
* Application entry point.
*/
void main(void) {
GPIOA->MODER |= (GPIO_MODER_MODER5_0); //A5 => Ausgang
GPIOA->OTYPER |= (GPIO_OTYPER_OT_5); //A5 0> OpenDrain
GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR5); //A5 ohne Pullup-Pulldown
}

Peter(TOO)
14.06.2016, 13:49
Hallo,


Jetzt bin ich aber etwas über die Syntax verwirrt. Ich kenne C eher von der Windows-Programmierung her und hab C vllt eine Woche lang auf nem ATMega verwendet. Wenn ich da ein Bit in einem Register setzen will, tu ich das für gewöhnlich so:

Register |= (1<<Bit);
Einzelne Register sind Write-Only, da funktioniert dies nicht!
Beim STM32 werden diese als 0 gelesen, bei anderen Prozessoren auch als 1.

Bei manchen µC und Anwendungen kommt man dann nicht darum herum Schatten-Register zu führen.
Dies dürfte auch eine der Ideen hinter den Bibliotheken für den Hardware Abstraction Layer sein.

MfG Peter(TOO)

White_Fox
17.06.2016, 20:24
Ich kam heute mal dazu meinen code zu testen.


#include <stdio.h>
#include <stdlib.h>
#include <stm32f446xx.h>

/************************************************** *******************
*
* main()
*
* Function description
* Application entry point.
*/
void main(void) {
GPIOA->MODER |= (GPIO_MODER_MODER5_0); //A5 => Ausgang
GPIOA->OTYPER |= (GPIO_OTYPER_OT_5); //A5 0> OpenDrain
GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR5); //A5 ohne Pullup-Pulldown

printf("Einstellung fertig\n");

GPIOA->ODR |= (GPIO_ODR_ODR_5); //Ausgang A5 setzen

printf("LED gesetzt\n");

while(1){
}
}

Jetzt müßte die LED an A5 leuchten. Tut sie aber nicht. Hab ich noch was vergessen oder einen Fehler gemacht?
Wenn ich den Debugger starte, kommen die printf-Meldungen. Der J-Link ist auch verbunden. Sagt Embedded Studio. Nurdie LED tut nix, wie gesagt...

Peter(TOO)
17.06.2016, 20:47
Ich kam heute mal dazu meinen code zu testen.

Jetzt müßte die LED an A5 leuchten. Tut sie aber nicht. Hab ich noch was vergessen oder einen Fehler gemacht?
Wenn ich den Debugger starte, kommen die printf-Meldungen. Der J-Link ist auch verbunden. Sagt Embedded Studio. Nurdie LED tut nix, wie gesagt...

Wie ist die LED angeschlossen?

A5 setzt du als Open Drain, da schaltet der Transistor nur gegen Masse. andernfalls ist der Pin einfach hochohmig.
Die LED muss also vom Pin über einen Widerstand gegen die positive Versorgungsspannung angeschlossen sein.

Damit der Transistor aber nach Masse schaltet musst du eine 0 Ausgeben!

MfG Peter(TOO)

White_Fox
17.06.2016, 21:30
Hm...stimmt, hätte ich mal schauen sollen ob die LED an Masse oder 3%5V hängt.

Man, Tests am Abend...

Unregistriert
18.06.2016, 12:18
1. Hast Du den Port A mit Takt versorgt?


RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

2. Wieso konfigurierst Du den Port nicht auf Push-Pull?

3. Ich empfehle Dir, das Du Dir angewöhnst, Port Pins mit Bitset (oder Bitreset) zu setzen. Deine Operation auf dem ODR ist nicht atomar, damit kannst Du also unerwünschte Nebeneffekte erzeugen, wenn andere Portpins von Port A genutzt werden. Nimm ODR nur, wenn Du den ganzen Port setzen willst.


GPIOA->BSRR |= GPIO_BSRR_BS_5;
oder

GPIOA->BSRR |= GPIO_BSRR_BR_5;



Ich kam heute mal dazu meinen code zu testen.


#include <stdio.h>
#include <stdlib.h>
#include <stm32f446xx.h>

/************************************************** *******************
*
* main()
*
* Function description
* Application entry point.
*/
void main(void) {
GPIOA->MODER |= (GPIO_MODER_MODER5_0); //A5 => Ausgang
GPIOA->OTYPER |= (GPIO_OTYPER_OT_5); //A5 0> OpenDrain
GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR5); //A5 ohne Pullup-Pulldown

printf("Einstellung fertig\n");

GPIOA->ODR |= (GPIO_ODR_ODR_5); //Ausgang A5 setzen

printf("LED gesetzt\n");

while(1){
}
}

Jetzt müßte die LED an A5 leuchten. Tut sie aber nicht. Hab ich noch was vergessen oder einen Fehler gemacht?
Wenn ich den Debugger starte, kommen die printf-Meldungen. Der J-Link ist auch verbunden. Sagt Embedded Studio. Nurdie LED tut nix, wie gesagt...

White_Fox
18.06.2016, 12:42
Hallo Unregistrierter


1. Hast Du den Port A mit Takt versorgt?
Gnaa....nein, das fehlt noch.
Und eigentlich wollte ich den Ausgang PP setzen...Open Drain macht nicht allzuviel Sinn. Keine Ahnung wie sich das eingeschlichen hat.



3. Ich empfehle Dir, das Du Dir angewöhnst, Port Pins mit Bitset (oder Bitreset) zu setzen. Deine Operation auf dem ODR ist nicht atomar, damit kannst Du also unerwünschte Nebeneffekte erzeugen, wenn andere Portpins von Port A genutzt werden. Nimm ODR nur, wenn Du den ganzen Port setzen willst.
Wo ist denn da genau der Unterschied? Danke auf jeden Fall für den Tip, das werd ich so machen.

White_Fox
18.06.2016, 16:35
Das verstehe wer will...die LED tut nix.
Laut Dokumentation soll die LED leuchten, wenn PA5 HIGH-Pegel hat.
Taktversorgung ist jetzt auch drin.


#include <stdio.h>
#include <stdlib.h>
#include <stm32f446xx.h>

void main(void) {
GPIOA->MODER |= (GPIO_MODER_MODER5_0); //A5 => Ausgang
GPIOA->OTYPER &= ~(GPIO_OTYPER_OT_5); //A5 0> Push-Pull
GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR5); //A5 ohne Pullup-Pulldown
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Taktversorgung Port A

//printf("Einstellung fertig\n");

GPIOA->BSRR |= GPIO_BSRR_BS_5; //Ausgang A5 setzen

//printf("LED gesetzt\n");

while(1){
}
}

Soweit ich das sehe hab ich doch alles drin:

A5 als Ausgang definiert (MODER 01)
Outout-Type als Push-Pull definiert (OTYPER 0)
Pullup/down-Widerstände abgeschaltet (PUPDR 00)
Taktversorgung einschalten
PA5 setzen


Danach: Über J-Link mit Target verbinden und dann Download File...

Hat jemand ne Idee was da noch fehlt?


Edit:
Es funktioniert endlich. Die Taktversorgung gehört an den Anfang. Anscheinend werden die Befehle, mit denen die GPIO-Register beschrieben werden, ohne Taktversorgung nicht ausgeführt.

Vielen Dank für die Hilfe bisher, vor allem an dich, Unregistrierter. :)

Unregistriert
18.06.2016, 17:53
Edit:
Es funktioniert endlich. Die Taktversorgung gehört an den Anfang. Anscheinend werden die Befehle, mit denen die GPIO-Register beschrieben werden, ohne Taktversorgung nicht ausgeführt.

Vielen Dank für die Hilfe bisher, vor allem an dich, Unregistrierter. :)


Das liegt daran, dass die ganze Port-Logik deaktiviert ist, wenn der Takt nicht anliegt.

Habe aber noch einen kleinen Fehler gemacht (der sich hier nicht auswirkt aufgrund der internen Logik des Controllers, aber nichtsdestotrotz sollte man das anders machen...):
Das BSRR-Register ist ein Write-Only-Register. Daher muss die Anweisung wie folgt lauten - auch wenn es jetzt so klappt:



GPIOA->BSRR = GPIO_BSRR_BS_5;


Also keinen Oder-Operator benutzen.

Peter(TOO)
18.06.2016, 18:41
Es funktioniert endlich. Die Taktversorgung gehört an den Anfang. Anscheinend werden die Befehle, mit denen die GPIO-Register beschrieben werden, ohne Taktversorgung nicht ausgeführt.
Sinn des Ganzen ist die Stromersparnis. Bei CMOS setzt sich der Stromverbrauch aus den Leckströmen und einem Strom, welcher direkt proportional zur Taktfrequenz ist, zusammen. Bei Batteriebetrieb zählt da jedes mA! Teilweise wird aber nicht nur der Takt, sondern auch die Betriebsspannung ganzer Schaltungsteile abgeschaltet, das reduziert dann auch noch die Leckströme.


Habe aber noch einen kleinen Fehler gemacht (der sich hier nicht auswirkt aufgrund der internen Logik des Controllers, aber nichtsdestotrotz sollte man das anders machen...):
Das BSRR-Register ist ein Write-Only-Register. Daher muss die Anweisung wie folgt lauten - auch wenn es jetzt so klappt:



GPIOA->BSRR = GPIO_BSRR_BS_5;


Also keinen Oder-Operator benutzen.
Es ist egal welche Variante du nimmst.
Write-Only-Register lesen sich, in diesem Fall, immer als 0, egal auf welche Werte das Register gesetzt ist.

Wenn du jetzt mehrere Bist verwalten willst, musst du ein Schatten-Register führen.
Also eine statische Variable, welch du wie das Register behandelst. Da kannst du dann einzelne Bis setzen oder löschen.
Wenn du mit dem Modifizieren der Bits fertig bist, schreibst du den Wert der Variablen ins Register.

WO-Register verwendet der Hersteller für Register welche in typischen Anwendungen nur bei der Initialisierung der Anwendung beschrieben werden müssen. Das spart Einiges an Transistoren und Platz auf dem Chip. Bei untypischen Anwendungen muss man sich dann halt mit dem Schattenregister behelfen.

Das ist auch der Grund für die HAL-Bibliotheken. Da werden, wo nötig, Schattenregister geführt und der Programmierer muss keine Ahnung von dieser Hardware-Eigenschaft haben. Über den HAL sind alle Register auch lesbar.
Allerdings kostet dies Programmspeicher und Laufzeit. Bei zeitkritischen Aufgaben, weiss man aber nie, was der HAL so alles macht und ganz lustig kann es werden, wenn der HAL ein Update erfahren hat.

MfG Peter(TOO)

Wsk8
18.06.2016, 21:50
Hier mal die Ansteuerung für die LED bei einem F411RE, sollte aber identisch mit deinem sein.



#include "stm32f4xx.h"
#include "stm32f4xx_conf.h"


#define LED2_PIN GPIO_Pin_5
#define LED2_GPIO_PORT GPIOA
#define LED2_GPIO_CLK RCC_AHB1Periph_GPIOA


#define USER_BUTTON_PIN GPIO_Pin_13
#define USER_BUTTON_GPIO_PORT GPIOC
#define USER_BUTTON_GPIO_CLK RCC_AHB1Periph_GPIOC
#define USER_BUTTON_EXTI_LINE EXTI_Line13
#define USER_BUTTON_EXTI_PORT_SOURCE GPIO_PortSourceGPIOC
#define USER_BUTTON_EXTI_PIN_SOURCE GPIO_PinSource13




void SysClock_Init(void)
{
RCC_ClocksTypeDef RCC_Clocks;


/* SysTick end of count event each 1ms */
RCC_GetClocksFreq(&RCC_Clocks);
SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);
}


void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;


/* Enable the GPIO Clock */
RCC_AHB1PeriphClockCmd(LED2_GPIO_CLK, ENABLE);


/* Configure the GPIO pin */
GPIO_InitStructure.GPIO_Pin = LED2_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
}


void LED_SetOnOff(uint8_t state)
{
if(ON == state)
GPIOA->BSRRL = GPIO_Pin_5;
else
GPIOA->BSRRH = GPIO_Pin_5;
}


void LED_Toggle(void)
{
GPIOA->ODR ^= GPIO_Pin_5;
}


mfg

Unregistriert
19.06.2016, 10:02
Es ist egal welche Variante du nimmst.
Write-Only-Register lesen sich, in diesem Fall, immer als 0, egal auf welche Werte das Register gesetzt ist.


Das ist nicht richtig.
Die Variante mit dem Oder braucht unnötigerweise etliche Takte mehr, da erst das Register gelesen werden muss, danach die Verknüpfung erfolgt und anschließend der Wert zurückgeschrieben wird -> also ein Vielfaches der Zeit.




WO-Register verwendet der Hersteller für Register welche in typischen Anwendungen nur bei der Initialisierung der Anwendung beschrieben werden müssen. Das spart Einiges an Transistoren und Platz auf dem Chip. Bei untypischen Anwendungen muss man sich dann halt mit dem Schattenregister behelfen.


Auch das ist nicht richtig. Der Grund ist, dass hier für den atomaren Zugriff ein Register geschaffen wurde, welches ein Setzen oder Zurücksetzen der Pins ermöglichen sollte.
Da ist ein Lesen natürlich unsinnig. Daher ist das ein WriteOnly-Register.

Peter(TOO)
19.06.2016, 15:41
Auch das ist nicht richtig. Der Grund ist, dass hier für den atomaren Zugriff ein Register geschaffen wurde, welches ein Setzen oder Zurücksetzen der Pins ermöglichen sollte.
Da ist ein Lesen natürlich unsinnig. Daher ist das ein WriteOnly-Register.
Dann erklär dich mal.
z.B. bei einer I2C-Emulation muss man SDA zwischen Ein- und Ausgang umschalten. Wäre einfacher mit einem R/W-Register, besonders weil für I2C nur zwei Pins benötigt werden.
Rein technisch wäre es kein Problem nur R/W-Register zu verwenden. Wobei 10-20 zusätzliche Transistoren für ein 8-Bit Register kein Problem sein sollten.

Vermutlich sind die W/O-Register heute einfach nur alte Zöpfe.
Heute ist die µC-Entwicklung mehr oder weniger ein Lego-System. Man nimmt die fertigen Module aus der Bibliothek und der Computer fügt alles zusammen.
Da die Module in VHDL o.ä. definiert sind, können sie mit jedem Prozess verwendet werden. Manche dieser VHDL-Module sind im Kern, schon 20-30 Jahre alt und wurden nur ausgebaut.

MfG Peter(TOO)

Unregistriert
23.06.2016, 07:21
Was gibt es da zu erklären?
Wir reden hier über das BSRR-Register und nicht I2C oder sonstwas.
Und da ist der einzige Grund der atomare Zugriff. Punkt.

Zum Auslesen gibt es das ODR- bzw. das IDR-Register.


ARM verwendet übrigens haupsächlich Verilog, insbesondere für den Kern. Einige PrimeCells sind auch in VHDL erhältlich.




Dann erklär dich mal.
z.B. bei einer I2C-Emulation muss man SDA zwischen Ein- und Ausgang umschalten. Wäre einfacher mit einem R/W-Register, besonders weil für I2C nur zwei Pins benötigt werden.
Rein technisch wäre es kein Problem nur R/W-Register zu verwenden. Wobei 10-20 zusätzliche Transistoren für ein 8-Bit Register kein Problem sein sollten.

Vermutlich sind die W/O-Register heute einfach nur alte Zöpfe.
Heute ist die µC-Entwicklung mehr oder weniger ein Lego-System. Man nimmt die fertigen Module aus der Bibliothek und der Computer fügt alles zusammen.
Da die Module in VHDL o.ä. definiert sind, können sie mit jedem Prozess verwendet werden. Manche dieser VHDL-Module sind im Kern, schon 20-30 Jahre alt und wurden nur ausgebaut.

MfG Peter(TOO)

White_Fox
23.06.2016, 18:51
Hallo allerseits

Ich hab mich diese Woche mal ausgiebig mit dem Kapitel RCC im Datenblatt beschäftigt, alledings hab ich da noch ein paar Unklarheiten.

1. Was genau soll diese f(VCO) sein, von der in den Formeln zum Einstellen der Multiplikatoren immer die Rede ist? (Kapitel 6.3.2)
2. Was hat es mit "PLL" auf sich? Ist das der Taktmultiplikator?

Peter(TOO)
23.06.2016, 23:30
Hallo,

1. Was genau soll diese f(VCO) sein, von der in den Formeln zum Einstellen der Multiplikatoren immer die Rede ist? (Kapitel 6.3.2)
2. Was hat es mit "PLL" auf sich? Ist das der Taktmultiplikator?
In meinem Datenblatt steht da unter 6.3.2 was ganz anderes.
Es ist da hilfreich einen Link auf das verwendete Datenblatt zu setzen, da gibt es oft unterschiedliche Versionen:
http://www.st.com/content/ccc/resource/technical/document/datasheet/65/cb/75/50/53/d6/48/24/DM00141306.pdf/files/DM00141306.pdf/jcr:content/translations/en.DM00141306.pdf
Hier sind die PLLs unter 6.3.11 zu finden.

Die CPU kann mit bis zu 180MHz getaktet werden.
Der Interne Oszillator hat aber nur 16MHz, ein externer Quarz liegt zwischen 4...26 MHz und mit einem externen Takt kann man bis maximal 50MHz takten.

Mit einer PLL kann man nun eine Frequenz vervielfache, aber auch teilen.
Mit einer PLL kann man Frequenzen erzeugen welche in einem ganzzahligen Verhältnis zu einer Referenzfrequenz stehen.

Das Herzstück ist ein VCO (Voltage-Controlled Oscillator, Spannungs-Frequenz Wandler), das Problem bei einem VCO ist, dass seine Frequenzstabilität recht mies ist. es gibt auch hochgenaue Schaltungen, welche den DCF77 (Zeitzeichensender der PTB) verwenden. Nicht nur die Zeit wird von der Atomuhr erzeugt, sondern auch die Trägerfrequenz von 77.5kHz.
Wenn man aber die VCO-Frequenz mit einer stabilen Frequenz, z.B. eines Quarzes, vergleicht und nachregelt, erreicht man die Frequenzkonstanz des Quarzes.
Wenn man die Frequenz des VCOs nun Teilt, liegt die VCO-Frequenz entsprechend höher als die Referenzfrequenz. Als nächstes kann man den Teiler noch einstellbar machen und kann dann eine VCO-Frequenz erzeugen, welche ein ganzzahliges vielfaches der Referenz-Frequenz ist. Hinzu kommt noch, dass die Phasenlag der beiden Frequenzen konstant ist.

Nun kann man den Teiler aber auch an der Referenz-Frequenz anschliessen und entsprechend niedrigeren Frequenzen erzeugen.

fVCO ist die Frequenz der PLL, da sie am VCO abgegriffen wird.

Dein STM32F446x verwendet mehrere PLLs, dadurch lassen sich die Taktfrequenzen verschiedener Einheiten unabhängig voneinander einstellen.

https://de.wikipedia.org/wiki/Phasenregelschleife

MfG Peter(TOO)

White_Fox
24.06.2016, 12:59
Haha...super, daß bringt mich doch schon wieder viel weiter. Vielen Dank. :)

Was würde ich ohne euch bloß machen...wie hat man sich solches Wissen eigentlich früher ohne Internet angeeignet, wo man noch nichtmal einfach so ein Datenblatt runterladen konnte? Man hat doch sicherlich nicht 1500 Seiten Dokumentation mit der Post vershickt...? Und wenn, dann sicherlich nur an Firmen...

Ceos
24.06.2016, 13:45
Man hat doch sicherlich nicht 1500 Seiten Dokumentation mit der Post vershickt...? Und wenn, dann sicherlich nur an Firmen...

ich könnte mal in den Keller meiner Eltern gehen, mein alter Herr hat auch gern gebastelt und kann ein Lied davon singen bzw. einen Schrank mit Dokumenten nach dir werfen :D

damals gabs ja auch noch net soooo komplexe chips, da war 386er PC in Mode

Peter(TOO)
24.06.2016, 19:24
Was würde ich ohne euch bloß machen...wie hat man sich solches Wissen eigentlich früher ohne Internet angeeignet, wo man noch nichtmal einfach so ein Datenblatt runterladen konnte? Man hat doch sicherlich nicht 1500 Seiten Dokumentation mit der Post vershickt...? Und wenn, dann sicherlich nur an Firmen...
Ich bin seit 1976 beruflich bei den µPs dabei. Da hat sich einiges angesammelt.
Damals waren die Chips noch wesentlich einfacher und vieles waren noch Datenblätter. Meist waren nur die Programmierunterlagen ein Buch.
Was man heute in einem einzelnen µC hat, wäre damals eine Leiterplatte, in mindestens der Grösse eines Kuchenblechs gewesen.
3x 16-Bit Timer gab es in einem 40-Pin Gehäuse. 2x 8-Bit Ports gab es auch in dieser Grösse.

Doch, damals wurde alles per Post verschickt. Anfangs gab es Datenbücher, die bekam man bis Ende der 70er einfach so zugeschickt, wenn wieder neue aufgelegt wurden, Dazwischen gab es mal ein Datenblatt für neue Chips.
Als Entwickler musste man auch viel per Telefon nachfragen. Da gab es öfters auch mal Datenblätter für Chips, welche dann gar nie hergestellt wurde. Man hatte deshalb zwangsweise Kontakt mit den Lieferanten und Vertretungen. Als schlauer Entwickler hat man immer zuerst nachgefragt, was überhaupt lieferbar ist.

TI war z.B. Spezialist im Herstellen von ICs für Grosskunden, welche dann auch frei verkauft wurden. Allerdings wurde der Chip dann nicht mehr produziert, wenn der Grosskunde die Fabrikation eingestellt hat.
Intel war etwas mühsam mit den Handbüchern. Man hat ein Handbuch bestellt und dann stand vorne drin, welche man noch alle zusätzlich braucht :-( Also zweite Bestellung machen. Mit der dritten Bestellung hatte man dann meist alles nötige zusammen. Das dauerte dann schnell mal 2-3 Wochen.

Allerdings war damals alles noch übersichtlicher. Bei maximal 64KB Speicherbereich, kannte man seine Bits noch persönlich. :-)

1976 war die Hobby-Scene noch sehr klein und ie meisten hatten beruflich mit Computern zu tun. Die meisten hatten auch nicht einmal einen Computer um die Software zu entwickeln. Das wurde damals noch von Hand assembliert und als Hex-Code eingetippt (z.B. KIM-1).
Ein KIM-1 kostet um die CHF 750.- und war ein Schnäppchen. Ein 6502, 1 KB RAM, ein Monitor-Programm, ein paar Timer und 16-Bit I/Os, eine 6-Stellige 7-Seg Anzeige für Adresse und Daten, sowie eine Hex-Tastatur. Wer es sich leisten konnte, konnte auch einen Fernschreiber als Terminal anschliessen.

Der Apple ][ wurde erst 1977 entwickelt und der IBM-PC kam 1981 auf den Markt.

Die heutige Technologie und ihre Verbreitung hat erst vor 30-40 Jahren angefangen. Den µP, also eine ganze CPU auf einem Chip, gibt es erst seit 1972. Allerdings war das ursprünglich eine Fehlentwicklung. Bull wollte von Intel einen Chipsatz für ein Terminal. Bei Intel kam man dann auf die Die etwas programmierbares zu machen und entwickelte den 4004, ein 4-Bit Prozessor. Bull war dann aber das Terminal zu langsam und sie wollten die Entwicklung nicht... Also musste Intel den Flop selber vermarkten um die Entwicklungskosten wieder einzuspielen.

MfG Peter(TOO)

- - - Aktualisiert - - -


damals gabs ja auch noch net soooo komplexe chips, da war 386er PC in Mode
Zum 80386 selbst, gab es zwei Handbücher von Intel, jeweils 500-1000 Seiten stark. eines behandelte nur die Hardware und das Zweite nur den Befehlssatz.
Dann gab es noch ein paar zur Programmierung in Assembler und App-Notes.
Neu beim 80386 war, dass die FPU mit auf dem Chip war. Beim 8086 war die FPU (80387) komplexer als die CPU und entsprechend teuer (so Mitte 80er 500-600 CHF).

MfG Peter(TOO)

White_Fox
02.07.2016, 14:57
Da ich hier bisher viel gute Hilfe bekommen habe, meldi ch mich mal wieder. :)

Ich will mit dem Timer2 einen Interrupt generieren, komme aber mit den Registern irgendwie nicht klar.

Was ich dem Reference-Manual
http://www.st.com/content/ccc/resource/technical/document/reference_manual/4d/ed/bc/89/b5/70/40/dc/DM00135183.pdf/files/DM00135183.pdf/jcr:content/translations/en.DM00135183.pdf
bisher entnehmen konnte war:

Autoreload-Register (ARR), enthält den Wert mit dem das Zählregister jedesmal wieder neu geladen wird
Prescaler (PSC), Vorteiler
Counter-Register (CNT), Zählregister des Timers
Controllregister 1/2 (CR1, CR2), verschiedene Einstellungen


Ich finde zwar die entsprechenden Register- und Bitbezeichnungen in der Headerdatei, aber nur für allgemein für alle Timer. Ich finde keine Definition, wie ich die Register für Timer2 beschreiben kann. Auch wie ich eine Funktion per Interrupt aufrufe ist mir noch nicht ganz klar. Dieses Konstrukt

extern "C" void TIM2_IRQHandler(){
}
hab ich so noch nie genutzt. Wie funktioniert das?

Sisor
02.07.2016, 15:06
Wenn du mit den Registern ein Interrupt-Betrieb für Timer2 konfigurierst, wird die Funktion mit dem Namen TIM2_IRQHANDLER 'automatisch' aufgerufen. Also einfach eine Funktion so definieren:

void TIM2_IRQHandler() {
// mach was...
}
Ausschnitte aus nem alten Projekt von mir mit nem STM32F107:


// ...
#include <stm32f10x.h> // sfr nach keil
#include "STM32F107_rcc.h" // strukturen der sfr
#include "STM32F107_gpio.h" // strukturen der sfr
#include "STM32F107_tim.h" // strukturen der sfr
int main() {
//...

// Timer 2 für Zeitmessung

#define VORTEILER_TIM2 ((SystemCoreClock / 10000)-1) // Teilung auf 10 kHz
#define TIM2_PRIO 0x10 // höchste Priorität
#define TIM2_INTERV 1 // 1ms
#define VORLADEWERT_TIM2 (TIM2_INTERV*10 - 1)

RCCst->APB1ENR.tim2 = 1; // Takt für Timer 2 freigeben
TIM2st->DIER.uie = 1; // Freigabe: Überlauf von Zähler löst Interrupt aus
TIM2st->CR1.dir = 1; // Zähler zählt runter
TIM2st->PSC = VORTEILER_TIM2;
TIM2st->ARR = VORLADEWERT_TIM2;
TIM2st->CNT = VORLADEWERT_TIM2;
TIM2st->CR1.cen = 0; // Timer 2 Zählfreigabe
NVIC->IP[2] = TIM2_PRIO; // Priorität

NVIC->ISER[TIM2_IRQn / 32] = 1 <<(TIM2_IRQn % 32); // ISR für Zeitmessung setzen
// ...
}
void TIM2_IRQHandler(void) // wird jede ms aufgerufen
{
TIM2st->SR.uif = 0;
zaehler_ms++;
}

White_Fox
02.07.2016, 16:24
Hm...die Bibliothek , die ES nutzt, scheint da etwas anders mit Interrupts umzugehen. TIM2_IRQHandler ist da nur ne stinknormale Funktion.

Hier ist mal der Code, den ich bisher gebastelt habe.


void TIM2_IRQHandler(void){
printf("TIM2 Interrupt\n");
}

void configTakt(){
RCC->CFGR |= RCC_CFGR_PPRE1_DIV16; //Takt für APB1 auf 1MHz runterteilen
}

void configTimer2(){
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; //Takt für Tim2 freigeben
TIM2->PSC = 10000; //Prescaler 10.000
TIM2->CR1 |= TIM_CR1_CEN; //Timer aktivieren
}

void main(void){
configTakt();
configTimer2();
configLed();
configButton();
printf("Einstellung fertig\n");

while(1){
}
}

Sisor
02.07.2016, 16:39
Das Anschubsen der Serviceroutine muss noch enabled werden:

NVIC->ISER[TIM2_IRQn / 32] = 1 <<(TIM2_IRQn % 32); // ISR für Zeitmessung setzen

White_Fox
23.07.2016, 15:25
Hallo Sisor

Erstmal vielen Dank.

Mir ist die Bedeutung in deiner Codezeile zwar noch nicht ganz klar, aber ich habs einfach mal kopiert und der Kompiler meckert nicht.

So richtig will es aber immer noch nicht. Hier mal der komplette Code. Muß ich mit dem NVIC noch irgendwas machen?
Und woher weiß der Kompiler, daß Timer-Interrupts in TIM2_IRQHandler abgearbeitet werden sollen? So richtig ist mir das
auch nicht klar. Ich hab das aus einem Beispiel, finde in der STM32F4xx aber nichts dazu. (Leider auch nichts ähnliches).

Mir reicht für den ersten Test eine Ansage vom Debugger in der ISR.


void TIM2_IRQHandler(void){
printf("TIM2 Interrupt\n");
}

void configLed(){
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Taktversorgung Port A
GPIOA->MODER |= (GPIO_MODER_MODER5_0); //A5 => Ausgang
GPIOA->OTYPER &= ~(GPIO_OTYPER_OT_5); //A5 0> Push-Pull
GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR5); //A5 ohne Pullup-Pulldown
}

void configButton(){
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Taktversorgung Port C
GPIOC->MODER |= GPIO_MODER_MODER0_0; //C13 => Eingang
GPIOC->PUPDR |= GPIO_PUPDR_PUPDR13_1; //C13 => Pulldown einschalten
}

void configTakt(){
RCC->CFGR |= RCC_CFGR_PPRE1_DIV16; //Takt für APB1 auf 1MHz runterteilen
}

void configTimer2(){
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; //Takt für Tim2 freigeben
TIM2->PSC = 10000; //Prescaler 10.000
TIM2->CR1 |= TIM_CR1_CEN; //Timer aktivierent

TIM2->DIER |= TIM_DIER_TIE; //Trigger Interrupt enable
NVIC->ISER[TIM2_IRQn / 32] = 1 <<(TIM2_IRQn % 32);
printf("Timer2 Konfi beendet\n");
}

void configNVIC(){
}

void main(void){
configTakt();
configTimer2();
configLed();
configButton();
printf("Einstellung fertig\n");

while(1){ //Halteschleife da da Debug-Fenster sonst mit Knopf-gedrückt-Meldungen geflutet wird
}

while(1){
if(!(GPIOC->IDR & GPIO_IDR_IDR_13)){
GPIOA->BSRR |= GPIO_BSRR_BS_5; //Ausgang A5 setzen
printf("Knopf gedrückt\n");
}
else{
GPIOA->BSRR |= (GPIO_BSRR_BR_5); //Ausgang A5 zurücksetzen
printf("Knopf nicht gedrückt\n");
}
}
}